What the server is and what it needs
The dedicated server is a headless copy of the game that only hosts multiplayer, run from a terminal; the official wiki highly recommends a dedicated Linux or Windows machine for it.
- Java is the only runtime. The build files compile the game for Java 17 (
targetCompatibilityin build.gradle), so we install the Java 17 runtime; that version choice is ours. - The download is
server-release.jarfrom the GitHub releases page; v160.7 was the latest release on 31 October 2026. - The port is 6567, TCP and UDP, the wiki's figure and the default in Vars.java.
config portchanges it. - Clients must match the server. The wiki FAQ says any device can join any other as long as all run the same version.
- The licence is GPL-3.0 (the repository's LICENSE file). In our general reading, which is not legal advice, GPL-3.0 has no network-use clause, so hosting players asks nothing of you, but distributing the server to others, modified or not, obliges you to make its corresponding source available to them (with it, or by one of the other routes section 6 of the licence allows).
The project publishes no CPU or memory figures for the server, so this page gives none. Our own rule of thumb, not the project's, is that single-thread speed matters more than core count.
Picking the machine
Every processor on our game server range scores 2,000 or more on PassMark's single-thread test, and each machine has one tenant, root and an unmetered port. For one Mindustry world, start with the entry build on the Xeon E3-1585L v5 (32 GB, one NVMe SSD, four cores), offered in every one of our cities. That line is the HPE ProLiant m710x cartridge, which (with the m510) has its own out-of-band console: the box stays reachable when SSH is not.
Several worlds on one box want more cores. Each extra instance needs its own directory, its own unit, its own game port (config port 6568) and its own socket port (config socketInputPort 6860), or the second start reports the socket already in use. Change the stop, save-job and backup commands to match.
A ready, racked machine is live about 30 minutes after the order is approved and can be billed by the hour, which suits a weekend event; a build to your configuration takes 4 to 24 hours. All five cities sit on our own network, AS19624: ask your players to ping each city's test IP on the data centers page and trace routes with the looking glass.
Install and service unit
The block below enables ufw allowing SSH alone, installs Java, creates a mindustry system user with home /srv/mindustry, downloads the jar, checks the SHA-256 digest GitHub lists for it, and writes the systemd unit without starting it. A failure prints STOP; re-running the block is safe.
# as root on Ubuntu 24.04; V and SUM from the GitHub release page
V=v160.7
SUM=f7b899a4038b95b14a824d5f8993984f79d73baa3254c385b6538327f9c0befd
J=/srv/mindustry/server-release.jar
sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw &&
ufw allow OpenSSH && ufw --force enable &&
apt-get update && apt-get install -y openjdk-17-jre-headless curl netcat-openbsd &&
{ id mindustry >/dev/null 2>&1 ||
useradd --system --create-home --home-dir /srv/mindustry \
--shell /usr/sbin/nologin mindustry; } &&
sudo -u mindustry curl -fL -o "$J.new" \
"https://github.com/Anuken/Mindustry/releases/download/$V/server-release.jar" &&
echo "$SUM $J.new" | sha256sum -c - &&
mv "$J.new" "$J" &&
cat > /etc/systemd/system/mindustry.service <<'EOF' &&
[Unit]
Description=Mindustry dedicated server
After=network-online.target
Wants=network-online.target
[Service]
User=mindustry
WorkingDirectory=/srv/mindustry
ExecStart=/usr/bin/java -jar /srv/mindustry/server-release.jar
ExecStop=-/bin/bash -c "printf 'save main\n' > /dev/tcp/127.0.0.1/6859 && sleep 10"
Restart=on-failure
RestartSec=10
SuccessExitStatus=130 143
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && echo "STEP 1 ok" || echo "STOP - fix the error above, then run the block again"
Unlike the repository's server/run-jar loop, systemd restarts the jar unless it exits with 0, 130 or 143 (the JVM's code when systemd stops it), and we drop -XX:+HeapDumpOnOutOfMemoryError. ExecStop asks the server to save before it stops and fails harmlessly if it is already down.
First start: configure, host, save
Run the server once in your own terminal so you can type into its console: sudo -u mindustry -H bash -c 'cd /srv/mindustry && java -jar server-release.jar'. It creates config/ next to the jar. Then enter, in order:
config name Co-op survival - run by youandconfig descwith a line about the server (100 characters at most).config autosave true. Autosave is off by default (Administration.java); once on, it writes anauto_save every 300 seconds and keeps the newest 10.config socketInputAddress 127.0.0.1, thenconfig socketInput true. This opens the command socket on TCP 6859, loopback only.maps all, thenhost Map_Name survivalwith a name from the list (the host command reads underscores as spaces). The modes are survival, sandbox, attack and pvp.save main, and wait for the line saying it saved toconfig/saves/main.msav.config startCommands load main, so every service start reloads that slot, thenexit.
Now start the service and open the game port: systemctl enable --now mindustry && ufw allow 6567 && ufw status. ufw allow 6567 without a protocol covers TCP and UDP, and because IPV6 was set before the first rule, the rule exists for IPv6 too. Watch the log with journalctl -u mindustry -f; type console commands with nc 127.0.0.1 6859. Close nc when you are done: the socket thread in ServerControl.java serves one connection at a time, so a forgotten session holds back the save that the backup and ExecStop send.
The command socket has no password. Anything on the machine that can open a TCP connection to 127.0.0.1:6859 can run every console command, including js, which runs arbitrary JavaScript. Keep it on loopback, never allow 6859 in ufw, and keep the box to your own accounts.
Admins, moderation and the public list
Join from the game, then run admin add YourName on the console while the server is hosting. By name it only finds a player who is online; an offline player can be made admin by their ID. Several protections are already on in a headless build, by the defaults in Administration.java: antiSpam, strict (corrects positions, refuses duplicate UUIDs) and votekick are on, and allowCustomClients is off.
- A private server stays private with
config whitelist trueand onewhitelist addper player ID. kick,ban,unban,bansandsubnet-banhandle griefers;infoshows the names and IPs a player has used.config autoPause truestops waves while nobody is online; it is off by default.- Custom maps go in
config/maps/(thenreloadmaps); mods and JVM plugins go inconfig/mods/.
To appear in every client's public list, open a pull request against the MindustryServerList repository; its README asks for active moderation, a name and description that say what kind of server it is, vanilla clients able to join without installing anything, and a server that is online when reviewers try it.
Backups and restore
Everything the server keeps lives under /srv/mindustry, the jar plus config/ (saves, maps, mods, logs, rules.hjson and stored settings). The script below asks the server to save main over the socket, waits 15 seconds, archives the whole folder plus the unit file, lists the archive, reads main.msav back out of it, and only then deletes archives older than 14 days. A second cron line sends save main every 10 minutes. Those numbers are our choice.
# as root, write the backup script, schedule it and run it once
cat > /usr/local/sbin/mindustry-backup.sh <<'EOF' &&
umask 077
dest=/var/backups/mindustry
mkdir -p -m 700 "$dest" || exit 1
if printf 'save main\n' > /dev/tcp/127.0.0.1/6859; then sleep 15; fi
out="$dest/mindustry-$(date +%F-%H%M%S).tar.gz"
if tar -czf "$out.part" -C / srv/mindustry etc/systemd/system/mindustry.service &&
tar -tzf "$out.part" > /dev/null &&
tar -xzOf "$out.part" srv/mindustry/config/saves/main.msav > /dev/null; then
mv "$out.part" "$out" && echo "backup written to $out" &&
find "$dest" -name 'mindustry-*.tar.gz' -mtime +14 -delete
else
rm -f "$out.part"; echo "backup FAILED, see the tar messages above" >&2; exit 1
fi
EOF
chmod 700 /usr/local/sbin/mindustry-backup.sh &&
printf '%s\n' "printf 'save main\\n' > /dev/tcp/127.0.0.1/6859" > /usr/local/sbin/mindustry-save.sh &&
printf '%s\n' '*/10 * * * * root bash /usr/local/sbin/mindustry-save.sh > /dev/null 2>&1' \
'30 4 * * * root bash /usr/local/sbin/mindustry-backup.sh >> /var/log/mindustry-backup.log 2>&1' > /etc/cron.d/mindustry-backup &&
bash /usr/local/sbin/mindustry-backup.sh || echo "STOP - no backup was written"
The archives sit on the same disks as the server, so copy /var/backups/mindustry to another machine yourself. SaveIO.java also keeps the previous version of each slot as main.msav-backup.msav on every save. A clean stop or reboot saves main first through ExecStop; after a crash, systemd restarts on the last main save, at most about 10 minutes old with the save job, while an autosave may be newer. To continue from one, type stop, then saves, then load with the autosave's name without .msav.
To restore, run this as root. It stops if the archive is missing or unreadable, or if an older set-aside copy is still there. On a new machine, run the install block first so the user and unit exist.
# as root; set f to an archive listed in /var/backups/mindustry
f=/var/backups/mindustry/mindustry-2026-10-31-043000.tar.gz
if [ -f "$f" ] && tar -tzf "$f" > /dev/null && [ ! -e /srv/mindustry.previous ]; then
systemctl stop mindustry &&
mv /srv/mindustry /srv/mindustry.previous &&
tar -xzf "$f" -C / srv/mindustry &&
chown -R mindustry:mindustry /srv/mindustry &&
systemctl start mindustry &&
echo "restore ok; the old copy is in /srv/mindustry.previous" ||
echo "STOP - restore halted part way; use the rollback below"
else
echo "STOP - archive missing or unreadable, or /srv/mindustry.previous exists"
fi
To roll back, run this as root: `systemctl stop mindustry; [ -d /srv/mindustry.previous ] && [ ! -e /srv/mindustry.failed ] && { [ ! -e /srv/mindustry ] || mv /srv/mindustry /srv/mindustry.failed; } && mv /srv/mindustry.previous /srv/mindustry && systemctl start mindustry && systemctl is-active mindustry && echo 'ROLLED BACK' || echo 'STOP - check the messages above'. A STOP has three causes. The set-aside copy is gone; a /srv/mindustry.failed left by an earlier attempt is blocking it (rename that folder and run the line again); or the files are back but the server did not start, which journalctl -u mindustry explains. When the restored world is right, delete /srv/mindustry.previous` so the next restore is not refused.
Updates and when not to run it here
When a new release ships, players' clients update and stop matching your server. Read the release notes, set V and SUM to the new tag and its digest in your shell, then run, as root: `J=/srv/mindustry/server-release.jar && bash /usr/local/sbin/mindustry-backup.sh && sudo -u mindustry curl -fL -o "$J.new" "https://github.com/Anuken/Mindustry/releases/download/$V/server-release.jar" && echo "$SUM $J.new" | sha256sum -c - && systemctl stop mindustry && mv "$J.new" "$J" && systemctl start mindustry`. The backup runs first and the old jar stays in place unless the new one matches its digest. A mod whose content fails to load makes the server exit with code 1 at start (ServerLauncher.java), and systemd keeps retrying every 10 seconds until you remove or fix the mod.
The dedicated server hosts maps in its own modes; it has no command for the campaign, and loading a save detaches the game from its campaign sector (ServerControl.java), so saving it back over the same slot loses that link. To play the campaign together, host from inside the game. Start playing, open the pause menu and press Host Multiplayer Game (the wiki FAQ's steps for playing on one network); a rented machine adds nothing there.
Questions
Can phone and Steam players join the same server?
Yes, if every device runs the same version; the wiki FAQ calls the game cross-platform. Update the server when your players' clients update.
Which port does a Mindustry server use?
6567, on both TCP and UDP. Change it by giving config port the new number on the console, then restart. The command socket on 6859 is a separate TCP port that must stay on 127.0.0.1.
How do I move my world to a new machine?
On the new box, run the install block first, copy one archive from /var/backups/mindustry across, and run the restore block with f pointing at it. The restore resets ownership to the new mindustry user, and the start command loads the main slot. Then run systemctl enable mindustry && ufw allow 6567, and run the backup block once so the new box writes its own nightly archives.