AWS Minecraft Server
December 2021 — Eric Canam

My friends and I wanted a Minecraft server that only we could contribute to, but didn't want to pay the steep price for Mojang's solution, and also wanted control of the server so that modifications, our own backups, and more would be easily configured.

The main problem was that none of us had a PC we could guarantee would be turned on. So the solution was a Linux EC2 spot instance hosted on AWS. To save money, the machine would turn itself off automatically if no player had connected in over 10 minutes, and would need one of us to request to turn it on again.

To turn it on, we pressed a button on a static webpage hosted on Amplify. This then triggered a Lambda function, through API Gateway:

Lambda: EC2-Start
import boto3

region = 'ca-central-1'
instances = ['your_ec2_instance_id']
ec2 = boto3.client('ec2', region_name=region)

# brings user to page with server status and its dynamic IP address for this launch
def rgo(code, ip):
    response = {}
    response["statusCode"] = 303
    response["headers"] = {'Location': "https://your_amplify_url.amplifyapp.com/ip.html?ip="+str(ip)+"&code="+str(code)}
    response["body"] = "{}"
    return response

def lambda_handler(event, context):
    
    instance = boto3.resource('ec2').Instance(instances[0])
    st = instance.state["Name"]
    
    # starts server if stopped. if another user has started it, show this user its IP address
    if st=="stopped":
        ni = ec2.start_instances(InstanceIds=instances)
        instance.wait_until_running()
        instance = boto3.resource('ec2').Instance(instances[0])
    elif st=="pending":
        instance.wait_until_running()
        instance = boto3.resource('ec2').Instance(instances[0])
    if st=="running" or st=="pending" or st=="stopped":
        public_ip = instance.public_ip_address
        return rgo("GS", public_ip)
    
    return rgo("NA", "null")

This redirected us to another static page that displayed the current IP address of the server (a static IP costs money on AWS!) From there, we'd have everything we needed to start playing.

Once we'd had our fun, we would log out and forget about it. The machine had a chron job set up to check if any players were active every 10 minutes. If it checked twice in a row and no players were online, it would trigger the following Lambda:

Lambda: EC2-Stop
import boto3
import rcon

region = 'ca-central-1'
instances = ['your_ec2_instance_id']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
	
    instance = boto3.resource('ec2').Instance(instances[0])
    
    # first stop Minecraft server so it saves correctly
    with rcon.Client(str(instance.public_ip_address), ((((minecraft_port)))), passwd='your_rcon_password') as client:
        response = client.run('stop')
    # now shut down EC2 instance
    ni = ec2.stop_instances(InstanceIds=instances)