DEV Community

Cover image for How to Run a Minecraft Server on AWS For Less Than 3 US$ a Month
Julien Bras
Julien Bras

Posted on • Edited on • Originally published at sidoine.org

How to Run a Minecraft Server on AWS For Less Than 3 US$ a Month

During the first weeks of the COVID-19 pandemic, back in april 2020 my son ask me to build a Minecraft server in order to play on the same world with his school friend. After checking some available services (yeah not so expensive finally), I have chosen to build a server on a EC2 instance. This article will explain you how to optimize the cost 😜, based on the usage!

Some Tools Used in the Article

AWS

I want to rely only on AWS services as I want to increase my knowledge on this big cloud offering. There is always one service you don't know ! In this particular example I will use the following services:

Minecraft

Minecraft is a popular sandbox video-game. In this case I will focus on the Minecraft Java Edition, because the server version is running well on Linux server, and my son is running a laptop on Debian.

Global Architecture of the Solution

The first month operating the server, I noticed that my son is using it a couple of hours each day, and then the server was idle. It's built on a EC2 t2.small with a 8 GB disk so I have a monthly cost of about 18 US$. Not a lot but I was thinking that there is room for improvement! The main part of the cost is the EC2 compute cost (~17 US$) and I know that it's not used 100% of the time. The global idea is to start the server only when my son is using it, but he doesn't have access to my AWS Console so I need to find a sweet solution!

Here is the various blocks used:

  • an EC2 instance, the Minecraft server
  • use SES (Simple Email Service) to receive e-mail, and trigger a Lambda function
  • one Lambda function to start the server
  • one Lambda function to stop the server

And that's it. My son is using it this way:

  • send an e-mail to a specific and secret e-mail address, this will start the instance
  • after 8h the instance is shutdown by the lambda function (I estimate that my son must not play on Minecraft more than 8h straight πŸ˜…)

Let's Build it Together

Build the EC2 Instance

This is the initial part, you must create a new EC2 instance. From the EC2 dashboard, click on Launch Instance and choose the Amazon Linux 2 AMI with the x86 option.

Launch Instance

Next you must choose the Instance Type. I recommend you the t2.small for Minecraft. You will able to change it after the creation.

Choose Instance Type

Click on Next: Configure Instance Details to continue the configuration. Keep the default settings, and the default size for the disk (8 GB) as it's enough.

For the tag screen I generally provide a Name (it's then displayed on EC2 instance list) and a costcenter (I use it for cost management later).

Instance Tags

For the Security Group, it the equivalent of a firewall on EC2 and you must configure which port will be accessible from internet on your server. I add SSH port and the Minecraft port (25565) like you see on the following screen:

Instance Security Group

Then to start the instance you must select or create a key pair. It's mandatory and allow then to connect remotely to your EC2 instance. In my case I am using an existing key pair but if you create a new key don't forget to download on your laptop the private key file.

Instance Key

Yes my key is named caroline. Why not?

Then you must connect your instance via SSH, I recommend this guide if you need help. Basically you must run this kind of command:



ssh -i my_private_key.pem ec2-user@public-ipv4


Enter fullscreen mode Exit fullscreen mode

The public-ipv4 is available in the instance list:

Get the IPv4 address

You first need java. As newer build of minecraft (since 1.17) are running only on Java 17, I recommend to use Corretto (the Amazon Java version):



sudo rpm --import https://yum.corretto.aws/corretto.key
sudo curl -L -o /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo
sudo yum install -y java-17-amazon-corretto-devel.x86_64
java --version


Enter fullscreen mode Exit fullscreen mode

You must have something like:



openjdk 17.0.1 2021-10-19 LTS
OpenJDK Runtime Environment Corretto-17.0.1.12.1 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM Corretto-17.0.1.12.1 (build 17.0.1+12-LTS, mixed mode, sharing)


Enter fullscreen mode Exit fullscreen mode

Thanks @mudhen459 for the research on this java issue ;)

And I want a dedicated user:



sudo adduser minecraft


Enter fullscreen mode Exit fullscreen mode

To install Minecraft you can rely on the Minecraft server page here.

For example for the version 1.17.1 I can run the following:



sudo su
mkdir /opt/minecraft/
mkdir /opt/minecraft/server/
cd /opt/minecraft/server
wget https://launcher.mojang.com/v1/objects/a16d67e5807f57fc4e550299cf20226194497dc2/server.jar
sudo chown -R minecraft:minecraft /opt/minecraft/


Enter fullscreen mode Exit fullscreen mode

⚠️ Warning regarding Java version:
It seems that starting with Minecraft 1.17, it require now a Java JRE 16 (instead of Java JRE 8).
This site is giving you links to download older Minecraft versions if needed.



