Let's Set Up A Minecraft Server
Let's set up a Minecraft server inside an LXD container and configure automatic backups.
The Big Picture
The purpose of this guide is to run Minecraft inside a portable Linux container where...
- the server can be moved/copied between hosts
- the container is safe from failed upgrades via snapshot/rollback features
- the host is protected from most Minecraft-driven vulnerabilities by isolating the server processes from the host system
- dependencies are isolated from the host system to reduce environment pollution
- automatic backups are taken via a scheduled script
LXD Container Setup
First, you will need to ensure LXD is installed on the host system and ready to launch containers. The setup instructions vary by Linux distro so your process may be different from mine. Please consult the official setup documentation for instructions.
Now it is time to make the container. Since Minecraft usually benefits from using the latest Java version, I will be creating an Arch Linux container for this tutorial. After listing available Arch Linux images with lxc image list images: arch amd64
, we can see that our container should be launched using the archlinux/cloud
image.
Note: Using a rolling release distro like Arch Linux is not typically recommended for server use. Thankfully, LXD has a snapshot/rollback system which can help recover a broken system after failed updates. Just make sure you use lxc snapshot
before updating so you can use lxc rollback
on a failed upgrade.
cait@athena ~> lxc image list images: arch amd64
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
| archlinux (5 more) | 3bf6e3fd8ac9 | yes | Archlinux current amd64 (20231117_04:18) | x86_64 | VIRTUAL-MACHINE | 537.95MB | Nov 17, 2023 at 12:00am (UTC) |
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
| archlinux (5 more) | a560cb0206b5 | yes | Archlinux current amd64 (20231117_04:18) | x86_64 | CONTAINER | 189.48MB | Nov 17, 2023 at 12:00am (UTC) |
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
| archlinux/cloud (3 more) | c1cb4cc1da16 | yes | Archlinux current amd64 (20231117_04:18) | x86_64 | CONTAINER | 212.71MB | Nov 17, 2023 at 12:00am (UTC) |
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
| archlinux/cloud (3 more) | f6ffac690baa | yes | Archlinux current amd64 (20231117_04:18) | x86_64 | VIRTUAL-MACHINE | 553.92MB | Nov 17, 2023 at 12:00am (UTC) |
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
| archlinux/desktop-gnome (3 more) | 6c31f85cc316 | yes | Archlinux current amd64 (20231117_04:18) | x86_64 | VIRTUAL-MACHINE | 1296.61MB | Nov 17, 2023 at 12:00am (UTC) |
+----------------------------------+--------------+--------+------------------------------------------+--------------+-----------------+-----------+-------------------------------+
cait@athena ~>
It is time to launch the container. I named the container demo
in this tutorial but you can pick any name you wish. The command to launch the server is lxc launch images:archlinux/cloud demo
. You can verify the server is running by using lxc list demo
.
cait@athena ~> lxc launch images:archlinux/cloud demo
Creating demo
Starting demo
cait@athena ~> lxc list demo
+------+---------+----------------------+-----------------------------------------------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+---------+----------------------+-----------------------------------------------+-----------+-----------+
| demo | RUNNING | 10.97.101.130 (eth0) | fd42:f75c:907b:627b:216:3eff:fe32:df7c (eth0) | CONTAINER | 0 |
+------+---------+----------------------+-----------------------------------------------+-----------+-----------+
cait@athena ~>
Let's go ahead and set up the port forwarding. Minecraft normally runs a TCP connection on port 25565
. I will use 25566
instead of the default since this server host is already running a Minecraft server. In LXD syntax, the command to create a port proxy is lxc config device add demo tcp25566 proxy listen=tcp:0.0.0.0:25566 connect=tcp:127.0.0.1:25566
. This command means "configure the demo
container to have a rule called tcp25566
which proxies a tcp
socket on port 25566
across all host network interfaces to a tcp
socket on port 25566
on the container's localhost". The command lxc config device show demo
will show our new proxy rule.
cait@athena ~> lxc config device show demo
tcp25566:
connect: tcp:127.0.0.1:25566
listen: tcp:0.0.0.0:25566
type: proxy
cait@athena ~>
Arch Linux Configuration
It is time to enter the container and set up the Minecraft service. Are you ready for the easy part? Good. Use lxc shell demo
to launch a shell inside the container.
cait@athena ~> lxc shell demo
[root@demo ~]#
Firstly, we need to upgrade the container to the latest version of Arch Linux. Run pacman -Syyu
. While -Syyu
is not recommended for upgrades, it is recommended for first-use.
[root@demo ~]# pacman -Syyu
:: Synchronizing package databases...
core 132.8 KiB 137 KiB/s 00:01 [###########################] 100%
extra 8.3 MiB 1120 KiB/s 00:08 [###########################] 100%
:: Starting full system upgrade...
there is nothing to do
[root@demo ~]#
We will be editing a lot of configuration files in this tutorial so I recommend installing your preferred text editor. For me, I will be installing Kakoune as my text editor. You will also need to install git
, cronie
, jdk-openjdk
, wget
, zip
and unzip
. The command for my package list ends up as pacman -S git kakoune cronie jdk-openjdk wget zip unzip
.
[root@demo ~]# pacman -S git kakoune cronie jdk-openjdk wget zip unzip
resolving dependencies...
looking for conflicting packages...
warning: dependency cycle detected:
warning: harfbuzz will be installed before its freetype2 dependency
Packages (25) freetype2-2.13.2-1 giflib-5.2.1-2 graphite-1:1.3.14-3
harfbuzz-8.3.0-1 hicolor-icon-theme-0.17-3
java-environment-common-3-5 java-runtime-common-3-5 jbigkit-2.1-7
lcms2-2.15-1 libjpeg-turbo-3.0.1-1 libnet-2:1.3-1 libpng-1.6.40-2
libtiff-4.6.0-1 nspr-4.35-1 nss-3.94-1 perl-error-0.17029-5
perl-mailtools-2.21-7 perl-timedate-2.33-5 cronie-1.7.0-4
git-2.42.1-1 jdk-openjdk-21.u35-8 kakoune-2023.07.29-1 unzip-6.0-20
wget-1.21.4-1 zip-3.0-11
Total Download Size: 471.69 MiB
Total Installed Size: 1193.15 MiB
:: Proceed with installation? [Y/n] y
:: Retrieving packages...
jdk-openjdk-21.u3... 457.5 MiB 3.05 MiB/s 02:30 [###########################] 100%
git-2.42.1-1-x86_64 6.2 MiB 2.94 MiB/s 00:02 [###########################] 100%
nss-3.94-1-x86_64 1617.6 KiB 2.18 MiB/s 00:01 [###########################] 100%
kakoune-2023.07.2... 1181.5 KiB 1818 KiB/s 00:01 [###########################] 100%
harfbuzz-8.3.0-1-... 1008.4 KiB 1681 KiB/s 00:01 [###########################] 100%
libtiff-4.6.0-1-x... 961.1 KiB 1197 KiB/s 00:01 [###########################] 100%
wget-1.21.4-1-x86_64 731.9 KiB 1515 KiB/s 00:00 [###########################] 100%
libjpeg-turbo-3.0... 536.9 KiB 1209 KiB/s 00:00 [###########################] 100%
freetype2-2.13.2-... 523.2 KiB 1123 KiB/s 00:00 [###########################] 100%
libnet-2:1.3-1-x86_64 295.0 KiB 922 KiB/s 00:00 [###########################] 100%
libpng-1.6.40-2-x... 248.5 KiB 866 KiB/s 00:00 [###########################] 100%
lcms2-2.15-1-x86_64 214.9 KiB 776 KiB/s 00:00 [###########################] 100%
nspr-4.35-1-x86_64 198.3 KiB 734 KiB/s 00:00 [###########################] 100%
zip-3.0-11-x86_64 169.9 KiB 708 KiB/s 00:00 [###########################] 100%
unzip-6.0-20-x86_64 142.3 KiB 619 KiB/s 00:00 [###########################] 100%
cronie-1.7.0-4-x86_64 91.1 KiB 472 KiB/s 00:00 [###########################] 100%
graphite-1:1.3.14... 84.0 KiB 442 KiB/s 00:00 [###########################] 100%
giflib-5.2.1-2-x86_64 73.7 KiB 388 KiB/s 00:00 [###########################] 100%
perl-mailtools-2.... 58.6 KiB 319 KiB/s 00:00 [###########################] 100%
jbigkit-2.1-7-x86_64 51.8 KiB 283 KiB/s 00:00 [###########################] 100%
perl-timedate-2.3... 34.0 KiB 192 KiB/s 00:00 [###########################] 100%
perl-error-0.1702... 21.4 KiB 124 KiB/s 00:00 [###########################] 100%
hicolor-icon-them... 9.8 KiB 56.6 KiB/s 00:00 [###########################] 100%
java-runtime-comm... 5.0 KiB 29.7 KiB/s 00:00 [###########################] 100%
java-environment-... 2.6 KiB 15.4 KiB/s 00:00 [###########################] 100%
Total (25/25) 471.7 MiB 2.93 MiB/s 02:41 [###########################] 100%
(25/25) checking keys in keyring [###########################] 100%
(25/25) checking package integrity [###########################] 100%
(25/25) loading package files [###########################] 100%
(25/25) checking for file conflicts [###########################] 100%
(25/25) checking available disk space [###########################] 100%
:: Processing package changes...
( 1/25) installing perl-error [###########################] 100%
( 2/25) installing perl-timedate [###########################] 100%
( 3/25) installing perl-mailtools [###########################] 100%
( 4/25) installing git [###########################] 100%
Optional dependencies for git
tk: gitk and git gui
openssh: ssh transport and crypto [installed]
perl-libwww: git svn
perl-term-readkey: git svn and interactive.singlekey setting
perl-io-socket-ssl: git send-email TLS support
perl-authen-sasl: git send-email TLS support
perl-mediawiki-api: git mediawiki support
perl-datetime-format-iso8601: git mediawiki support
perl-lwp-protocol-https: git mediawiki https support
perl-cgi: gitweb (web interface) support
python: git svn & git p4 [installed]
subversion: git svn
org.freedesktop.secrets: keyring credential helper
libsecret: libsecret credential helper [installed]
( 5/25) installing kakoune [###########################] 100%
Optional dependencies for kakoune
aspell: spell check, correct text
clang: error reporting and diagnostics, completion
editorconfig-core-c: set formatting options project-wide
git: display and cycle through hunks, blame lines, handle file status [installed]
kak-lsp: Language Server Protocol (LSP) client
tmux: split windows, spawn tabs
xdotool: X11 window management
xorg-xmessage: print detailed crash information in a separate window
( 6/25) installing cronie [###########################] 100%
Optional dependencies for cronie
smtp-server: send job output via email
smtp-forwarder: forward job output to email server
( 7/25) installing java-runtime-common [###########################] 100%
For the complete set of Java binaries to be available in your PATH,
you need to re-login or source /etc/profile.d/jre.sh
Please note that this package does not support forcing JAVA_HOME as former package java-common did
( 8/25) installing nspr [###########################] 100%
( 9/25) installing nss [###########################] 100%
(10/25) installing libjpeg-turbo [###########################] 100%
Optional dependencies for libjpeg-turbo
java-runtime>11: for TurboJPEG Java wrapper [pending]
(11/25) installing jbigkit [###########################] 100%
(12/25) installing libtiff [###########################] 100%
Optional dependencies for libtiff
freeglut: for using tiffgt
(13/25) installing lcms2 [###########################] 100%
(14/25) installing libnet [###########################] 100%
(15/25) installing libpng [###########################] 100%
(16/25) installing graphite [###########################] 100%
Optional dependencies for graphite
graphite-docs: Documentation
(17/25) installing harfbuzz [###########################] 100%
Optional dependencies for harfbuzz
harfbuzz-utils: utilities
(18/25) installing freetype2 [###########################] 100%
(19/25) installing java-environment-common [###########################] 100%
(20/25) installing hicolor-icon-theme [###########################] 100%
(21/25) installing giflib [###########################] 100%
(22/25) installing jdk-openjdk [###########################] 100%
Optional dependencies for jdk-openjdk
java-rhino: for some JavaScript support
alsa-lib: for basic sound support
gtk2: for the Gtk+ 2 look and feel - desktop usage
gtk3: for the Gtk+ 3 look and feel - desktop usage
(23/25) installing wget [###########################] 100%
Optional dependencies for wget
ca-certificates: HTTPS downloads [installed]
(24/25) installing zip [###########################] 100%
(25/25) installing unzip [###########################] 100%
:: Running post-transaction hooks...
(1/5) Creating system user accounts...
(2/5) Reloading system manager configuration...
(3/5) Arming ConditionNeedsUpdate...
(4/5) Warn about old perl modules
(5/5) Updating the info directory file...
[root@demo ~]#
Installing Tools
Now it is time to clone and install some useful tools. There are three tools in total:
- Minecraft-Scripts, my collection of Minecraft scripts and config files
- mcrcon, an RCON client for Minecraft servers
- Verifier, my file verification utility which is useful for backups
mcrcon
Clone the repository using git clone https://github.com/Tiiffi/mcrcon.git
. Enter the downloaded directory with cd mcrcon
. We will need to install some tools to build this repository. Use pacman -S base-devel
to install some basic developer tools including gcc
and make
. Run the make
command to compile mcrcon. If there are no errors, you should see a file called mcrcon
in the current directory. Run make install
to install it globally on the system.
Here is that same sequence of commands in an easy to copy/paste format:
git clone https://github.com/Tiiffi/mcrcon.git
cd mcrcon
pacman -S base-devel
make
make install
cd
mcrcon
Here is my terminal session for the entire setup process:
[root@demo ~]# git clone https://github.com/Tiiffi/mcrcon.git
Cloning into 'mcrcon'...
remote: Enumerating objects: 527, done.
remote: Counting objects: 100% (36/36), done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 527 (delta 19), reused 20 (delta 12), pack-reused 491
Receiving objects: 100% (527/527), 116.04 KiB | 1.66 MiB/s, done.
Resolving deltas: 100% (311/311), done.
[root@demo ~]# cd mcrcon
[root@demo mcrcon]# ls
CHANGELOG.md INSTALL.md LICENSE Makefile mcrcon.1 mcrcon.c README.md
[root@demo mcrcon]# pacman -S base-devel
resolving dependencies...
looking for conflicting packages...
Packages (19) autoconf-2.71-4 automake-1.16.5-2 binutils-2.41-3 bison-3.8.2-6
debugedit-5.0-5 fakeroot-1.32.2-1 flex-2.6.4-5 gc-8.2.4-1
gcc-13.2.1-3 guile-3.0.9-1 jansson-2.14-2 libisl-0.26-1
libmpc-1.3.1-1 libtool-2.4.7+4+g1ec8fa28-6 m4-1.4.19-3 make-4.4.1-2
patch-2.7.6-10 pkgconf-1.8.1-1 base-devel-1-1
Total Download Size: 67.45 MiB
Total Installed Size: 297.01 MiB
:: Proceed with installation? [Y/n] y
:: Retrieving packages...
gcc-13.2.1-3-x86_64 46.8 MiB 2.86 MiB/s 00:16 [###########################] 100%
guile-3.0.9-1-x86_64 8.1 MiB 3.14 MiB/s 00:03 [###########################] 100%
binutils-2.41-3-x... 7.6 MiB 3.07 MiB/s 00:02 [###########################] 100%
libisl-0.26-1-x86_64 873.3 KiB 1736 KiB/s 00:01 [###########################] 100%
bison-3.8.2-6-x86_64 772.5 KiB 1545 KiB/s 00:01 [###########################] 100%
autoconf-2.71-4-any 644.8 KiB 1384 KiB/s 00:00 [###########################] 100%
automake-1.16.5-2-any 612.8 KiB 1277 KiB/s 00:00 [###########################] 100%
make-4.4.1-2-x86_64 523.8 KiB 1290 KiB/s 00:00 [###########################] 100%
libtool-2.4.7+4+g... 412.4 KiB 1165 KiB/s 00:00 [###########################] 100%
flex-2.6.4-5-x86_64 307.5 KiB 1015 KiB/s 00:00 [###########################] 100%
m4-1.4.19-3-x86_64 246.0 KiB 869 KiB/s 00:00 [###########################] 100%
gc-8.2.4-1-x86_64 234.3 KiB 817 KiB/s 00:00 [###########################] 100%
patch-2.7.6-10-x86_64 93.0 KiB 458 KiB/s 00:00 [###########################] 100%
libmpc-1.3.1-1-x86_64 84.2 KiB 413 KiB/s 00:00 [###########################] 100%
fakeroot-1.32.2-1... 76.6 KiB 383 KiB/s 00:00 [###########################] 100%
pkgconf-1.8.1-1-x... 57.5 KiB 298 KiB/s 00:00 [###########################] 100%
jansson-2.14-2-x86_64 51.7 KiB 268 KiB/s 00:00 [###########################] 100%
debugedit-5.0-5-x... 43.5 KiB 229 KiB/s 00:00 [###########################] 100%
base-devel-1-1-any 2.0 KiB 11.2 KiB/s 00:00 [###########################] 100%
Total (19/19) 67.4 MiB 2.47 MiB/s 00:27 [###########################] 100%
(19/19) checking keys in keyring [###########################] 100%
(19/19) checking package integrity [###########################] 100%
(19/19) loading package files [###########################] 100%
(19/19) checking for file conflicts [###########################] 100%
(19/19) checking available disk space [###########################] 100%
:: Processing package changes...
( 1/19) installing m4 [###########################] 100%
( 2/19) installing autoconf [###########################] 100%
( 3/19) installing automake [###########################] 100%
( 4/19) installing jansson [###########################] 100%
( 5/19) installing binutils [###########################] 100%
Optional dependencies for binutils
debuginfod: for debuginfod server/client functionality
( 6/19) installing bison [###########################] 100%
( 7/19) installing debugedit [###########################] 100%
( 8/19) installing fakeroot [###########################] 100%
( 9/19) installing flex [###########################] 100%
(10/19) installing libmpc [###########################] 100%
(11/19) installing libisl [###########################] 100%
(12/19) installing gcc [###########################] 100%
Optional dependencies for gcc
lib32-gcc-libs: for generating code for 32-bit ABI
(13/19) installing libtool [###########################] 100%
(14/19) installing gc [###########################] 100%
(15/19) installing guile [###########################] 100%
(16/19) installing make [###########################] 100%
(17/19) installing patch [###########################] 100%
Optional dependencies for patch
ed: for patch -e functionality
(18/19) installing pkgconf [###########################] 100%
(19/19) installing base-devel [###########################] 100%
:: Running post-transaction hooks...
(1/2) Arming ConditionNeedsUpdate...
(2/2) Updating the info directory file...
[root@demo mcrcon]# make
gcc -std=gnu99 -Wall -Wextra -Wpedantic -Os -s -fstack-protector-strong -o mcrcon mcrcon.c
[root@demo mcrcon]# ls
CHANGELOG.md INSTALL.md LICENSE Makefile mcrcon mcrcon.1 mcrcon.c README.md
[root@demo mcrcon]# make install
install -vD mcrcon /usr/local/bin/mcrcon
'mcrcon' -> '/usr/local/bin/mcrcon'
install -vD -m 0644 mcrcon.1 /usr/local/share/man/man1/mcrcon.1
install: creating directory '/usr/local/share/man/man1'
'mcrcon.1' -> '/usr/local/share/man/man1/mcrcon.1'
\nmcrcon installed. Run 'make uninstall' if you want to uninstall.\n
[root@demo mcrcon]# cd
[root@demo ~]# mcrcon
You must give password (-p password).
Try 'mcrcon -h' or 'man mcrcon' for help.
[root@demo ~]#
Verifier
Return to the home directory using cd
with no arguments. Run git clone https://gitlab.com/BitiTiger/verifier.git
to download the program. Enter the directory with cd verifier
. Run the install script using ./install
. You can now return to the home directory with cd
and verify the installation using verifier --help
. If you encounter errors, ensure the python
package is installed.
Here is that same sequence of commands in an easy to copy/paste format:
git clone https://gitlab.com/BitiTiger/verifier.git
cd verifier
./install
cd
verifier --help
Here is my terminal session for the entire setup process:
[root@demo ~]# git clone https://gitlab.com/BitiTiger/verifier.git
Cloning into 'verifier'...
remote: Enumerating objects: 348, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 348 (delta 5), reused 5 (delta 2), pack-reused 333
Receiving objects: 100% (348/348), 100.46 KiB | 5.91 MiB/s, done.
Resolving deltas: 100% (197/197), done.
[root@demo ~]# cd verifier/
[root@demo verifier]# ./install
Getting sudo access...
Making folder for program files...
Copying program files...
Creating systemlink...
DONE! Try "verifier --help" to verify installation
[root@demo verifier]# cd
[root@demo ~]# verifier --help
Usage: verifier [option]
All options:
--generate : generate verification metadata
--verify : check files for corruption
--delete : delete verification metadata
--help : show help and exit
--license : show program license
--license-w : show license warranty
--license-c : show license conditions
--version : show version and exit
[root@demo ~]#
Minecraft-Scripts
Clone the repository using git clone https://gitlab.com/BitiTiger/minecraft-scripts.git
. This repository contains most of the files we will need to make in order to use the server. The process is very hands-on so there is no magic script to set it up.
Enter the minecraft-scripts
directory using cd minecraft-scripts
and run ls
to list its contents. To explain what you are looking at, consult the table below. I will guide you through the setup process so try not to worry about these files.
File Name | Description |
---|---|
backup | the automatic backup script used by cron |
crontab.txt | the list of cron jobs to run |
LICENSE.md | license information for the repository |
minecraft.service | the systemd service file for the server |
rcon | the RCON connection script used by cron |
README.md | important information for the repository |
start | a script for manually starting the server |
start_service | a script for starting the server via systemd |
Note: start
will loop forever and is useful when hosting the server via an interactive session through something like screen
or tmux
. start_service
is intended for use with an init system or single-use launches.
Copy the backup
, crontab.txt
and rcon
files to the home directory of the root
user with cp backup crontab.txt rcon /root/
.
Now let's create the minecraft
user which will run the server process. Use useradd -m minecraft
to add the user to the system. Copy start
, start_service
, and minecraft.service
to the home directory of the minecraft
user with cp start start_service minecraft.service /home/minecraft/
. Ensure the files are owned by the minecraft
user by running chown minecraft:minecraft /home/minecraft/start /home/minecraft/start_service /home/minecraft/minecraft.service
.
Here are all the commands up to this point in an easy to copy/paste format:
git clone https://gitlab.com/BitiTiger/minecraft-scripts.git
cd minecraft-scripts
ls
cp backup crontab.txt rcon /root/
useradd -m minecraft
cp start start_service minecraft.service /home/minecraft/
chown minecraft:minecraft /home/minecraft/start /home/minecraft/start_service /home/minecraft/minecraft.service
Minecraft User Setup
The next part of setup is done from the minecraft
user. Use su minecraft
and cd
to switch to the minecraft
user and its home directory. Make a directory called server-files
which will contain all the Minecraft related files using mkdir server-files
. Enter that directory with cd server-files
. Now we will make two directories: one for automatic backups and one for the server jar. Run mkdir autobackups myserver
to make the two directories. Move the start
and start_service
files into the myserver
directory with mv /home/minecraft/start /home/minecraft/start_service /home/minecraft/server-files/myserver/
.
It is time to download the server jar. Visit the official download page and copy the link to the server jar. Now ensure you are inside the myserver
directory with cd /home/minecraft/server-files/myserver
and run wget link
where link
is your copied link. At the time of writing, Minecraft 1.20.2 is the latest version so my command is wget https://piston-data.mojang.com/v1/objects/5b868151bd02b41319f54c8d4061b8cae84e665c/server.jar
Here are all the commands used since the last code block:
su minecraft
cd
mkdir server-files
cd server-files
mkdir autobackups myserver
cd myserver
mv /home/minecraft/start /home/minecraft/start_service /home/minecraft/server-files/myserver/
wget https://piston-data.mojang.com/v1/objects/5b868151bd02b41319f54c8d4061b8cae84e665c/server.jar
Now let's configure the start scripts. Open start
in your preferred text editor. Change JAVA_EXECUTABLE="/usr/lib/jvm/java-21-openjdk/bin/java"
to the path to your installed version of Java. The path should be similar in the future but with a different number (ex: java-21-openjdk
-> java-22-openjdk
). Change SERVER_JAR="./server.jar"
to the path of your server jar file. This should be the same unless you downloaded a custom server jar. Optionally, you can change JAVA_FLAGS="-Xms2G -Xmx4G"
to your preferred memory values. Make sure you update the same lines inside start_service
as well. You can use diff
to check if the lines match. Here is the output of diff start start_service
when the configuration lines match:
[minecraft@demo myserver]$ diff start start_service
14,32c14,15
< # function which counts down for 10 seconds
< function countdown {
< for x in {10..1}; do
< if [ $x -eq 1 ]; then
< printf "\rRestarting server in $x second... "
< else
< printf "\rRestarting server in $x seconds... "
< fi
< sleep 1
< done
< printf "\rRestarting server now... \n"
< }
<
< # keep restarting the server in case it crashes
< while :; do
< $JAVA_EXECUTABLE $JAVA_FLAGS -jar $SERVER_JAR $JAR_FLAGS
< echo "Server stopped."
< countdown
< done
---
> # run the server
> $JAVA_EXECUTABLE $JAVA_FLAGS -jar $SERVER_JAR $JAR_FLAGS
Run the server with ./start_service
and wait until it crashes. You should something like this in your terminal:
[00:07:11] [ServerMain/WARN]: Failed to load eula.txt
[00:07:11] [ServerMain/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
Server stopped.
Open eula.txt
in your preferred text editor and change eula=false
to eula=true
. Restart the Minecraft server with ./start_service
. The server should output a line stating that the server has loaded like the one below:
[00:09:56] [Server thread/INFO]: Time elapsed: 30943 ms
[00:09:56] [Server thread/INFO]: Done (36.580s)! For help, type "help"
Use the stop
command to stop the server. The server may take a long time to stop since it is saving the world for the first time. Open server.properties
with your preferred text editor. Change enable-rcon=false
to enable-rcon=true
and rcon.password=
to rcon.password=PASS
where PASS
is your preferred password. Note that we did not forward the RCON port to the host's network interface so your password doesn't need to be super secure. For this tutorial, I'm using rawrxd
as my RCON password so my line is set to rcon.password=rawrxd
. I also changed server-port=25565
to server-port=25566
and query.port=25565
to query.port=25566
to match my port forwarding as mentioned earlier. Go ahead and run ./start_service
again and verify your server starts up correctly. You should see something similar to the lines below:
[00:17:29] [Server thread/INFO]: Time elapsed: 4830 ms
[00:17:29] [Server thread/INFO]: Done (6.819s)! For help, type "help"
[00:17:29] [Server thread/INFO]: Starting remote control listener
[00:17:29] [Server thread/INFO]: Thread RCON Listener started
[00:17:29] [Server thread/INFO]: RCON running on 0.0.0.0:25575
Congratulations! The Minecraft server part of the setup is complete. Now we just need to configure automatic backups and the system service. Stop the server by using the stop
console command. This is the last time we will directly run the start_service
script.
Go back to the minecraft
user's home directory with cd
. Open minecraft.service
with your preferred text editor. Change WorkingDirectory=/home/minecraft/server-files/my-server/
to WorkingDirectory=/home/minecraft/server-files/myserver/
. Change ExecStart=/home/minecraft/server-files/my-server/start_service
to ExecStart=/home/minecraft/server-files/myserver/start_service
.
Systemd Service Configuration
Now we are done with the minecraft
user. Run exit
to return to a root shell. Ensure you are in root
's home directory by running cd
.
Link the minecraft.service
file to /etc/systemd/system/minecraft.service
with ln -s /home/minecraft/minecraft.service /etc/systemd/system/minecraft.service
. Reload the systemd daemon using systemctl daemon-reload
. This tells systemd that the service files have changed and that it needs to scan for changes. Now use systemctl enable --now minecraft.service
to start and enable the Minecraft service. You can use systemctl status minecraft.service
to verify the Minecraft server is running. You should see a line that says Active: active (running)
in the output. If you are stuck inside a pager window, use q
to exit.
[root@demo ~]# systemctl daemon-reload
[root@demo ~]# systemctl enable --now minecraft.service
Created symlink /etc/systemd/system/multi-user.target.wants/minecraft.service → /home/minecraft/minecraft.service.
[root@demo ~]# systemctl status minecraft.service
WARNING: terminal is not fully functional
Press RETURN to continue
● minecraft.service - Minecraft server
Loaded: loaded (/etc/systemd/system/minecraft.service; enabled; preset: disabled)
Drop-In: /run/systemd/system/service.d
└─zzz-lxc-service.conf
Active: active (running) since Sat 2023-11-18 00:42:06 UTC; 24s ago
Main PID: 3252 (start_service)
Tasks: 52 (limit: 4915)
Memory: 1.7G
CPU: 1min 24.901s
CGroup: /system.slice/minecraft.service
├─3252 /bin/sh /home/minecraft/server-files/myserver/start_service
└─3253 /usr/lib/jvm/java-21-openjdk/bin/java -Xms2G -Xmx4G -jar ./server>
Nov 18 00:42:23 demo start_service[3253]: [00:42:23] [Worker-Main-4/INFO]: Preparing >
Nov 18 00:42:23 demo start_service[3253]: [00:42:23] [Worker-Main-4/INFO]: Preparing >
Nov 18 00:42:23 demo start_service[3253]: [00:42:23] [Worker-Main-11/INFO]: Preparing>
Nov 18 00:42:24 demo start_service[3253]: [00:42:24] [Worker-Main-4/INFO]: Preparing >
Nov 18 00:42:24 demo start_service[3253]: [00:42:24] [Worker-Main-6/INFO]: Preparing >
Nov 18 00:42:25 demo start_service[3253]: [00:42:25] [Server thread/INFO]: Time elaps>
Nov 18 00:42:25 demo start_service[3253]: [00:42:25] [Server thread/INFO]: Done (6.24>
Nov 18 00:42:25 demo start_service[3253]: [00:42:25] [Server thread/INFO]: Starting r>
Nov 18 00:42:25 demo start_service[3253]: [00:42:25] [Server thread/INFO]: Thread RCO>
Nov 18 00:42:25 demo start_service[3253]: [00:42:25] [Server thread/INFO]: RCON runni>
[root@demo ~]#
RCON Configuration
Open /root/rcon
in your preferred text editor. Change RCON_PASS="rawrxd"
to your RCON password. I set up my server to use rawrxd
as my RCON password so no change is needed for me. You can use ./rcon list
to verify that the rcon
script is working properly.
[root@demo ~]# ./rcon list
There are 0 of a max of 20 players online:
[root@demo ~]#
Backup Configuration
Open backup
in your preferred text editor. Change SERVER_DIR_NAME="my-server"
to SERVER_DIR_NAME="myserver"
. If you followed my tutorial exactly, no other lines should need changes. Test the backup script by running ./backup
. If the script does not crash, you have successfully configured backups.
Cron Configuration
Ensure the cronie service is enabled and started by using systemctl enable --now cronie
. You can verify it is working by running systemctl status cronie
. You should see a line that says Active: active (running)
in the output. If you are stuck inside a pager window, use q
to exit.
[root@demo ~]# systemctl status cronie
● cronie.service - Command Scheduler
Loaded: loaded (/usr/lib/systemd/system/cronie.service; enabled; preset: disable>
Drop-In: /run/systemd/system/service.d
└─zzz-lxc-service.conf
Active: active (running) since Sat 2023-11-18 01:05:24 UTC; 44s ago
Main PID: 4241 (crond)
Tasks: 1 (limit: 4915)
Memory: 888.0K
CPU: 2ms
CGroup: /system.slice/cronie.service
└─4241 /usr/sbin/crond -n
Nov 18 01:05:24 demo systemd[1]: Started Command Scheduler.
Nov 18 01:05:24 demo crond[4241]: (CRON) STARTUP (1.7.0)
Nov 18 01:05:24 demo crond[4241]: (CRON) INFO (Syslog will be used instead of sendmai>
Nov 18 01:05:24 demo crond[4241]: (CRON) INFO (RANDOM_DELAY will be scaled with facto>
Nov 18 01:05:24 demo crond[4241]: (CRON) INFO (running with inotify support)
[root@demo ~]#
Now we need to configure the system crontab. Copy the text from crontab.txt
. Run EDITOR=kak crontab -e
where EDITOR
is set to your preferred text editor. Paste the contents of crontab.txt
into the editor window. Save and exit.
NOTE: Kakoune can easily paste the contents of another file. From command mode (the default), use |
to open the pipe prompt. Type cat crontab.txt
and press ENTER
. Your crontab file should now match crontab.txt
. Use :wq
to save and exit Kakoune.
Use crontab -l
to verify the crontab saved correctly. My terminal session for this section looks like this:
[root@demo ~]# EDITOR=kak crontab -e
no crontab for root - using an empty one
crontab: installing new crontab
[root@demo ~]# crontab -l
# +--------------- Minute (0-59)
# | +------------ Hour (0-23)
# | | +--------- Day of the month (1-31)
# | | | +------ Month of the year (1-12)
# | | | | +--- Day of the week (0-6)
# | | | | |
55 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"5\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
56 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"4\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
57 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"3\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
58 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"2\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
59 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"1\",\"color\":\"aqua\"},{\"text\":\" minute.\",\"color\":\"yellow\"}]"
0 0 * * * /root/backup | tee -a /root/autobackup.log
[root@demo ~]#
Timezone Configuration (optional)
By default, the provided crontab will display notices and perform an automatic backup at midnight in the system timezone. Most Linux servers use UTC by default. If you would like to align the crontab schedule with your local timezone, follow these steps.
Run timedatectl list-timezones
and locate your timezone from the list. For me, my local timezone is called America/New_York
. Use timedatectl set-timezone my_timezone
where my_timezone
is your local timezone from the list. To set your timezone to America/New_York
, use timedatectl set-timezone America/New_York
. Verify your timezone is set correctly by running date
.
[root@demo ~]# date
Sat Nov 18 01:20:58 AM UTC 2023
[root@demo ~]# timedatectl list-timezones | grep -i new
America/New_York
America/North_Dakota/New_Salem
Canada/Newfoundland
[root@demo ~]# timedatectl set-timezone America/New_York
[root@demo ~]# date
Fri Nov 17 08:21:14 PM EST 2023
[root@demo ~]#
Final Testing
Congratulations! We should be done now. We can verify everything is set up by restarting the container and checking that everything starts up properly.
Exit the container by running exit
. Restart the container using lxc restart demo
. Launch a shell inside the container with lxc shell demo
. Now you can check the output of ps aux
and verify that java and crond are running. The output of crontab -l
should match the crontab settings you had prior to restarting the container. Running ./rcon list
should list the players on the Minecraft server without error. Here is my terminal session for verifying setup.
[root@demo ~]# exit
logout
cait@athena ~> lxc restart demo
cait@athena ~> lxc shell demo
[root@demo ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 21136 12928 ? Ss 20:24 0:00 /sbin/init
root 119 0.0 0.1 131784 18228 ? Ss 20:24 0:00 /usr/lib/systemd/syst
root 127 0.0 0.0 16752 6784 ? Ss 20:24 0:00 /usr/lib/systemd/syst
root 152 0.0 0.0 29844 8832 ? Ss 20:24 0:00 /usr/lib/systemd/syst
systemd+ 170 0.0 0.1 21256 13184 ? Ss 20:24 0:00 /usr/lib/systemd/syst
dbus 178 0.0 0.0 8808 4736 ? Ss 20:24 0:00 /usr/bin/dbus-daemon
root 179 0.0 0.0 17024 7808 ? Ss 20:24 0:00 /usr/lib/systemd/syst
root 180 0.0 0.0 17192 7936 ? Ss 20:24 0:00 /usr/lib/systemd/syst
systemd+ 185 0.0 0.0 18576 9472 ? Ss 20:24 0:00 /usr/lib/systemd/syst
minecra+ 186 0.0 0.0 7512 3712 ? Ss 20:24 0:00 /bin/sh /home/minecra
root 201 0.0 0.0 10892 7296 ? Ss 20:24 0:00 sshd: /usr/bin/sshd -
root 208 0.0 0.0 5496 2176 pts/0 Ss+ 20:24 0:00 /sbin/agetty -o -p --
root 209 0.0 0.0 6664 3200 ? Ss 20:24 0:00 /usr/sbin/crond -n
minecra+ 213 0.0 15.8 9349672 1926716 ? Sl 20:24 2:21 /usr/lib/jvm/java-21-
root 296 0.0 0.0 17140 6528 ? S 20:29 0:00 systemd-userwork
root 297 0.0 0.0 17140 6528 ? S 20:29 0:00 systemd-userwork
root 298 0.0 0.0 17140 6528 ? S 20:29 0:00 systemd-userwork
root 301 0.0 0.0 10412 4224 pts/1 Ss 20:33 0:00 su -l
root 302 0.0 0.0 7776 4480 pts/1 S 20:33 0:00 -bash
root 306 0.0 0.0 10992 4352 pts/1 R+ 20:33 0:00 ps aux
[root@demo ~]# crontab -l
# +--------------- Minute (0-59)
# | +------------ Hour (0-23)
# | | +--------- Day of the month (1-31)
# | | | +------ Month of the year (1-12)
# | | | | +--- Day of the week (0-6)
# | | | | |
55 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"5\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
56 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"4\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
57 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"3\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
58 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"2\",\"color\":\"aqua\"},{\"text\":\" minutes.\",\"color\":\"yellow\"}]"
59 23 * * * /root/rcon "tellraw @a [{\"text\":\"Server will restart in \",\"color\":\"yellow\"},{\"text\":\"1\",\"color\":\"aqua\"},{\"text\":\" minute.\",\"color\":\"yellow\"}]"
0 0 * * * /root/backup | tee -a /root/autobackup.log
[root@demo ~]# ./rcon list
There are 0 of a max of 20 players online:
[root@demo ~]#
Now it is time to play on the server and verify it works. Open Minecraft and click on Multiplayer
. Click Direct Connection
and type the IP address and port of the server. The format is address:port
. For me, this would be 192.168.0.3:25566
Credits
All of this information comes from my own personal experience except for the mcrcon build instructions. However, there are many great resources for learning more.