Every once in a while my gaming group decides they want to sit back, relax, and chill on a little Minecraft. It's not often, so paying for a full time dedicated server doesn't make sense. And joining a local session can be tricky with routers, ISPs, firewalls, and VPNs. Why not try to make a dedicated server which only runs when people want it to?

EC2 server and auto-shutdown

I started by just getting an EC2 instance on AWS to host a minecraft server through Docker. This was pretty straight forward with some Docker experience. Next, I referenced this script to write a script which would check for player count and shutdown the server if it's been empty for X minutes. Check it out:

#!/bin/sh

COMMAND_NAME=mc-server-runner
PLAYERS_EMPTY_MESSAGE="There are 0 of a max of 20 players online:"

if ! ps ax | grep -v grep | grep $COMMAND_NAME > /dev/null;  then
    echo "Minecraft not running, exiting script"
    exit 0
fi

if ! docker exec mc rcon-cli list | grep "$PLAYERS_EMPTY_MESSAGE" > /dev/null; then
    echo "Players online, exiting script"
    exit 0
fi

echo "No players online, waiting 10 min to check again"
sleep 10m

if docker exec mc rcon-cli list | grep "$PLAYERS_EMPTY_MESSAGE" > /dev/null; then
    echo "After waiting 10 min, still no players, shutting down"
    $(sudo poweroff)
fi

Basically it has a bunch of short-circuiting if statements. Is minecraft not running? Exit. Are players online? Exit. Wait 10 min then see if the server is empty. Yes? Then shut myself down. Cron executes this every 5th minute. Simple and sweet. My command to start the docker container is

docker run -d -e EULA=TRUE -p 25565:25565 --name mc -v /home/freied/minecraft/data:/data --restart unless-stopped itzg/minecraft-server

Also pretty easy. --restart unless-stopped is great for keeping minecraft going through server stops & starts.

Turning the server on

Another problem is getting the server to turn on. The initial/simplest way is by starting the server through the AWS portal when someone wants to play. This is not great for the obvious reason that my time and availability is required no matter who wants to play. I thought it'd be really cool to make a web app that my friends can use to start and stop the server.

I have a web application repository called 'playground' which holds some random side projects. The grandest example is my woodworking portfolio. That playground web application is where I decided to add this functionality. In the backend I use rust with rocket. In the front I'm using elm.

With rust, I used the AWS SDK called rusoto. Here is some code which interacts with EC2:

fn start_instance(ec2: &Ec2Client, t_rt: &mut Runtime) -> Result<(), String> {
    let instance_id = get_instance_id();
    let req = StartInstancesRequest {
        instance_ids: vec![instance_id],
        ..Default::default()
    };

    // t_rt is a Tokio Runtime so I can use async in an otherwise synchronous codebase
    t_rt.block_on(ec2.start_instances(req))
        .map(|_res| ())
        .map_err(|e| format!("Could not start instance, error = {:?}", e))
}

This code isn't perfect rust (manually using a tokio runtime (my version of rocket doesn't support it), not returning the actual error etc.), but this playground is a bit more quick-and-dirty so I don't really care.

The front end just has a simple page with a status indicator and button to start the EC2 instance.

Thoughts

This whole system was actually pretty straight forward to set up! We played for ~5 hours one night and I was charged 30 cents. Other than that, the server has been off, costing me 0 cents. The server is a t3a.medium: 2vCPU, 4GB memory, and 16GB magnetic disk storage. I'm really happy with how it's working and that I need not be bothered when a friend wants to play in our dedicated minecraft server. For all I know, it might be shut down for 6 months before we want to play again.