Exception in thread "main" java.lang.UnsupportedClassVersionError: net/minecraft/server/Main has been compiled by a more recent version of the Java Runtime (class file version 60.0), this version of the Java Runtime only recognizes class file versions up to 52.0


Enter fullscreen mode Exit fullscreen mode

I have created a little service to avoid start manually the server. I want the Minecraft process to start as soon as I start the server.

To do that I have created a file under /etc/systemd/system/minecraft.service with the following content:



[Unit]
Description=Minecraft Server
After=network.target

[Service]
User=minecraft
Nice=5
KillMode=none
SuccessExitStatus=0 1
InaccessibleDirectories=/root /sys /srv /media -/lost+found
NoNewPrivileges=true
WorkingDirectory=/opt/minecraft/server
ReadWriteDirectories=/opt/minecraft/server
ExecStart=/usr/bin/java -Xmx1024M -Xms1024M -jar server.jar nogui
ExecStop=/opt/minecraft/tools/mcrcon/mcrcon -H 127.0.0.1 -P 25575 -p strong-password stop

[Install]
WantedBy=multi-user.target


Enter fullscreen mode Exit fullscreen mode

Then advise the new service by the following:



chmod 664 /etc/systemd/system/minecraft.service
systemctl daemon-reload


Enter fullscreen mode Exit fullscreen mode

More information on systemd here.

Now if you restart the EC2 instance a Minecraft server must be available! You can check βœ… this first step!

I am not speaking of the fact that the IPv4 is dynamic by default. I recommend to setup an static Elastic IP for this server (here!) in order to get a static IP.

Build the Start Scenario

Let's first create our Lambda function. Go into Lambda, and click on Create function to build a new one. Name it mc_start and use a Node.js 14.x or more runtime.

Then you must have this type of screen:

Lambda Start

Replace the content of index.js file with the following:



const AWS = require("aws-sdk");
var ec2 = new AWS.EC2();

exports.handler = async (event) => {
  try {
    var result;
    var params = {
      InstanceIds: [process.env.INSTANCE_ID],
    };
    var data = await ec2.startInstances(params).promise();
    result = "instance started"

    const response = {
      statusCode: 200,
      body: result,
    };
    return response;
  } catch (error) {
    console.error(error);
    const response = {
      statusCode: 500,
      body: "error during script",
    };
    return response;
  }
};


Enter fullscreen mode Exit fullscreen mode

In Configuration, set the following:

  • add an environnement variable named INSTANCE_ID with the value that correspond to the Instance Id of your Minecraft server (something like i-031fdf9c3bafd7a34).
  • the role permissions must include the right to start our EC2 instance like this:

Lambda Permissions

In Simple Email Service, it's time to create a new Rule Set in the Email Receiving section:

SES EMail Receiving

Click on Create rule inside default-rule-set. Take note that the Email Receiving feature is only available today in 3 regions: us-east-1, us-west-2 and eu-west-1 (source here).

If SES is receiving an email on this particular identity:

SES Config 1

It invoke a Lambda function:

SES Config 2

You must add the domain to the Verified identities to make this work. It's also necessary to publish an MX entry in order to declare SES as the email receiver for a specific domain or subdomain (more info here).

Build the Stop Scenario

This time we want to stop the instance after 8h. It's a simple Lambda function.

Let's first create our Lambda function. Go into Lambda, and click on Create function to build a new one. Name it mc_shutdown and use a Node.js 14.x or more runtime.

Replace the content of index.js file with the following:



const AWS = require("aws-sdk");
var ec2 = new AWS.EC2();

exports.handler = async (event) => {
  try {
    var result;
    var params = {
      InstanceIds: [process.env.INSTANCE_ID],
    };
    var data = await ec2.describeInstances(params).promise();
    var instance = data.Reservations[0].Instances[0];

    if (instance.State.Name !== "stopped") {
      var launch_time = new Date(instance.LaunchTime);
      var today = new Date();
      result = "instance running";
      if ((today - launch_time) / 3600000 > process.env.MAX_HOURS) {
        console.log("stopping the instance...");
        var stop_data = await ec2.stopInstances(params).promise();
        result = "instance stopped";
      }
    } else {
      result = "instance not running";
    }
    const response = {
      statusCode: 200,
      body: result,
    };
    return response;
  } catch (error) {
    console.error(error);
    const response = {
      statusCode: 500,
      body: "error during script",
    };
    return response;
  }
};



Enter fullscreen mode Exit fullscreen mode

In Configuration, set the following:

  • add an environnement variable named INSTANCE_ID with the value that correspond to the Instance Id of your Minecraft server (something like i-031fdf9c3bafd7a34).
  • add an environnement variable named MAX_HOURS with the value that correspond to number of hours allowed after startup, like 8 for 8 hours).
  • the role permissions must include the right to start our EC2 instance like this:

Lambda Permissions

We add a trigger to fire the task every 20 minutes:

Add Trigger

