Host a Minecraft server on a VPS
Run a Paper Minecraft server on an Ubuntu 24.04 VPS: RAM sizing, Java 21, a systemd unit, both firewalls on 25565, safe world backups, and the exact errors.
What you are building
A Minecraft Java Edition server that stays up: Paper on Ubuntu 24.04, running as a dedicated minecraft user under systemd with Restart=on-failure, whitelist on, port 25565 open in both of the firewalls standing between your players and the JVM, and backups taken in a way that does not corrupt the world they exist to protect. The server itself is one jar and one java command; everything that separates a server your friends play on for a year from one that dies the first weekend is around that command, and that is what this guide covers.
Two honest warnings before you spend money. First, Minecraft is memory-hungry and mostly single-threaded: a 1 GB VPS will not run a playable server, and one fast core beats four slow ones. Second, this is Java Edition. Bedrock players — consoles, phones, the Windows Bedrock app — cannot join a Java server; Bedrock needs different server software on UDP 19132, or the Geyser plugin, which is out of scope here.
You need a fresh Ubuntu 24.04 KVM VPS with root or sudo, and about thirty minutes.
Sizing: what a friends server actually needs
RAM is the number that matters, and the honest figures are lower than the modpack forums suggest:
- 2 GB runs vanilla for 2–5 players at a modest view distance. Playable, no headroom.
- 4 GB is the comfortable friends-server size: around ten concurrent players on Paper with a handful of plugins, and enough left over for the OS.
- 8 GB and up is modpack and 20-plus-player territory, where you should also care about single-core CPU speed, because the main game loop runs on one thread.
The world also grows on disk as players explore — a well-travelled survival world reaches several gigabytes — so leave headroom for the world and its backups.
Vanilla or Paper? Paper is a drop-in replacement for the official jar: same worlds, same clients, no client-side mods needed. It rewrites the slowest server paths — chunk loading, entity ticking, hoppers — and adds a plugin API. The only reason to run vanilla is if you need bit-exact vanilla mechanics for technical redstone farms that Paper's optimisations can alter; for everyone else, Paper is more players per gigabyte. Everything below works for both.
Step 1: install Java 21
Minecraft 1.20.5 and every release since (all of 1.21.x) requires Java 21. Ubuntu 24.04 ships it in the default repos, and the headless build skips the desktop libraries a server will never use:
sudo apt update && sudo apt install -y openjdk-21-jre-headless
java -version
Correct result: the first line reads openjdk version "21.0.x". If it reads 17 or 11, another JRE is installed and winning; fix it with sudo update-alternatives --config java and pick the entry containing java-21.
Skipping this check produces the single most-googled Minecraft server error later. With Java 17, the server jar dies instantly with:
Error: LinkageError occurred while loading main class net.minecraft.bundler.Main
java.lang.UnsupportedClassVersionError: net/minecraft/bundler/Main has been
compiled by a more recent version of the Java Runtime (class file version 65.0),
this version of the Java Runtime only recognizes class file versions up to 61.0
The numbers decode the mismatch: class file version 65.0 is Java 21, 61.0 is Java 17, 60.0 is Java 16. Whatever pair you see, the fix is the same — install the newer JRE and re-run update-alternatives.
Step 2: a dedicated user, never root
The server executes plugin code and parses network input from anyone who can reach the port. If that ever goes wrong, a compromise running as root owns your VPS; one running as minecraft owns a game world.
sudo adduser --system --group --home /opt/minecraft minecraft
--system creates a no-password account that cannot log in over SSH, with /opt/minecraft as its home and working directory. Every command that touches the server from here on runs as this user.
Step 3: download the server jar from the official source
Only two places should ever hand you a server jar: the official server download page at minecraft.net for vanilla, the downloads page at papermc.io for Paper. Third-party "download hubs" repackaging server jars are a long-running malware channel — a server jar is arbitrary code you are about to run 24/7 on an internet-facing box.
Copy the download link for the current build from the official page (the URL changes with every version and build, so hardcoding one here would rot), then:
cd /opt/minecraft
sudo -u minecraft wget -O server.jar 'PASTE-THE-COPIED-URL-HERE'
Correct result: a server.jar of roughly 50 MB owned by minecraft, confirmed with ls -lh /opt/minecraft/server.jar.
Step 4: first run, the EULA, and op-ing yourself
Run the server interactively once before touching systemd — the first run has two jobs only a live console does well.
cd /opt/minecraft
sudo -u minecraft java -Xms1G -Xmx1G -jar server.jar nogui
It exits within seconds — Paper spends a few extra moments applying its patches first — and the log ends with the line everyone hits:
[ServerMain/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
This is not an error; it is a license gate. The run wrote eula.txt next to the jar. Read the linked EULA if you have not, then flip the flag:
sudo -u minecraft sed -i 's/eula=false/eula=true/' /opt/minecraft/eula.txt
Run the same java command again. This time it generates the world — a minute or two on first boot — and settles at:
[Server thread/INFO]: Done (9.204s)! For help, type "help"
You now have a live console. Use it for the second job: type op YourMinecraftName (your exact in-game username) and the server writes you into ops.json, so later — when there is no console attached — you can run every admin command from inside the game. Then type stop to save and exit cleanly.
Step 5: server.properties, and why online-mode stays true
The first run also wrote server.properties. Most defaults are fine; these lines are the ones worth deciding deliberately:
online-mode=true
white-list=true
enforce-whitelist=true
view-distance=8
max-players=10
motd=A private server for people I actually know
online-mode=true makes the server verify every joining username against Mojang's session servers, proving the player owns the account. Setting it to false — the "cracked server" toggle — means the server takes any client's word for who they are: anyone can join as any name, including yours, and since ops and whitelists are keyed by name, an impersonator walks in with your operator permissions. The only legitimate reason it is ever false is behind an authenticating proxy like Velocity, which verifies players itself. On a normal server it stays true, full stop.
view-distance is the biggest performance lever in the file — CPU and RAM cost scale roughly with its square. 8 is a sensible VPS default; 10 is the vanilla default and noticeably heavier.
white-list=true plus enforce-whitelist=true closes the server to strangers, which the security section below argues is not optional. Add players with /whitelist add TheirName in-game as an op.
Step 6: memory flags — why -Xms and -Xmx should match
The JVM allocates its heap between -Xms (starting size) and -Xmx (ceiling). On a dedicated game server, set them to the same value: the heap will reach the ceiling anyway, and growing it in steps just adds garbage-collector churn during play. On a 4 GB VPS the right pair is:
/usr/bin/java -Xms3G -Xmx3G -jar server.jar nogui
-Xmx is not the total memory the process uses. The JVM adds off-heap overhead — thread stacks, JIT caches, direct buffers — of roughly half a gigabyte or more, and Ubuntu itself needs room. The working rule: -Xmx at most VPS RAM minus 1 GB. Overshoot and you get one of two failures. If the allocation fails up front, the server refuses to start with:
Error: Could not reserve enough space for object heap
That one is honest and immediate. The nastier version starts fine and dies hours later, when the heap fills under load and the kernel's OOM killer picks the biggest process on the box — the JVM. Nothing appears in the server log; the evidence is in sudo dmesg | grep -i oom, a line like Out of memory: Killed process 1234 (java). If your service restarts "randomly", check there first, and lower -Xmx.
For Paper, the project documents Aikar's flags — a tuned G1GC set (-XX:+UseG1GC -XX:MaxGCPauseMillis=200 and a dozen friends) that smooths garbage-collection pauses on larger heaps. Generate the full line from the Paper documentation; on a 2–4 GB heap the gain is modest but the flags are harmless.
Step 7: a systemd unit with Restart=on-failure
A server started by hand dies with your SSH session and stays down after a reboot. Write /etc/systemd/system/minecraft.service:
[Unit]
Description=Minecraft server (Paper)
After=network-online.target
Wants=network-online.target
[Service]
User=minecraft
Group=minecraft
WorkingDirectory=/opt/minecraft
ExecStart=/usr/bin/java -Xms3G -Xmx3G -jar server.jar nogui
Restart=on-failure
RestartSec=10
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now minecraft
systemctl status minecraft
journalctl -u minecraft -f
Correct result: status shows active (running), and the journal — which now carries the server console output — ends at the Done line again.
Two deliberate choices in that unit. Restart=on-failure brings the server back after a crash but not after a clean stop, so maintenance stays possible. And TimeoutStopSec=120 matters more than it looks: systemctl stop sends SIGTERM, the Minecraft server catches it and saves every world before exiting, and on a big world that save takes time. Systemd's default timeout can SIGKILL the JVM mid-save, which is exactly how region files corrupt. Give it two minutes.
If the service flaps and the journal shows:
[Server thread/WARN]: **** FAILED TO BIND TO PORT!
[Server thread/WARN]: The exception was: io.netty.channel.unix.Errors$NativeIoException: bind(..) failed: Address already in use
[Server thread/WARN]: Perhaps a server is already running on that port?
then something already holds 25565 — almost always your earlier foreground run still alive in another SSH session. Find it with sudo ss -tlnp | grep 25565 and stop that copy; never run two servers on one port.
Step 8: open 25565 on both firewalls
Minecraft Java uses TCP 25565 only (UDP is just the optional query protocol, off by default). On the VPS:
sudo ufw allow OpenSSH
sudo ufw allow 25565/tcp
sudo ufw enable
That is firewall one. Most VPS providers run a second, network-level firewall in the control panel, and it is the one people forget. Open TCP 25565 there too. The symptom of getting only one of the two is maddeningly unspecific: the client hangs on "Connecting to the server..." and fails with Connection timed out: no further information — no refusal, no server error, nothing in the journal, because the packets never arrived. Verify from a machine outside the VPS with nc -vz your.server.ip 25565; succeeded means both firewalls are open, a hang means at least one is not.
While you are in ufw, the same box is running SSH on a public IP, and the brute-force log noise starts within hours — set up Fail2ban for SSH on Ubuntu 24.04 before you forget.
Now connect: Multiplayer, Direct Connection, the server IP. Because you op'd yourself in step 4, in-game commands work immediately — /whitelist add FriendName for each player, /gamemode creative if that is the plan.
Step 9: whitelist, because the internet finds port 25565
Leaving a Minecraft server open to the internet is not a theoretical risk: mass scanners sweep the whole IPv4 space for port 25565 continuously — hobbyist griefing projects have mapped hundreds of thousands of open servers this way — and an unwhitelisted server gets uninvited visitors within days, sometimes hours. The whitelist from step 5 is the fix: online-mode=true proves identity, the whitelist restricts entry to identities you chose. That pair is the whole security model, and it is enough for a friends server.
For console access without the game — scripted backups, a cron job — enable RCON in server.properties (enable-rcon=true, a strong rcon.password, port 25575) and build the small mcrcon client from its GitHub source on the VPS itself; it is not in Ubuntu's repos. Do not open 25575 in any firewall: RCON sends the password in cleartext, so it stays loopback-only.
Step 10: backups that do not corrupt the world
The one rule: never copy a world directory while the server is writing it. Region files are rewritten continuously during play; a tar that races a write produces an archive with half-written chunks, and when you eventually restore it — the one moment a backup must work — the log fills with chunk-loading errors and the terrain has holes. Two disciplines avoid this.
Stop-copy-start is the simple one, at the cost of a minute of downtime:
sudo systemctl stop minecraft
sudo mkdir -p /opt/backups
sudo tar czf /opt/backups/world-$(date +%F-%H%M).tgz \
-C /opt/minecraft world world_nether world_the_end
sudo systemctl start minecraft
(Paper splits the dimensions into world, world_nether, and world_the_end; a vanilla server keeps everything in world, so drop the other two there.)
Save-off backs up live: over RCON, issue save-off (stop the autosaver), then save-all flush (force everything to disk and wait), take the tar, then save-on. This is the version to put in cron at 5 a.m.
Either way, a backup sitting on the same VPS as the server is not a backup — scp or rclone the archives off the box, and keep more than one. And since you will not be staring at the console when something breaks, put an external check on the port: Uptime Kuma on another box doing a TCP check against 25565 tells you the server is down before your players do.
Plugins and upgrades
On Paper, a plugin is a jar dropped into /opt/minecraft/plugins/ followed by a restart — that is the whole install. Get plugins only from their official project pages (Hangar, Modrinth, SpigotMC), for the same reason as the server jar: each one is arbitrary code running as the minecraft user.
Upgrading the server is: stop, back up, replace server.jar with the build for the new Minecraft version, start. Two cautions. World upgrades are one-way — a world opened by a newer version will not open in an older one, which is another job for the backups. And plugins often lag a new Minecraft release by days or weeks, so check compatibility before chasing a release on day one.
If you would rather manage all of this as a container — version pinning, EULA acceptance, and memory flags as environment variables — the widely used itzg/minecraft-server image runs the same Paper server under Docker Compose on your VPS with the identical firewall and backup discipline.
FAQ
How much RAM does a Minecraft server need?
2 GB runs vanilla for 2–5 players; 4 GB is comfortable for a roughly ten-player friends server on Paper with a few plugins; modpacks and 20-plus players want 8 GB or more. Whatever the VPS size, set -Xmx to at most total RAM minus 1 GB — the JVM uses memory beyond the heap, and overshooting ends in Error: Could not reserve enough space for object heap or a silent OOM kill.
Why can't my friends connect to my Minecraft server?
A client hang ending in Connection timed out: no further information almost always means a firewall, and there are two: ufw on the VPS and your provider's network firewall in the control panel. TCP 25565 must be open in both. Confirm the server is actually listening with sudo ss -tlnp | grep 25565 on the box, then test from outside with nc -vz your.server.ip 25565.
Should I set online-mode=false in server.properties?
No. With online-mode=false the server skips Mojang authentication entirely, so anyone can join under any username — including an op's name, inheriting their permissions — and skins and stable UUIDs break. The only legitimate use is behind an authenticating proxy like Velocity that verifies players itself. For a normal server it stays true.
Is Paper better than the vanilla Minecraft server?
For almost everyone, yes: Paper is a drop-in jar that loads vanilla worlds, accepts unmodified clients, ticks entities and loads chunks far more efficiently, and adds the plugin ecosystem. The exception is technical-redstone communities that need bit-exact vanilla behaviour, which some Paper optimisations alter. On a small VPS, Paper's efficiency is effectively free extra RAM.
How do I back up a Minecraft world without corrupting it?
Never archive the world directory while the server is writing to it — a copy racing a region-file write produces corrupted chunks you only discover at restore time. Either stop the server, tar the world directories, and start it again, or keep it live with save-off, save-all flush, tar, save-on over RCON. Then move the archive off the VPS.