Game Server Automation with AWS EC2 and Discord
Running a game server on AWS EC2 is great until you start doing the maths on idle costs. An instance running 24/7 when people only actually play for a few hours a day is expensive and wasteful. The obvious solution is to stop the instance when not in use — but then someone needs to start it again, and that person needs AWS console access, which you probably don't want to hand out to your whole friend group.
The solution: a Discord bot that handles the EC2 lifecycle entirely. Your friends type a command in Discord. The server starts. When they're done, they stop it. Nobody touches the AWS console.
EC2 control via boto3
The AWS side is handled entirely through boto3, the AWS Python SDK. The bot uses a restricted IAM role that can only start, stop, and reboot specific tagged instances — it has no other AWS permissions, so even if credentials were somehow compromised, the blast radius is minimal.
def start_instance(instance_id):
ec2 = boto3.client("ec2", region_name=REGION)
ec2.start_instances(InstanceIds=[instance_id])
waiter = ec2.get_waiter("instance_running")
waiter.wait(InstanceIds=[instance_id])
return get_public_ip(instance_id)
Instance discovery uses EC2 tags. The bot only manages instances tagged with the Discord Guild ID, which prevents it from accidentally touching other instances in the account.
Discord commands
The bot exposes a small set of commands:
.start— starts the instance, posts the IP when ready.stop— stops the instance after confirming intent.state— returns the current instance state.info— returns the public IP (useful after a stop/start cycle).totaluptime— returns cumulative uptime tracked in SQLite
Pterodactyl integration
Pterodactyl is a game server management panel — it handles the actual game server process, player management, and resource limits. The bot optionally integrates with the Pterodactyl API to start or stop the game server process independently of the EC2 instance, which is useful for games where you want the instance running but the server stopped.
Uptime tracking
A local SQLite database records every start and stop event with timestamps. The .totaluptime command queries this to show cumulative hours — useful for splitting costs among players, or just knowing how much you've actually used the server this month.
Switching from always-on to on-demand cut my monthly EC2 bill by about 70% for the same amount of actual gameplay. The bot paid for itself in the first week.
Deployment
The bot runs in Docker, with the docker-compose.yml handling environment variable injection for AWS credentials, Discord token, and Pterodactyl API key. The README walks through IAM setup, EC2 tagging, and Discord bot creation from scratch.