Hurray the configuration is done !

Conclusion

This setup is working nicely here, my son is happy because he start himself the instance when he need. I am happy because it reduce a lot the cost of this service. On the last 3 months I see that the EC2 Compute cost for this server is less than 1 US$ πŸ˜… (around 17 US$ before the optimization) so 95% less expensive !

Currently the configuration is made manually in the console, I would love to spend some time to change that one day, using for example the CDK toolkit.

It's also probably possible to manage the storage of the Minecraft world on S3 instead of the Instance EBS disk (some $$ to save here, but not a lot).

It was a very fun project to build using multiple AWS services! Do you see other usages of dynamically boot EC2 instances using Lambda functions? Let me know in the comments!

Top comments (29)

Collapse
 
carlo profile image
Carlo

Hi Julien, really nice guide! Following it I've been able to setup my own server too (only struggled creating the minecraft linux user and make it work without password.
To furtherly save some $$ I've implemented a change to the stop scenario to check if any player is connected and shutdown the server if someone is actually using it.
To do it I've found the nodejs library "mcping-js" that is really easy to use, here's the relevant code snippet:

const mcping = require('mcping-js');

/* Check if instance is running */
/* ... omissis ... */
/* ... */

// variable checkMinecraft is true if the server is running 
// from more than 30 minutes
if (checkMinecraft) {
    var promise = new Promise(function(resolve, reject) {
        // remark: serverAddress should be the internet facing one
        var server = new mcping.MinecraftServer(serverAddress, serverPort);
        server.ping(10000, -1, (err, res) => {
            if (err) {
                console.error(err); 
                resolve(-1);
            } else {
                console.log(res);
                var onlinePlayers = res.players.online;
                resolve(onlinePlayers);
            }
        });
    });
    let minecraftPlayers = await promise;
    if (minecraftPlayers == 0) {
        console.log("no one is playing")
        shutdownInstance = true;
    } else {
        console.log(minecraftPlayers + " player connected")
        result = "Still playing";
    }
}

if (shutdownInstance) {
    // shutdown
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Now I've scheduled the stop scenario to run every 30 minutes.

Collapse
 
gfting profile image
Gabriel Ting • Edited

Thank you for the great guide! Also if anyone is having issues having the systemctl service not actually running, you can sanity check with:
systemctl status minecraft
systemctl is-enabled minecraft

and if it's not enabled, then you can run:
sudo systemctl enable minecraft

One more thing, if you're wondering why mcrcon is missing, follow install instructions for github.com/Tiiffi/mcrcon in the directory named in the script /opt/minecraft/tools/

Collapse
 
nickoc97 profile image
Nicholas O'Connell

king

Collapse
 
calflan profile image
Calum Flanagan

Have you considered using a Fargate instance instead of EC2 so you dont have to keep stopping and starting the instance to save money? As I understand it, Fargate is constantly running (just like ec2), but only charges on a usage basis, rather than based on the uptime (like ec2).

Collapse
 
julbrs profile image
Julien Bras

Hi πŸ‘‹
Fargate is an ECS flavour where you are not managing directly the EC2 behind ECS. But Fargate comes with a pricing per hour: aws.amazon.com/fargate/pricing/

So if you create a Fargate task, you will pay for it as long as the task is up and idle or running πŸ˜‡.

Collapse
 
mudhen459 profile image
Jonathan M. Zook

Hi Julien,

Thank you for this guide on setting up an AWS EC2 instance to use as a Minecraft server. I've been able to follow your directions and get everything set up, but I'm struggling to get the right version of Java installed on the Amazon Linux 2 AMI instance. I installed Java 8 on the server per your instructions, and I used the same URL to pull down the server.jar file. When I try to launch the Minecraft server I get a message stating the file was compiled with Java 60 and I only have Java 52 running. Any thoughts on how to get the correct match of Java and the server.jar file on the server together?

Thanks in advance!!

Collapse
 
julbrs profile image
Julien Bras

Let me check that it seems that the class version is not correct.

52 is Java 8
60 is Java 17 I think.

Maybe Minecraft server is now requirering Java 17? I will check that for you and update the article πŸ•΅οΈ

Collapse
 
mudhen459 profile image
Jonathan M. Zook

Thank you sir. From the quick research I did, I can't seem to find an option for Java above V11 for the Amazon Linux 2 OS. Hopefully your research yields better results than mine.

Thread Thread
 
julbrs profile image
Julien Bras

OK it's related to the Minecraft version, as 1.17 is now requiring Java 16 (instead of Java 8 on 1.16.5, that's a pretty big move!)

minecraft.fandom.com/wiki/Java_Edi...

I will update the article to be sure to download 1.16.5 because it seems less easy to get Java 16 on Amazon Linux 2.

Thanks !

Thread Thread
 
mudhen459 profile image
Jonathan M. Zook • Edited

