Run your own Minecraft server on AWS, control it from a web app, and keep idle cost low by stopping or hibernating when nobody is playing.
This project is web-app first. CLI/manual shell flows are supported as optional add-ons.
If you want the fastest path from clone to a live panel and server, just run the setup script.
You do not need Node.js, pnpm, or mise installed ahead of time. ./setup.sh checks for mise, installs it if needed, activates it for the current setup session, and then uses it to install the correct Node.js and pnpm versions for this project.
git clone <your-repo-url>
cd mc-aws
bash ./setup.sh./setup.sh automatically:
- Installs/verifies
mise - Activates
misefor the current setup run and your future shell sessions - Uses
miseto install the correct Node.js andpnpmversions for this project - Installs project dependencies
- Launches the credential wizard (
scripts/setup-wizard.sh) - Deploys AWS infrastructure with CDK
- Stores deployment outputs (including
INSTANCE_ID) in.env.productionand.env.local - Deploys the Next.js app to Cloudflare Workers
- AWS account (for EC2/Lambda/SSM/CDK; SES is optional for email features)
- Cloudflare zone + DNS API token (for runtime DNS updates)
- Google OAuth client (for web app sign-in)
- GitHub token (
reposcope) plus repo/user values (currently required by CDK setup in this repo)
Setup guides:
cp .env.example .env.local
pnpm devOpen http://localhost:3000.
Local auth options:
- Google sign-in (when OAuth env vars are configured)
- Dev login route:
http://localhost:3000/api/auth/dev-login(pnpm devenables this by default)
- Server status, health, and player visibility
- Start, stop, resume, and hibernate operations
- Backup, restore, and backup listing
- Cost views and email allowlist management
- Admin shortcuts for common operational tasks
ADMIN_EMAIL: full accessALLOWED_EMAILS: can check status and start- signed-in users not listed above: status-only
VERIFIED_SENDER,NOTIFICATION_EMAIL, andSTART_KEYWORDare optional.- If they are not configured, core panel/server flows still work.
- Without
VERIFIED_SENDER, email-triggered commands and SES notification sending are disabled.
- Hibernate intentionally detaches and deletes all attached instance volumes (zero-EBS-cost mode).
- Resume reconstructs the root volume from the instance's own source AMI root snapshot (
ImageId+RootDeviceName). - Resume does not use drifting "latest" public AMI lookups.
- If pinned source metadata cannot be resolved, resume fails explicitly instead of silently falling back.
The web app is the default workflow. If you want terminal control, these commands are available:
pnpm server:status
pnpm server:start
pnpm server:stop
pnpm server:resume
pnpm server:hibernate
pnpm server:backup
pnpm server:backups
pnpm server:restore -- <backup-name>
pnpm operations:cleanup
pnpm operations:cleanup -- --dry-run --retention-days=14Durable operation-state records in SSM (/minecraft/operations/*) default to a 30-day retention window.
Set MC_OPERATION_STATE_RETENTION_DAYS to override that window.
Manual EC2 shell access (advanced):
./bin/connect.sh
./bin/console.shAfter initial setup, normal app updates are usually:
wrangler login
pnpm deploy:cfpnpm deploy:cf uses .env.production by default.
It also writes a temporary .env.production.local during build so next build cannot be overridden by .env.local.
For explicit control:
ENV_FILE=.env.production pnpm deploy:cfFor infrastructure changes:
pnpm cdk:diff
pnpm cdk:deploy- Ensure
aws sts get-caller-identityworks - Ensure
.env.productionhas required values from the wizard - Re-run
./setup.sh
- Re-run
./setup.shso it can finish themisesetup step - If it still fails, check whether
misewas installed to~/.local/bin/mise - Restart your terminal, then run
./setup.shagain
- Add exact callback URLs in Google Console:
http://localhost:3000/api/auth/callbackhttps://<your-panel-domain>/api/auth/callback
- Make sure
NEXT_PUBLIC_APP_URLmatches the deployed panel URL
- Use Wrangler OAuth:
wrangler login - Keep DNS runtime token in your deployment env file as
CLOUDFLARE_DNS_API_TOKEN - Avoid exporting
CLOUDFLARE_DNS_API_TOKENglobally in your shell
