Skip to content disloops

Minecraft Bedrock Server in AWS

I am interested in any kind of open-world server that I can host and allow others to join. It was just a matter of time before I got into Minecraft. This article details the steps I took to do the following:

  • Create a new EC2 instance and install the Minecraft Bedrock server
  • Allow remote access and configure logging through CloudWatch
  • Create an alert that generates SMS messages when users connect
  • Connect to the instance from the PS4 console

Come play on minecraft.disloops.com if you just want to see the finished product. It's a vanilla Bedrock server on survival mode.

Initial Setup

Go into the AWS EC2 console and launch a new instance. I used a t3.micro Ubuntu Server 20.04 instance with a 30 GB EBS volume. I also added a tag to the EBS volume that's configured within the AWS Lifecycle Manager to conduct periodic snapshots of the volume. This is a great way to perform automatic backups of EC2 instances.

I use a single "bastion" server to SSH into EC2 instances with RSA keys, so launching a new one requires me to change the SSH port and correct the security groups. Hopefully you're already familiar with this and have your own process for it.

We will need to attach an additional security group that allows inbound traffic for Minecraft. By default, Minecraft requires ports 19132 and 19133 to accept both TCP and UDP traffic. I originally had problems because I failed to allow UDP traffic. I believe port 19132 is used for IPv4 and 19133 is for IPv6.

Next, create an Elastic IP address and associate it with the instance. You may want to create a DNS entry for your host in Route 53 at this time. Once completed, log in to the EC2, update the operating system and reboot. You can also configure automatic updates as follows:

sudo apt-get install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades

Once installed, edit the following file:

/etc/apt/apt.conf.d/50unattended-upgrades 

Uncomment and change the following settings where necessary:

// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true"; // Automatically reboot *WITHOUT CONFIRMATION* if // the file /var/run/reboot-required is found after the upgrade Unattended-Upgrade::Automatic-Reboot "true"; // Automatically reboot even if there are users currently logged in.
Unattended-Upgrade::Automatic-Reboot-WithUsers "true";

Once that's done, make sure the /etc/apt/apt.conf.d/20auto-upgrades file contains the following lines:

APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Verbose "2"; 

Now patches and upgrades should be applied automatically.

Installing the Minecraft Server

Visit the Minecraft Bedrock server homepage and right-click the Ubuntu download link to copy the path to the installation package. From the EC2 instance, download the package as follows:

wget https://minecraft.azureedge.net/bin-linux/bedrock-server-1.18.2.03.zip

Once downloaded, create a directory and expanded the archive:

mkdir -p /home/ubuntu/Tech/minecraft/server
unzip bedrock-server-1.18.2.03.zip -d /home/ubuntu/Tech/minecraft/server

Now you can configure the Minecraft server to your liking. I am not going to explain this in-depth, but my process was as follows:

  • Added my Xbox Live Minecraft user as an operator in permissions.json
  • Added my user in whitelist.json (despite not enabling the whitelist)
  • Changed the following settings in server.properties:
    • server-name
    • difficulty
    • view-distance
    • tick-distance
    • player-idle-timeout
    • level-name

Creating a Service

Next we'll set up Minecraft as a persistent service using the systemd daemon. First create a unit file:

sudo vi /etc/systemd/system/minecraft.service

The file should have the following contents:

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

[Service]
User=ubuntu
Type=simple
WorkingDirectory=/home/ubuntu/Tech/minecraft/server
ExecStart=/bin/bash -c "LD_LIBRARY_PATH=. ./bedrock_server 2>&1 | tee -a ./custom_log.txt"
Restart=on-failure

[Install]
WantedBy=multi-user.target

Looking at the ExecStart line, you'll see that we're redirecting output from the running process to a custom log file. That's because Minecraft does not keep a running log file by default, but rather writes the output to a file called Dedicated_Server.txt once the Minecraft server is stopped. Creating a live log file will allow CloudWatch to send us custom alerts when someone logs in (more on this later).

Next we'll run the following three commands to enable and start the Minecraft service:

sudo systemctl daemon-reload
sudo systemctl enable /etc/systemd/system/minecraft.service
sudo systemctl start minecraft

This should now run on startup.

Setting up Logging

AWS has a newer agent for collecting logs called the Unified CloudWatch Agent. Instructions for installing it can be found here:

https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html

While running the configuration wizard, I chose not to monitor any system metrics. Enter the path to the custom Minecraft server log when asked what logs to monitor:

/home/ubuntu/Tech/minecraft/server/custom_log.txt

The resulting configuration file will be stored here:

/opt/aws/amazon-cloudwatch-agent/bin/config.json

Remember to associate the correct IAM Policy with the EC2 instance in order for the CloudWatch agent to operate. At the time of writing this, it requires the CloudWatchAgentServerPolicy policy. Lastly, start the agent with the following command:

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json

Setting up Alerting

Now we're going to use the CloudWatch log data to generate a text message any time someone logs in. First, go to AWS SNS and create a new topic that sends a text message when invoked. Copy the ARN of this topic.

Next, go to Lambda and create a new Python 3.8 function with the following contents:

# Filter pattern:
# ?"] Player connected: "

# Taken from:
# https://awsfeed.com/whats-new/management-tools/how-to-get-notified-on-specific-lambda-function-error-patterns-using-cloudwatch

import base64
import boto3
import gzip
import json
import logging
import os
from botocore.exceptions import ClientError

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def log_payload(event):

    logger.setLevel(logging.DEBUG)
    compressed_payload = base64.b64decode(event['awslogs']['data'])
    uncompressed_payload = gzip.decompress(compressed_payload)
    payload = json.loads(uncompressed_payload)

    log_msg = payload['logEvents'][0]['message']
    log_stream = payload['logStream']

    return log_msg, log_stream

def publish_message(log_msg):

    # Get the SNS Topic ARN in the Lambda environment variables
    sns_arn = os.environ['SNS_ARN']

    snsclient = boto3.client('sns')

    try:
        message = 'Minecraft: ' + log_msg.rsplit(']',1)[1]
        snsclient.publish( TargetArn=sns_arn, Subject=f'Minecraft Login Alert - AWS Lambda', Message=message )
    except ClientError as e:
    	logger.error("An error occured: %s" % e)

def handler(event, context):
    log_msg, log_stream = log_payload(event)

    if log_stream == 'custom_log.txt':
        publish_message(log_msg)
    else:
        logger.debug(f'Exiting due to log_stream: {log_stream}')

During creation, create a new "Execution Role" and attach a policy that will allow it to publish to the SNS topic you just created. Here is one such policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "[SNS ARN HERE]"
        }
    ]
}

It should also have the AWS-managed policy called AWSLambdaBasicExecutionRole attached.

Save this Lambda function and click "Configuration" then "Environment variables". Add a new variable with a key of SNS_ARN and assign the ARN of your SNS topic as the value.

Now navigate back to the CloudWatch log group for Minecraft logs in the AWS console. It should show up if you've started the Minecraft server and the CloudWatch agent correctly. Click on the "Subscription filters" tab and create a new Lambda subscription filter for this log group. Associate it with the Lambda function we just created. The "Subscription filter pattern" field should read as follows:

?"] Player connected: "

Once saved, this filter should now watch the log file for logins and use the Lambda function to send you a text message when someone connects.

Connecting from PS4

The only thing left to do is play Minecraft. Sadly, connecting to this server from a console requires one more configuration change.

Minecraft does not allow console players to connect to remote servers except for a number of "featured" servers that are explicitly listed. However, by modifying our local DNS settings, we can redirect Minecraft to our own server when it attempts to connect to one of the featured servers.

My own DNS provider makes it easy to set up custom DNS entries. Most home routers also have an option for static DNS entries. We just need to redirect requests for the featured servers to the hostname or IP of our own server. At the time of writing this, my DNS entries are as follows:

*.play.inpvp.net → minecraft.disloops.com
*.mco.cubecraft.net → minecraft.disloops.com
*.geo.hivebedrock.network → minecraft.disloops.com
*.play.galaxite.net → minecraft.disloops.com
*.mineplex.com → minecraft.disloops.com
*.lbsg.net → minecraft.disloops.com
*.pixelparadise.gg → minecraft.disloops.com

Now connecting to any featured server will instead connect you to my own server.

I hope you do connect to minecraft.disloops.com and check out whatever I'm building – all are welcome. Leave me a note if these steps were useful to you or you have any questions and I'll help if I can!

Leave a Reply

Your email address will not be published. Required fields are marked *