🇰🇷 한국어 보기
This project aims to build and manage a medium-to-large scale Minecraft server infrastructure running on AWS, using Velocity proxy server and Paper bucket servers in Docker containers, automated and managed with Terraform.

- Compute: EC2 (Ubuntu 22.04 ARM64)
- Network: VPC, Public/Private Subnet, NAT Gateway, Security Groups, Elastic IP
- Storage: EBS GP3 Volume, S3 (VPC Flow Logs)
- Monitoring: CloudWatch (Metrics/Alarms), SNS, Lambda (Discord Notifications) + Prometheus/Grafana (Application Level)
- Backup: Data Lifecycle Manager (1-hour snapshots)
- Security: IAM Roles, SSM Session Manager, VPC Flow Logs
- Velocity Proxy:
itzg/mc-proxy
- Paper Servers:
itzg/minecraft-server
- Monitoring:
prom/prometheus
,grafana/grafana
- Management:
portainer/portainer-ce
# Install Terraform (macOS)
brew install terraform
# Install and configure AWS CLI
brew install awscli
aws configure
Minimum IAM permissions required for deployment:
- EC2, VPC, EBS management
- CloudWatch, SNS, Lambda management
- IAM role creation/management
- S3 bucket creation/management
git clone https://github.com/your-username/velocity-papermc-server.git
cd velocity-papermc-server
Create a terraform.tfvars
file and enter the following content:
# Project basic settings
project_name = "mcserver"
aws_region = "ap-northeast-2"
# Network settings
vpc_cidr = "10.0.0.0/16"
availability_zone = "ap-northeast-2a"
public_subnet_cidr = "10.0.1.0/24"
private_subnet_cidr = "10.0.2.0/24"
# EC2 instance settings
velocity_instance_type = "t4g.small" # ARM64 instance
paper_instance_type = "r6g.medium" # Memory optimized
# EBS volume sizes (GB)
velocity_ebs_size = 20
paper_ebs_size = 50
# SSH key pair (must be created in AWS console beforehand)
key_name = "your-keypair-name"
# Discord notifications (optional)
discord_webhook_url = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"
# Monitoring & access control
admin_ip = "YOUR_PUBLIC_IP/32" # Your public IP
grafana_admin_username = "admin"
grafana_admin_password = "your-secure-password"
prometheus_scrape_interval = "5s"
prometheus_retention = "7d"
- Never commit the
terraform.tfvars
file to Git - Set
grafana_admin_password
to a strong password - Set
admin_ip
to allow only your own public IP
# Initialize Terraform
terraform init
# Check deployment plan
terraform plan
# Deploy infrastructure (takes about 10-15 minutes)
terraform apply
After deployment completes, the following information will be output:
Outputs:
velocity_ec2_public_ip = "XX.XX.XX.XX"
grafana_url = "http://XX.XX.XX.XX:3000"
paper_ec2_private_ip = "10.0.2.XXX"
- Connect to Velocity EC2:
# SSH connection
ssh -i ~/.ssh/your-keypair.pem ubuntu@[VELOCITY_PUBLIC_IP]
# Or use SSM Session Manager
aws ssm start-session --target [VELOCITY_INSTANCE_ID]
- velocity.toml configuration:
# Navigate to config file location
cd /mcserver/velocity
# Edit velocity.toml
sudo nano velocity.toml
Main velocity.toml
settings:
# velocity.toml
config-version = "2.6"
bind = "0.0.0.0:25577"
motd = ""
show-max-players = 100
online-mode = true
force-key-authentication = true
[servers]
lobby = "PAPER_PRIVATE_IP:25501"
wild = "PAPER_PRIVATE_IP:25502"
village = "PAPER_PRIVATE_IP:25503"
try = ["lobby"]
[forced-hosts]
"lobby.yourserver.com" = ["lobby"]
"wild.yourserver.com" = ["wild"]
"village.yourserver.com" = ["village"]
- forwarding-secret configuration:
# Check forwarding.secret file
echo "your-secret-key-here" > /mcserver/velocity/forwarding.secret
- Connect to Paper EC2 (via Velocity Jump Host):
# Connect to Paper EC2 through Velocity EC2 (Security Group permission required)
ssh -i ~/.ssh/your-keypair.pem -J ubuntu@[VELOCITY_PUBLIC_IP] ubuntu@[PAPER_PRIVATE_IP]
# Or use SSM Session Manager
aws ssm start-session --target [PAPER_INSTANCE_ID]
- Paper server config file (paper-global.yml):
# Edit common config file (paper-global.yml)
cd /mcserver/paper/lobby/config
sudo nano paper-global.yml
sudo cp paper-global.yml ../wild/config/paper-global.yml
sudo cp paper-global.yml ../village/config/paper-global.yml
paper-global.yml
configuration:
# paper-global.yml
proxies:
velocity:
enabled: true
online-mode: true
secret: "your-secret-key-here" # velocity's forwarding.secret value
settings:
velocity-support:
enabled: true
online-mode: true
secret: "your-secret-key-here"
# Performance optimization settings
chunk-loading:
autoconfig-send-distance: true
enable-frustum-priority: true
timings:
enabled: false
network:
keep-alive: 30
- Individual server settings:
# Edit server.properties in each server directory
sudo nano /mcserver/paper/lobby/server.properties
sudo nano /mcserver/paper/wild/server.properties
sudo nano /mcserver/paper/village/server.properties
Each server's server.properties
:
# server.properties (individual server settings)
server-name=Lobby Server # Set differently per server
online-mode=false # Set to false when using Velocity
server-port=25565 # Internal container port
# On Velocity EC2
cd /mcserver && docker-compose ps
# On Paper EC2
cd /mcserver && docker-compose ps
# Check real-time logs
docker-compose logs -f velocity-proxy
docker-compose logs -f lobby-server
docker-compose logs -f wild-server
docker-compose logs -f village-server
This project collects metrics using the bungeecord-prometheus-exporter
plugin.
- Access
http://[VELOCITY_PUBLIC_IP]:3000
in browser - Login credentials:
- Username: user_name set in
terraform.tfvars
- Password: password set in
terraform.tfvars
- Username: user_name set in
- Grafana → Configuration → Data Sources
- Add data source → Prometheus
- URL:
http://prometheus:9090
- Save & Test
# SSM port forwarding from local PC
aws ssm start-session \
--target [PAPER_INSTANCE_ID] \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["9000"],"localPortNumber":["9000"]}'
# Access http://localhost:9000 in browser
- Connect to
[VELOCITY_PUBLIC_IP]:25565
from Minecraft client - Test server switching:
/server lobby /server wild /server village
- Grafana: Check application-level server metrics (player count, JVM memory, etc.)
- CloudWatch: AWS resource monitoring
- Discord: SNS + Lambda alerts
Issue: "Can't connect to server"
# Solution: Check security groups and firewall
aws ec2 describe-security-groups --group-ids [SG_ID]
sudo ufw status
Issue: "Server failed to start"
# Solution: Check container logs
docker-compose logs [service-name]
sudo journalctl -u docker
Issue: "Permission denied" (monitoring)
# Solution: Reset permissions
sudo chown -R 65534:65534 /mcserver/monitoring/prometheus/data
sudo chown -R 472:472 /mcserver/monitoring/grafana
# Delete infrastructure
terraform destroy
# Type 'yes' in the confirmation prompt