Provisions and runs a Minecraft Java Edition server on AWS ECS Fargate using Terraform and a Docker image. The world is stored on EFS so it survives restarts. The whole thing is deployed from the command line (or GitHub Actions) without using the AWS Management Console.
The server runs as a container on Fargate instead of a managed EC2 instance.
Terraform builds the networking, an ECR repository, an EFS file system, the ECS
cluster, and the service. A Docker image holds the server; the world data is
mounted from EFS at /data. The ECS service keeps one task running and replaces
it automatically if it stops, which covers the auto-restart requirement. On
shutdown ECS sends SIGTERM to the container, where the JVM runs as PID 1 and
saves the world before exiting.
Dockerfile --build/push--> ECR image
Terraform --provisions--> EFS + ECS Fargate service (+ security groups, logs)
ECS task --public IP--> port 25565
AWS CLI --resolves IP--> nmap -sV -Pn -p T:25565 <ip>
flowchart TD
A[Dockerfile] -->|build + push| B[(ECR image)]
C[Terraform] -->|provisions| D[ECS Fargate service]
B --> D
C --> E[(EFS world volume)]
D -->|mounts /data| E
D -->|public IP :25565| F[nmap]
Run from Linux or WSL2 on Windows. Install:
- Terraform >= 1.6
- AWS CLI v2
- Docker
- nmap
Configure AWS credentials with aws configure (region us-west-2). Verify with
aws sts get-caller-identity.
By default the stack creates its own IAM roles. In an AWS Academy Learner Lab,
which does not allow creating roles, create terraform/terraform.tfvars with:
create_iam_roles = false
existing_role_name = "LabRole"./scripts/deploy.sh # build image, push to ECR, provision everything
./scripts/connect.sh # resolve the server IP and run the nmap check
./scripts/destroy.sh # tear everything down
deploy.sh initializes Terraform, creates the ECR repository, builds and pushes
the image, then applies the rest of the stack. connect.sh waits for the task to
start, finds its public IP through the AWS CLI, and probes port 25565. Fargate
assigns a new IP each time the task starts, so re-run connect.sh after a
redeploy.
After connect.sh prints Server: <ip>:25565, join from the Minecraft client
(Multiplayer > Add Server > paste the IP), or confirm reachability with:
nmap -sV -Pn -p T:25565 <ip>
.github/workflows/validate.ymlrunsterraform fmt,validate, and a Docker build on every push..github/workflows/deploy.ymlruns the full deploy on push tomain. It needs the repository secretsAWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY.
docker/ Dockerfile + entrypoint
terraform/ main, network, storage, iam, ecs, variables, outputs
scripts/ deploy, connect, destroy
.github/ CI workflows
- https://www.minecraft.net/en-us/download/server
- https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html
- https://docs.aws.amazon.com/AmazonECS/latest/developerguide/efs-volumes.html
- https://registry.terraform.io/providers/hashicorp/aws/latest/docs
- https://hub.docker.com/_/eclipse-temurin