What you will set up
A Minecraft Java Edition server for you and your friends, without a game panel. It runs from the itzg/minecraft-server image, which downloads the server, keeps it up to date and stops it cleanly, in Podman under a user of its own called minecraft, as in the Podman guide. Players connect straight to port 25565, so Caddy is not needed.
The server type is Paper, which plays like the game Mojang ships but has many performance fixes, so a two-core server handles more players, and it can load plugins. Paper fixes a few vanilla bugs that technical players build on, such as TNT duplication; if you want Mojang's own server instead, set TYPE=VANILLA in step 2.
Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:
- The server started Minecraft 26.3 with Paper in about 30 seconds, and answered a server list ping from outside over IPv4 with its version and player count. Over IPv6 it answered a ping from the server itself.
- A player was added to the whitelist and made an operator from the console, and the console port was not reachable from the internet.
- A world backup was taken while the server ran. A block removed after the backup was back after restoring it.
- An update from Minecraft 26.2 to 26.3 kept the world, and everything came back by itself after a reboot.
- An SRV record for the server's name resolved as expected.
A real player login was not tested: joining needs a Minecraft account bought with a Microsoft account. Only the server list ping, which is what the game's Multiplayer screen shows, was tested.
With nobody online, the server used about 2 GB of memory and a tenth of one core.
Before you start
You need:
- a server with Debian 13, secured as in steps 1 to 4 of the security guide, with ufw switched on;
- Podman, installed as in step 1 of the Podman guide;
- for a name instead of an address, an A record and an AAAA record for
mc.example.compointing at your server.
The examples use mc.example.com, 203.0.113.10 and 2001:db8::10 for the server, and Anna for a player. Replace them throughout.
1. Create the user
As root:
useradd -m -s /bin/bash minecraft
loginctl enable-linger minecraft
machinectl shell minecraft@
Steps 2 and 4 to 9 run as minecraft.
2. Describe the server
mkdir -p ~/.config/containers/systemd ~/minecraft
Create ~/.config/containers/systemd/minecraft.container:
[Unit]
Description=Minecraft server
[Container]
ContainerName=minecraft
Image=docker.io/itzg/minecraft-server:stable
# The files in ~/minecraft belong to the minecraft user, not to a user ID of the container.
UserNS=keep-id:uid=1000,gid=1000
Volume=%h/minecraft:/data
PublishPort=25565:25565
Environment=EULA=TRUE
Environment=TYPE=PAPER
Environment=VERSION=26.3
Environment=MEMORY=4G
Environment=ENABLE_WHITELIST=TRUE
Environment=SKIP_DOWNLOAD_DEFAULTS=TRUE
Environment=TZ=Europe/Stockholm
# Give the server time to save the world when it stops.
StopTimeout=90
AutoUpdate=registry
[Service]
Restart=always
TimeoutStopSec=120
[Install]
WantedBy=default.target
What the lines do:
EULA=TRUEmeans you accept the Minecraft End User License Agreement, which the image requires before it starts a server. Among other things, it says that you may not sell players advantages in the game. The image writeseula=trueto~/minecraft/eula.txt.UserNS=keep-idruns the server as user 1000 inside the container, mapped tominecraftoutside it, so you can read, copy and back up the files in~/minecraftwithout extra steps.VERSIONpins the Minecraft version, so a new release does not arrive before you choose it (step 9). Within that version, the image fetches the newest Paper build each time the server starts.ENABLE_WHITELISTlets in only the players you add in step 6. Anyone else who finds your address cannot join.SKIP_DOWNLOAD_DEFAULTSstops the image from downloading Paper's default settings from a third party's GitHub repository on every start. Paper writes the same defaults by itself.StopTimeout=90gives the server time to save before Podman stops it. The image sendsstopthrough the console, and the world is saved.
MEMORY is the memory Java may use for the game. Java needs about a quarter more on top, and the system needs the rest. For our plans:
| Server memory | MEMORY |
|---|---|
| 8 GB | 4G |
| 16 GB | 10G |
| 24 GB | 16G |
| 32 GB | 22G |
4 GB is plenty for a group of friends. More memory helps with many players, large view distances and mods, not with a small group. Only the 8 GB setting was tested.
Start it:
systemctl --user daemon-reload
systemctl --user start minecraft
systemctl --user enable --now podman-auto-update.timer
The first start downloads the image, Paper and Minecraft, and creates the world, which took a little under two minutes. Follow it with journalctl --user -u minecraft -f, and wait for this line:
[07:59:22 INFO]: Done (29.257s)! For help, type "help"
3. Open the game port
Go back to root with exit, and run:
ufw allow 25565/tcp
ufw applies to rootless Podman like to any other program, so the port stays closed until you allow it. Only port 25565 is open: the server's console, RCON on port 25575, stays inside the container and cannot be reached from the internet or from the server itself.
4. Check that it answers
The image has a tool, mc-monitor, that asks a server for its status like the game's server list does. Switch back with machinectl shell minecraft@, and ask over IPv4 and over IPv6, with your server's addresses:
podman run --rm --network host --entrypoint mc-monitor docker.io/itzg/minecraft-server:stable status --host 203.0.113.10
podman run --rm --network host --entrypoint mc-monitor docker.io/itzg/minecraft-server:stable status --host 2001:db8::10
203.0.113.10:25565 : version=Paper 26.3 online=0 max=20 motd='A Minecraft Server'
2001:db8::10:25565 : version=Paper 26.3 online=0 max=20 motd='A Minecraft Server'
Then, in Minecraft on your own computer, choose Multiplayer, Add Server, and enter mc.example.com or 203.0.113.10 as Server Address. The server list shows the server with its version and player count. This step was not tested in the game itself, only with mc-monitor.
5. Use a name without a port
Players can type mc.example.com as it is: the game uses port 25565 when no port is given. For a shorter name, such as example.com, whose A record already points at your website, add an SRV record at your DNS provider:
| Name | Type | Priority | Weight | Port | Target |
|---|---|---|---|---|---|
_minecraft._tcp.example.com | SRV | 0 | 5 | 25565 | mc.example.com |
The game looks up this record first, and connects to the target and port it gives. The same record lets you run the server on another port without players having to type it. Check it from any computer:
dig +short SRV _minecraft._tcp.example.com
0 5 25565 mc.example.com.
6. Add players and operators
rcon-cli in the container sends commands to the server's console. As minecraft:
podman exec minecraft rcon-cli whitelist add Anna
podman exec minecraft rcon-cli op Anna
Added Anna to the whitelist
Made Anna a server operator
Names are looked up in Mojang's player list, so they must be spelled as the player's Minecraft name. An operator can run every command in the game, such as /whitelist add for their friends, so give it only to people you trust. Other commands you will use:
podman exec minecraft rcon-cli whitelist list
podman exec minecraft rcon-cli whitelist remove Anna
podman exec minecraft rcon-cli list
For a console to type in, run podman exec -it minecraft rcon-cli, and leave it with Ctrl+D. The console password is in ~/minecraft/server.properties, which only minecraft can read.
Paper sends anonymous counts to bStats, a statistics service for plugin authors: a random server ID, the number of players, and the versions of Minecraft, Java and the operating system. To switch it off, set enabled: false in ~/minecraft/plugins/bStats/config.yml, and restart with systemctl --user restart minecraft.
7. Back up the world
Copying the world while the server writes to it can give a broken copy. The server can pause its saving instead, so a backup does not need to stop it. Create ~/bin/backup-world:
#!/bin/sh
# Back up the Minecraft world without stopping the server.
set -e
cd ~/minecraft
mkdir -p ~/backup
podman exec minecraft rcon-cli save-off
trap 'podman exec minecraft rcon-cli save-on' EXIT
podman exec minecraft rcon-cli save-all flush
tar -czf ~/backup/world-$(date +%Y-%m-%d-%H%M).tar.gz world whitelist.json ops.json banned-players.json banned-ips.json
# Keep two weeks of backups.
find ~/backup -name 'world-*.tar.gz' -mtime +14 -delete
save-off pauses saving, save-all flush writes everything in memory to disk, and save-on turns saving back on, also if the copy fails. Players can keep playing meanwhile. Make it runnable, and try it:
mkdir -p ~/bin
chmod +x ~/bin/backup-world
~/bin/backup-world
ls ~/backup
To run it every night, create ~/.config/systemd/user/backup-world.service:
[Unit]
Description=Back up the Minecraft world
[Service]
Type=oneshot
ExecStart=%h/bin/backup-world
And ~/.config/systemd/user/backup-world.timer:
[Unit]
Description=Back up the Minecraft world every night
[Timer]
OnCalendar=*-*-* 04:30
Persistent=true
[Install]
WantedBy=timers.target
systemctl --user daemon-reload
systemctl --user enable --now backup-world.timer
A new, small world took about 0.5 MB, and the backup took a second. The backups sit on the same disk as the world, so copy ~/backup to another machine now and then.
8. Restore a backup
Stop the server, set the current world aside, unpack the backup, and start again. With the name of your backup:
systemctl --user stop minecraft
mv ~/minecraft/world ~/minecraft/world.old
tar -xzf ~/backup/world-2026-09-27-0802.tar.gz -C ~/minecraft
systemctl --user start minecraft
The whitelist and operators come back from the backup too. When the restored world looks right, remove the old one with rm -rf ~/minecraft/world.old.
9. Update
There are two kinds of update:
- The image and Paper.
podman-auto-update.timerchecks every night for a new image, and restarts the server when there is one. Every start also fetches the newest Paper build for your Minecraft version.podman auto-update --dry-runshows whether an image update is waiting. - A new Minecraft version. This is your choice, because players need the same version in their game, and a world opened in a new version cannot go back to an old one. Paper usually has a build some days after Mojang's release: check papermc.io/downloads.
To move to a new version, take a backup, change the VERSION line in ~/.config/containers/systemd/minecraft.container, for example from 26.2 to 26.3, and restart:
~/bin/backup-world
systemctl --user daemon-reload
systemctl --user restart minecraft
The log shows the new version as the server starts:
[08:05:26 INFO]: [bootstrap] Loading Paper 26.3-49-main@0fdc088 (2026-09-26T20:39:03Z) for Minecraft 26.3
Troubleshooting
whitelist add answers "That player does not exist". The name is not a Minecraft player name. Check the spelling with the player.
The server does not show up in the game's server list. Check that ufw status has the rule from step 3, and that ss -tlnp | grep 25565 shows pasta listening. If mc-monitor from step 4 answers but the game does not, the problem is between you and the server, or the name: try the address instead.
podman exec answers that the RCON connection was refused. The server is still starting. Wait for the Done line in journalctl --user -u minecraft.
The log shows warnings about sun.misc.Unsafe. They come from a library in Minecraft itself on the newest Java, and do no harm.
systemctl --user says "Failed to connect to user scope bus". You switched user with su or sudo. Use machinectl shell minecraft@ from step 1.