{"id":409,"date":"2026-08-31T13:04:54","date_gmt":"2026-08-31T13:04:54","guid":{"rendered":"https:\/\/www.vps.tc\/blog\/?p=409"},"modified":"2026-08-31T09:31:15","modified_gmt":"2026-08-31T09:31:15","slug":"how-to-set-up-minecraft-server-on-vps","status":"publish","type":"post","link":"https:\/\/www.vps.tc\/blog\/en\/how-to-set-up-minecraft-server-on-vps\/","title":{"rendered":"How to Set Up a Minecraft Server on a VPS"},"content":{"rendered":"<div class=\"aiw-toc\" style=\"border:1px solid #dbe3ea;border-radius:8px;padding:16px 20px;margin:0 0 28px\"><strong>Table of Contents<\/strong><\/p>\n<ol style=\"margin:10px 0 0;padding-left:22px\">\n<li><a href=\"#before-you-put-minecraft-on-a-vps\">Before you put Minecraft on a VPS<\/a><\/li>\n<li><a href=\"#cpu-and-ram-are-only-starting-points\">CPU and RAM are only starting points<\/a><\/li>\n<li><a href=\"#prepare-the-vps-without-doing-everything-as-root\">Prepare the VPS without doing everything as root<\/a><\/li>\n<li><a href=\"#install-a-java-version-that-fits-the-server\">Install a Java version that fits the server<\/a><\/li>\n<li><a href=\"#download-and-start-paper\">Download and start Paper<\/a><\/li>\n<li><a href=\"#heap-size-view-distance-and-actual-performance\">Heap size, view distance, and actual performance<\/a><\/li>\n<li><a href=\"#let-systemd-own-the-process\">Let systemd own the process<\/a><\/li>\n<li><a href=\"#open-only-the-required-network-ports\">Open only the required network ports<\/a><\/li>\n<li><a href=\"#give-players-a-hostname\">Give players a hostname<\/a><\/li>\n<li><a href=\"#whitelist-first-plugins-second\">Whitelist first, plugins second<\/a><\/li>\n<li><a href=\"#a-running-world-is-not-a-backup\">A running world is not a backup<\/a><\/li>\n<li><a href=\"#monitor-the-game-not-just-the-port\">Monitor the game, not just the port<\/a><\/li>\n<li><a href=\"#check-these-before-sharing-the-address\">Check these before sharing the address<\/a><\/li>\n<li><a href=\"#questions-i-hear-often\">Questions I hear often<\/a><\/li>\n<\/ol>\n<\/div>\n<h2 id=\"before-you-put-minecraft-on-a-vps\">Before you put Minecraft on a VPS<\/h2>\n<p>The first few players rarely expose a Minecraft server&#8217;s limits. Five minutes later, three of them may be exploring different chunks, generating new terrain while a plugin saves data and the operating system waits on storage. That is when a VPS that looked comfortably sized starts feeling very small.<\/p>\n<p>I plan the resources before downloading the server JAR. The examples below use Ubuntu Server 24.04 LTS, Minecraft Java Edition 1.21.x, and Paper. Paper is a sensible choice when you want plugin support and useful performance settings without straying too far from vanilla Minecraft. If you choose vanilla, the JAR name and start command change; the user, firewall, and backup work still apply.<\/p>\n<h2 id=\"cpu-and-ram-are-only-starting-points\">CPU and RAM are only starting points<\/h2>\n<p>Minecraft is sensitive to single-thread CPU performance. One world&#8217;s main game loop may not benefit much from a provider&#8217;s &#8220;8 vCPU&#8221; label, especially when those virtual CPUs are shared. I check the provider&#8217;s CPU policy, processor generation, and storage type instead of counting cores alone.<\/p>\n<p>My own <em><a href=\"https:\/\/www.vps.tc\/blog\/en\/10-essential-steps-to-secure-and-harden-your-linux-server\/\">10 Essential Steps to Secure and Harden Your Linux Server<\/a><\/em> checklist starts with choosing an environment I can understand and monitor.<\/p>\n<table>\n<thead>\n<tr>\n<th>Players and usage<\/th>\n<th>Suggested RAM<\/th>\n<th>Notes<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>2-5 players, few plugins<\/td>\n<td>2 GB<\/td>\n<td>Possible with a new world and a low view distance<\/td>\n<\/tr>\n<tr>\n<td>5-10 players, Paper<\/td>\n<td>4 GB<\/td>\n<td>A reasonable starting point for a small friends-only server<\/td>\n<\/tr>\n<tr>\n<td>10-20 players, plugins<\/td>\n<td>6-8 GB<\/td>\n<td>Watch world generation and plugin behavior closely<\/td>\n<\/tr>\n<tr>\n<td>Modpack or busy world<\/td>\n<td>8 GB and above<\/td>\n<td>Account for the mods&#8217; memory requirements separately<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>These are starting points, not promises. World generation, view distance, mob counts, and plugin behavior can matter as much as player count. If the VPS runs other services, physical RAM is not all available to Minecraft.<\/p>\n<p>On a 4 GB VPS, I normally leave roughly 700 MB to 1 GB for the operating system, SSH, monitoring, and filesystem cache. Setting the Java heap to <code>-Xmx3G<\/code> may look efficient, but allocating nearly everything to Java can push the machine into swap. If you need swap, see <em><a href=\"https:\/\/www.vps.tc\/blog\/en\/how-to-create-swap-space-on-a-vps-for-linux-memory-management\/\">How to Create Swap Space on a VPS for Linux Memory Management<\/a><\/em>. Swap is not RAM.<\/p>\n<h2 id=\"prepare-the-vps-without-doing-everything-as-root\">Prepare the VPS without doing everything as root<\/h2>\n<p>Logging in as root and running every command directly is convenient. It also makes a bad plugin or an incorrect command much more dangerous. I create a separate account for the Minecraft process and keep administrative work separate from game data.<\/p>\n<pre><code>ssh root@SERVER_IP\napt update &amp;&amp; apt full-upgrade -y\napt install -y sudo\nadduser --disabled-password --gecos \"\" minecraft\nusermod -aG sudo minecraft\nhostnamectl set-hostname mc-01<\/code><\/pre>\n<p>Replace <code>mc-01<\/code> with your own hostname. My production prompts are red, and my test machines have distinct hostnames. I once got within one keystroke of running <code>rm -rf<\/code> on the wrong server; the hostname in the terminal stopped me. Since then, checking the hostname has been a pre-flight check, not a vague good habit.<\/p>\n<p>If you use SSH keys, copy yours to the new account:<\/p>\n<pre><code>ssh-copy-id minecraft@SERVER_IP<\/code><\/pre>\n<p>Do not disable root SSH access until you have tested a separate session as <code>minecraft<\/code>. After that test succeeds, restrict root and password authentication in <code>\/etc\/ssh\/sshd_config<\/code>:<\/p>\n<pre><code>PermitRootLogin no\nPasswordAuthentication no<\/code><\/pre>\n<p>Validate the configuration before applying it:<\/p>\n<pre><code>sshd -t &amp;&amp; systemctl reload ssh<\/code><\/pre>\n<p>Changing the SSH port is not security by itself. I prefer fail2ban and nftables, while still reading the logs to see what actually reaches the server. The Minecraft port will be public, so do not skip the checks in <em>10 Essential Steps to Secure and Harden Your Linux Server<\/em>.<\/p>\n<h2 id=\"install-a-java-version-that-fits-the-server\">Install a Java version that fits the server<\/h2>\n<p>The Java version must match the Minecraft and Paper versions. For Minecraft 1.20.5 and later, Java 21 is the normal starting point for the Paper releases I use. Check the release notes for your exact build before installing anything.<\/p>\n<pre><code>apt install -y openjdk-21-jre-headless curl wget unzip ufw\njava -version<\/code><\/pre>\n<p>The output should show Java 21, for example <code>openjdk version \"21.0.x\"<\/code>. Minecraft 1.20.4 and older commonly use Java 17, but the server and Paper documentation should decide the final version.<\/p>\n<p>The JRE is enough to run the server. There is no need for a full JDK unless you have another task that actually requires one.<\/p>\n<h2 id=\"download-and-start-paper\">Download and start Paper<\/h2>\n<p>Switch to the Minecraft account and create a dedicated working directory. This layout makes version changes and backups less confusing.<\/p>\n<pre><code>su - minecraft\nmkdir -p \/home\/minecraft\/server\ncd \/home\/minecraft\/server\nwget -O paper.jar 'PAPER_DOWNLOAD_URL'\nls -lh paper.jar<\/code><\/pre>\n<p>Replace <code>PAPER_DOWNLOAD_URL<\/code> with the download link for your selected Minecraft version from Paper&#8217;s official download page. I am not hard-coding a URL because Paper releases change. Do not run a random JAR because its filename looks familiar; compare its SHA-256 checksum with the published value.<\/p>\n<pre><code>sha256sum paper.jar<\/code><\/pre>\n<p>If the value does not match, stop there. This check takes seconds.<\/p>\n<p>The first start creates the EULA file and normally exits immediately:<\/p>\n<pre><code>cd \/home\/minecraft\/server\njava -Xms2G -Xmx3G -jar paper.jar --nogui<\/code><\/pre>\n<p>Read the EULA. If you accept it, update the value:<\/p>\n<pre><code>sed -i 's\/^eula=false$\/eula=true\/' eula.txt<\/code><\/pre>\n<p>Start the server again and wait for the world to generate. Initial map generation can produce a short burst of CPU and disk activity, so do not judge the VPS from that first minute alone.<\/p>\n<h2 id=\"heap-size-view-distance-and-actual-performance\">Heap size, view distance, and actual performance<\/h2>\n<p><code>-Xms<\/code> sets the initial Java heap, while <code>-Xmx<\/code> sets its upper limit. They do not have to be identical on a small VPS. Leave room for the operating system.<\/p>\n<p>On a 4 GB machine, I usually begin with <code>-Xms2G -Xmx3G<\/code>. On an 8 GB machine, I might allocate 5-6 GB, depending on the plugins and player count. More heap does not fix every performance problem. If the main CPU thread cannot keep up with chunk generation, adding another 4 GB will not repair the TPS.<\/p>\n<p>These are conservative starting values in <code>server.properties<\/code>:<\/p>\n<pre><code>view-distance=8\nsimulation-distance=6\nonline-mode=true\nmotd=Friends' Minecraft Server\nmax-players=10<\/code><\/pre>\n<p><code>view-distance<\/code> controls how many chunks are sent to a player. <code>simulation-distance<\/code> controls the area where entities, redstone, and similar mechanics remain active. Raising both without a reason increases CPU work, especially on a small VPS.<\/p>\n<p>When performance is poor, measure before changing settings. Check Paper&#8217;s timings or profiling tools alongside CPU load, memory pressure, disk activity, and player movement. I have seen &#8220;the server needs more RAM&#8221; become the diagnosis simply because someone looked at memory usage and stopped there.<\/p>\n<h2 id=\"let-systemd-own-the-process\">Let systemd own the process<\/h2>\n<p>Leaving an SSH session open with <code>java<\/code> running in the terminal is fine for testing, not for a live server. <code>tmux<\/code> can preserve a session when SSH drops (I moved away from <code>screen<\/code> years ago), but systemd gives the service a proper boot sequence, restart policy, and log location.<\/p>\n<p>Create the service file as root:<\/p>\n<pre><code>exit\nvim \/etc\/systemd\/system\/minecraft.service<\/code><\/pre>\n<pre><code>[Unit]\nDescription=Minecraft Paper Server\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nUser=minecraft\nGroup=minecraft\nWorkingDirectory=\/home\/minecraft\/server\nExecStart=\/usr\/bin\/java -Xms2G -Xmx3G -jar paper.jar --nogui\nRestart=on-failure\nRestartSec=10\nTimeoutStopSec=60\n\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n<p><code>User=minecraft<\/code> matters. The server does not run as root, so a vulnerability in Paper or a plugin does not automatically grant unrestricted access to the operating system. It is not a complete security boundary, but removing unnecessary privileges still helps.<\/p>\n<pre><code>systemctl daemon-reload\nsystemctl enable --now minecraft\nsystemctl status minecraft --no-pager<\/code><\/pre>\n<p>You should see <code>active (running)<\/code>. If it fails, do not restart it repeatedly. Read the logs:<\/p>\n<pre><code>journalctl -u minecraft -n 100 --no-pager\njournalctl -u minecraft -f<\/code><\/pre>\n<p>While repairing a server that failed to boot after a kernel update, I had to work through rescue mode and <code>chroot<\/code>. That incident reminded me to take a snapshot before a significant change. I do that before Java or plugin updates now.<\/p>\n<h2 id=\"open-only-the-required-network-ports\">Open only the required network ports<\/h2>\n<p>Minecraft Java Edition uses TCP <code>25565<\/code> by default. Open only the traffic you need with UFW:<\/p>\n<pre><code>ufw default deny incoming\nufw default allow outgoing\nufw allow OpenSSH\nufw allow 25565\/tcp\nufw enable\nufw status verbose<\/code><\/pre>\n<p>If SSH uses a non-standard port, replace <code>OpenSSH<\/code> with the actual port rule. Before enabling the firewall, verify a second SSH connection without closing the current one. Otherwise, you can lock yourself out of the VPS.<\/p>\n<p>Bedrock players need more than a Java server. A bridge such as GeyserMC and usually a UDP port are required. If you are serving Java Edition only, do not open an unnecessary UDP rule.<\/p>\n<h2 id=\"give-players-a-hostname\">Give players a hostname<\/h2>\n<p>An address such as <code>mc.example.com<\/code> is easier to share than an IP address. Add an A record pointing that name to the VPS IPv4 address. If you use IPv6, check that the AAAA record points to the intended server; a wrong AAAA record can affect only some players, which makes the problem particularly annoying.<\/p>\n<pre><code>dig +short A mc.example.com\ndig +short AAAA mc.example.com<\/code><\/pre>\n<p>When DNS is involved, I go to the terminal before the control panel. If <code>dig +short<\/code> does not return the address you expect, changing Minecraft settings will not help. If the server uses a non-default port, players must connect with an address such as <code>mc.example.com:25570<\/code>. An SRV record can hide the port, but an A record is simpler for a first setup.<\/p>\n<h2 id=\"whitelist-first-plugins-second\">Whitelist first, plugins second<\/h2>\n<p>When the server is running, use its console to grant yourself operator access and enable the whitelist:<\/p>\n<pre><code>op PLAYER_NAME\nwhitelist on\nwhitelist add PLAYER_NAME\nsave-all<\/code><\/pre>\n<p>A whitelist prevents unknown players from entering a private world. Give OP privileges only to accounts that need them. Plugin administrators are not root, either; a malicious or outdated plugin can still damage every file the Minecraft user can access. Do not test an unknown JAR on a live server.<\/p>\n<p>Paper plugins go in <code>\/home\/minecraft\/server\/plugins<\/code>:<\/p>\n<pre><code>install -d -o minecraft -g minecraft \/home\/minecraft\/server\/plugins\nsystemctl stop minecraft\ncp plugin.jar \/home\/minecraft\/server\/plugins\/\nchown minecraft:minecraft \/home\/minecraft\/server\/plugins\/plugin.jar\nsystemctl start minecraft<\/code><\/pre>\n<p>Run these commands with root privileges. Add plugins one at a time so you know which change caused a problem. If you want container-based management, see <em><a href=\"https:\/\/www.vps.tc\/blog\/en\/docker-installation-on-vps-first-container\/\">How to Install Docker on a VPS and Run Your First Container<\/a><\/em>. Docker does not remove the need to understand Java, storage, or backups.<\/p>\n<h2 id=\"a-running-world-is-not-a-backup\">A running world is not a backup<\/h2>\n<p>A Minecraft world contains hours of other people&#8217;s work. Disk failure, a bad plugin, a corrupted chunk, or an abusive player can make it unusable.<\/p>\n<p>For a consistent backup of a small server, I prefer saving the world and briefly stopping the service:<\/p>\n<pre><code>systemctl stop minecraft\ntar -czf \/var\/backups\/minecraft-$(date +%F).tar.gz \n  -C \/home\/minecraft\/server world world_nether world_the_end server.properties whitelist.json ops.json\nsystemctl start minecraft<\/code><\/pre>\n<p>This creates a local archive. A file on the same VPS will not save you if the VPS disappears. Copy it to external storage with rsync or borgbackup, then test the restore. A backup that has never been restored is only a hopeful pile of bytes.<\/p>\n<p>Once a month, I extract one archive into a temporary directory and check that the world files are present and readable. Testing is part of taking the backup.<\/p>\n<p>You can automate this with cron, but check the schedule twice. I once added an extra asterisk to a cron entry; the backup script ran every minute and a mail queue grew behind it. A tiny syntax mistake can turn into an expensive night in systems administration.<\/p>\n<h2 id=\"monitor-the-game-not-just-the-port\">Monitor the game, not just the port<\/h2>\n<p>Uptime Kuma can tell you whether the port responds. It cannot tell you whether the game is running well. Sending CPU, RAM, disk usage, and disk latency to Prometheus with node_exporter, then viewing them in Grafana, gives a more useful picture.<\/p>\n<p>In my homelab, a failing fan once made the temperature graph climb in steps. The graph warned me before the players did.<\/p>\n<p>To inspect disk usage:<\/p>\n<pre><code>df -h\nncdu -x \/home\/minecraft\/server<\/code><\/pre>\n<p>The <code>-x<\/code> flag matters: <code>ncdu<\/code> stays on the same filesystem instead of wandering into mounted paths. If the world directory grows unexpectedly, inspect world generation, log files, and plugin caches.<\/p>\n<p>If the service is running but nobody can connect:<\/p>\n<pre><code>systemctl status minecraft --no-pager\nss -ltnp | grep 25565\nufw status\nmtr -rwzbc 20 mc.example.com<\/code><\/pre>\n<p>The <code>ss<\/code> output should show Java listening on the port. If the port is listening, the firewall is open, and DNS is correct, use <code>mtr<\/code> to inspect the network path. Packet loss at an intermediate hop is not automatically a Minecraft problem; some routers limit ICMP responses.<\/p>\n<p>When players say &#8220;there is lag,&#8221; separate TPS, CPU load, and network latency. Lowering view distance may reduce CPU pressure, but it will not fix high ping. Adding RAM will not remove an entity flood caused by a broken plugin.<\/p>\n<h2 id=\"check-these-before-sharing-the-address\">Check these before sharing the address<\/h2>\n<ul>\n<li>Does the Java version match the Minecraft and Paper versions?<\/li>\n<li>Does the server run as <code>minecraft<\/code> instead of root?<\/li>\n<li>Will it start after a reboot with <code>systemctl enable minecraft<\/code>?<\/li>\n<li>Does UFW expose only SSH and the required Minecraft port?<\/li>\n<li>Is the whitelist enabled, with OP accounts limited?<\/li>\n<li>Do you have an external backup that has been restored successfully?<\/li>\n<li>Do you have alerts for CPU, RAM, disk usage, and service status?<\/li>\n<\/ul>\n<p>If you cannot answer yes to all of these, wait before announcing the server. I still catch myself saying &#8220;it works for now&#8221; and postponing monitoring or backups. That is how a small maintenance task becomes a night shift.<\/p>\n<h2 id=\"questions-i-hear-often\">Questions I hear often<\/h2>\n<h3>How much RAM does a Minecraft server need?<\/h3>\n<p>A Java server for 2-5 players with few plugins can start with 2 GB, but the operating system also needs memory. As player count, mods, view distance, and world generation grow, 4 GB or more may be necessary.<\/p>\n<h3>Which Java version should I use for a Minecraft VPS?<\/h3>\n<p>Minecraft 1.20.5 and newer versions generally use Java 21, while older versions commonly use Java 17. Confirm the exact version in the relevant Paper notes and the server startup output.<\/p>\n<h3>How do I point a domain name to a Minecraft server?<\/h3>\n<p>Add an A record for the subdomain pointing to the VPS IPv4 address. If the server uses a port other than 25565, players must append the port or you must configure a suitable SRV record.<\/p>\n<h3>Can I back up the server without stopping it?<\/h3>\n<p>Yes, but a copy taken while the world is being written may be inconsistent. On a small server, <code>save-all<\/code> followed by a short stop is safer; more advanced setups can use filesystem snapshots and restore tests.<\/p>\n<p>For me, setup is not finished when the first player connects. It is finished when the server starts after a reboot and a backup has successfully come back. I do not send the IP to the group chat until both tests pass.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A practical walkthrough for setting up a Paper Minecraft Java server on an Ubuntu 24.04 VPS, with resource planning, security, systemd, DNS, and tested backups.<\/p>\n","protected":false},"author":2,"featured_media":407,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[1479,43,1470,1473,687,1482,1476,29],"class_list":["post-409","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hosting","tag-java-en","tag-linux","tag-minecraft-server","tag-paper-en","tag-server-administration","tag-systemd","tag-ubuntu-server","tag-vps"],"lang":"en","translations":{"en":409,"tr":408},"pll_sync_post":[],"_links":{"self":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/409","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/comments?post=409"}],"version-history":[{"count":1,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/409\/revisions"}],"predecessor-version":[{"id":411,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/posts\/409\/revisions\/411"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/media\/407"}],"wp:attachment":[{"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/media?parent=409"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/categories?post=409"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vps.tc\/blog\/wp-json\/wp\/v2\/tags?post=409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}