A dedicated Minecraft Java Edition server gives you and your friends a world that stays online when you log off, with your own rules, whitelist and settings. In this tutorial you will install the official server from Mojang on Ubuntu 24.04, run it under a dedicated user as a systemd service, open its port in the firewall, manage it from the command line with RCON, and schedule automatic world backups.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 vCPUs and 4 GB of RAM. Plan on about 1 GB of server heap per 5-10 active players on a vanilla world, plus 1 GB for the operating system.
- A non-root user with
sudoprivileges. - A Minecraft Java Edition account to connect and test.
- Optionally, a domain name with an A record pointing to your server's IP address, so players can connect with a name instead of an IP.
Step 1 - Installing Java and tools
The Minecraft server is a Java application, and each Minecraft release requires a minimum Java version. Install jq first so you can read that requirement from Mojang's version metadata:
sudo apt update
sudo apt install curl jq
Mojang publishes a manifest listing every version. Get the latest release and the URL of its metadata:
MC_VERSION=$(curl -fsSL https://piston-meta.mojang.com/mc/game/version_manifest_v2.json | jq -r '.latest.release')
MC_META=$(curl -fsSL https://piston-meta.mojang.com/mc/game/version_manifest_v2.json | jq -r --arg v "$MC_VERSION" '.versions[] | select(.id == $v) | .url')
echo "$MC_VERSION"
curl -fsSL "$MC_META" | jq '.javaVersion.majorVersion'
1.21.9
21
Your version numbers may be newer. The second line is the Java major version you need. Install the matching headless OpenJDK runtime from Ubuntu's repositories, replacing 21 if the output showed a different number:
sudo apt install openjdk-21-jre-headless
java -version
openjdk version "21.0.8" 2025-07-15
OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu124.04.1)
OpenJDK 64-Bit Server VM (build 21.0.8+9-Ubuntu-0ubuntu124.04.1, mixed mode, sharing)
Step 2 - Creating a dedicated user
Running the server as its own system user keeps it away from your account and the rest of the system if a plugin or exploit ever compromises it. Create a user with its home in /opt/minecraft and no login shell:
sudo useradd --system --create-home --home-dir /opt/minecraft --shell /usr/sbin/nologin minecraft
sudo mkdir -p /opt/minecraft/server /opt/minecraft/backups
sudo chown -R minecraft:minecraft /opt/minecraft
Step 3 - Downloading the server
Read the download URL of the server JAR from the version metadata you found in Step 1 and download it as the minecraft user. If you opened a new shell since then, run the two MC_VERSION and MC_META lines again first:
SERVER_URL=$(curl -fsSL "$MC_META" | jq -r '.downloads.server.url')
sudo -u minecraft curl -fsSL -o /opt/minecraft/server/server.jar "$SERVER_URL"
sudo ls -lh /opt/minecraft/server/server.jar
-rw-r--r-- 1 minecraft minecraft 57M Sep 25 10:12 /opt/minecraft/server/server.jar
Run it once to generate the configuration files. It stops immediately because the EULA has not been accepted:
sudo -u minecraft sh -c 'cd /opt/minecraft/server && java -Xmx1G -jar server.jar --nogui'
[ServerMain/WARN]: Failed to load eula.txt
[ServerMain/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
Read the Minecraft EULA at https://aka.ms/MinecraftEULA. If you agree, accept it:
sudo -u minecraft sed -i 's/^eula=false/eula=true/' /opt/minecraft/server/eula.txt
Step 4 - Configuring server.properties
The main settings live in /opt/minecraft/server/server.properties. Open it:
sudo -u minecraft nano /opt/minecraft/server/server.properties
Change these keys and leave the rest at their defaults:
motd=My CubePath Minecraft Server
max-players=20
difficulty=normal
gamemode=survival
view-distance=10
simulation-distance=8
online-mode=true
white-list=true
enforce-whitelist=true
enable-rcon=true
rcon.port=25575
rcon.password=your_strong_password
server-port=25565
What they do:
online-mode=truechecks every player against Mojang's authentication servers. Keep it on; turning it off lets anyone join with any name.white-listandenforce-whitelistonly admit players you add, and kick anyone removed from the list.view-distanceandsimulation-distanceare the biggest levers on CPU and RAM use. Lower them if the server lags.enable-rconturns on the remote console you will use to run commands. Replaceyour_strong_passwordwith a long random string, for example fromopenssl rand -base64 24. You will not open port 25575 in the firewall, so RCON stays reachable only from the server itself.
Because the file now contains a password, restrict it to the minecraft user:
sudo chmod 600 /opt/minecraft/server/server.properties
Step 5 - Creating the systemd service
A systemd unit starts the server at boot, restarts it if it crashes and stops it cleanly on shutdown. Create the unit file:
sudo nano /etc/systemd/system/minecraft.service
[Unit]
Description=Minecraft Java Edition server
After=network-online.target
Wants=network-online.target
[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/opt/minecraft/server
ExecStart=/usr/bin/java -Xms2G -Xmx2G -jar server.jar --nogui
SuccessExitStatus=0 143
Restart=on-failure
RestartSec=10
TimeoutStopSec=90
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
-Xms2G -Xmx2G gives the server a fixed 2 GB heap; set both to the same value, and leave at least 1 GB of RAM for the operating system. On a 4 GB server, 2-3 GB is right; on 8 GB, use 5-6 GB. When systemd stops the service it sends SIGTERM, the server saves the world and exits with status 143, which SuccessExitStatus treats as a clean stop.
Load the unit and start the server:
sudo systemctl daemon-reload
sudo systemctl enable --now minecraft
The first start generates the world and takes a minute. Follow the log:
sudo journalctl -u minecraft -f
[Server thread/INFO]: Preparing level "world"
[Server thread/INFO]: Preparing spawn area: 100%
[Server thread/INFO]: Done (18.412s)! For help, type "help"
[Server thread/INFO]: Starting remote control listener
[RCON Listener #1/INFO]: RCON running on 0.0.0.0:25575
Press Ctrl+C to stop following. The Done line means the server is ready.
Step 6 - Opening the firewall
Players connect over TCP port 25565. Allow it in UFW, making sure SSH stays allowed before you enable the firewall:
sudo ufw allow OpenSSH
sudo ufw allow 25565/tcp
sudo ufw enable
sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
25565/tcp ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
25565/tcp (v6) ALLOW Anywhere (v6)
Port 25575 (RCON) is not listed, so it is blocked from the internet.
Step 7 - Managing the server with RCON
The server runs in the background, so you need RCON to type console commands. Build mcrcon, a small command line RCON client:
sudo apt install git build-essential
git clone https://github.com/Tiiffi/mcrcon.git
cd mcrcon
make
sudo make install
Store the RCON password in an environment variable for the current shell, so it does not end up on the command line of every call:
read -rs MCRCON_PASS && export MCRCON_PASS
Type the password and press Enter. Now add yourself to the whitelist and make yourself an operator, replacing your_player_name with your Minecraft username:
mcrcon -H 127.0.0.1 -P 25575 "whitelist add your_player_name" "op your_player_name"
Added your_player_name to the whitelist
Made your_player_name a server operator
Run mcrcon -H 127.0.0.1 -P 25575 -t for an interactive console, and type Q to leave it. Useful commands are list (online players), say <message>, whitelist remove <name> and save-all.
Now open Minecraft Java Edition, choose Multiplayer, Add Server, and enter your_server_ip or your_domain. The server should appear with your MOTD and you can join.
Step 8 - Scheduling automatic backups
A world copy taken while the server writes to it can be corrupted. The backup script below pauses autosaving, forces a full save, archives the world and turns autosaving back on. It keeps the last 14 archives.
Store the RCON password in a file only root can read:
sudo install -m 600 /dev/null /etc/minecraft-rcon.env
sudo nano /etc/minecraft-rcon.env
MCRCON_PASS=your_strong_password
Create the script:
sudo nano /usr/local/bin/minecraft-backup
#!/usr/bin/env bash
set -euo pipefail
SERVER_DIR="/opt/minecraft/server"
BACKUP_DIR="/opt/minecraft/backups"
KEEP=14
RCON=(/usr/local/bin/mcrcon -H 127.0.0.1 -P 25575)
restore_autosave() { "${RCON[@]}" "save-on" >/dev/null || true; }
trap restore_autosave EXIT
"${RCON[@]}" "save-off" "save-all flush" >/dev/null
archive="${BACKUP_DIR}/world-$(date +%Y%m%d-%H%M%S).tar.gz"
tar -czf "$archive" -C "$SERVER_DIR" world
find "$BACKUP_DIR" -maxdepth 1 -name 'world-*.tar.gz' -printf '%T@ %p\n' \
| sort -rn | tail -n +"$((KEEP + 1))" | cut -d' ' -f2- | xargs -r rm --
echo "Backup written to $archive"
Make it executable:
sudo chmod 755 /usr/local/bin/minecraft-backup
Create a systemd service that runs the script as the minecraft user with the password file:
sudo nano /etc/systemd/system/minecraft-backup.service
[Unit]
Description=Back up the Minecraft world
After=minecraft.service
[Service]
Type=oneshot
User=minecraft
EnvironmentFile=/etc/minecraft-rcon.env
ExecStart=/usr/local/bin/minecraft-backup
And a timer that runs it every day at 04:30:
sudo nano /etc/systemd/system/minecraft-backup.timer
[Unit]
Description=Daily Minecraft world backup
[Timer]
OnCalendar=*-*-* 04:30:00
Persistent=true
[Install]
WantedBy=timers.target
Enable the timer and run one backup now to test it:
sudo systemctl daemon-reload
sudo systemctl enable --now minecraft-backup.timer
sudo systemctl start minecraft-backup.service
sudo journalctl -u minecraft-backup --no-pager -n 5
sudo ls -lh /opt/minecraft/backups
Backup written to /opt/minecraft/backups/world-20260925-104512.tar.gz
-rw-r--r-- 1 minecraft minecraft 38M Sep 25 10:45 world-20260925-104512.tar.gz
Backups on the same disk do not protect against losing the server. Copy /opt/minecraft/backups to another location regularly, for example with rsync or rclone to object storage.
To restore, stop the server, move the current world directory aside, extract an archive into /opt/minecraft/server with sudo -u minecraft tar -xzf <archive> -C /opt/minecraft/server, and start the server again.
Step 9 - Updating the server
When a new Minecraft version is released, players' clients update automatically and can no longer join an older server. To update, take a backup, stop the server, replace the JAR and start it again:
sudo systemctl start minecraft-backup.service
sudo systemctl stop minecraft
Repeat the commands from Step 1 to read the new version and its Java requirement (install a newer OpenJDK package if it changed), then download the new JAR as in Step 3 and start the server:
sudo systemctl start minecraft
sudo journalctl -u minecraft -f
The server converts the world to the new format on first start. That conversion cannot be undone, which is why the backup comes first.
Troubleshooting
The service fails with UnsupportedClassVersionError. The installed Java is older than the version the server needs. Check .javaVersion.majorVersion as in Step 1 and install the matching openjdk-NN-jre-headless package.
The server is killed and the journal shows Out of memory or OOM. -Xmx plus the operating system needs more RAM than the server has. Lower -Xmx in the unit file, run sudo systemctl daemon-reload and restart.
Players see Connection timed out. Confirm the server is listening with sudo ss -tlnp | grep 25565 and that UFW allows the port. If you use a domain, check that its A record points to the server.
Players see You are not white-listed on this server. Add them with whitelist add <name> through RCON. The name must match their Java Edition username exactly.
mcrcon returns Connection failed or Authentication failed. Check that enable-rcon=true is set, that the server has finished starting, and that MCRCON_PASS matches rcon.password.
The server lags with many players. Lower view-distance and simulation-distance, and check CPU with top. The server's main game loop runs on a single thread, so CPU speed per core matters more than core count.
Conclusion
Your Minecraft Java Edition server now runs as a dedicated user under systemd, starts at boot, admits only whitelisted players, can be managed over RCON from the server itself, and backs up its world every day. From here you can switch to Paper for better performance and plugin support by replacing server.jar with a Paper build, pre-generate the world to reduce lag while players explore, and copy your backups to off-site storage.