Julien, I think I found the way to get a higher level of Java on Amazon Linux 2 using the Corretto library. Here are the commands:

sudo rpm --import yum.corretto.aws/corretto.key

sudo curl -L -o /etc/yum.repos.d/corretto.repo yum.corretto.aws/corretto.repo
sudo yum list available | grep java

sudo yum install -y java-17-amazon-corretto-devel.x86_64

sudo update-alternatives --config java
Select the Corretto Java 17 from the list

Thread Thread
 
julbrs profile image
Julien Bras

big thanks for digging that! I will update the article ASAP and test on my minecraft server !

Thread Thread
 
julbrs profile image
Julien Bras

done @mudhen459 thanks a lot !

Collapse
 
yngve_403b23167aa0 profile image
Yngve

Very handy description. However, on my AWS account I have never activated/used SES, so when I try to bring up the console where "it's time to create a new Rule Set in the Email Receiving section:" , this section is greyed out for me. Does the guide assume that one already have set up a domain/emailaddress for the SES?

Collapse
 
julbrs profile image
Julien Bras • Edited

Hello,

It seems that Email Receiving is only active in 3 AWS region (us-east-1, us-west-2 and eu-west-1). Are you in one of those regions?

Source: docs.aws.amazon.com/general/latest...

Thanks !

Collapse
 
yngve_403b23167aa0 profile image
Yngve

Ah - I set it up in eu-north-1. That explains it then :-) Thank you!

Thread Thread
 
yngve_403b23167aa0 profile image
Yngve

Hm - still struggling with the email-part. Do I understand it correctly that you need to use AWS for MX for your domain to get SES to do what you need here?

Thread Thread
 
julbrs profile image
Julien Bras

Hello, yes it miss the MX declaration! Here is the detailed guide: docs.aws.amazon.com/ses/latest/dg/.... It's not needed to use Route53, but only to declare an MX entry in your DNS:

10 inbound-smtp.regionInboundUrl.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

(replace regionInboundUrl by the AWS region you are using)

Collapse
 
shillstr profile image
ScotchJeffrey

Incredible guide! I was able to set up my own EC2 version of a server running in just a few hours. Thank you. I'm excited to try implementing the Lambda Functions

Have you run into this error? "Can't keep up! Did the system time change, or is the server overloaded? Running [####]ms behind, skipping [##] tick(s)".

I'm running on the T2.Small like you recommended but it doesn't seem to be enough. Thoughts?

Collapse
 
julbrs profile image
Julien Bras

Hello!

I am running mine on a t2.small (complete list here), it's a 1 vcpu and 2 GB of memory that fit the bare minimum for a Minecraft server. I am not observing such issues, but there is not a lot of load on the server (probably 2 users maximum !).

You can try to change and give a bit more memory (go to medium or large, and test both t2 and t3 - t3 is the latest generation so maybe more power for the same amount of money).

Good luck with the sizing, there is so much choice on EC2 πŸ˜‡

Collapse
 
tinchox7 profile image
Tinchox

Hi, i need help, i did everything exect the autostart.
how you change directory to /etc/systemd/system/minecraft.service ?

i try cd and listing directories and only showing this

[root@ip server]# ls
server.jar

Collapse
 
julbrs profile image
Julien Bras

can you try:

nano /etc/systemd/system/minecraft.service
Enter fullscreen mode Exit fullscreen mode

or

cd /etc/systemd/system
touch minecraft.service
nano minecraft.service
Enter fullscreen mode Exit fullscreen mode

Thanks !

Collapse
 
archae0pteryx profile image
Ryan Lewis

really great man. There has been some mild changes here and there but was able to follow pretty well. You might add a bit of info about the mcrcon bin and what it does. That was a little confusing as you didn't put instructions about this. Thanks to some comments i got it figured out (pretty much. got some unrelated issues now). Another suggestion might be to add a bit more info about how the mail lamda's work. I know how these things go though... lol. anyway mostly wanted to say thanks!

Collapse
 
josthedude profile image
Jos

First of all, beautiful setup, love the way everything was done here and setup in a nice guide for anyone to understand and learn from.

I do a lot in the Minecraft Multiplayer scene, and I was wondering, had you considered utilizing a game server host such as Minehut // Aternos (free hosts) as an alternative w/ a panel for your son to manage to play with friends? Assuming so, but was just wondering for my own personal curiosity :)

Collapse
 
itkujo profile image
itkujo

I just tried using this guide but cant get the mincraft server to start, I keep getting a pollkit error when executing systemctl daemon-reload.
any help would be appreciated.

Collapse
 
sbartacuss profile image
SBartacusS

You need sudo to run that command. It's a misleading warning.

Collapse
 
julbrs profile image
Julien Bras

What is the step where it’s failing?

Some comments have been hidden by the post's author - find out more