Mindustry Server v159.7 on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and use of Mindustry Server v159.7 on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images.
Mindustry is an open-source factory-building tower-defence game: players mine resources, lay down conveyor and power networks, automate production, and defend what they have built against waves of enemies. It is at its best in multiplayer, where a group builds one factory together on a shared, persistent map — and that is what a dedicated server is for.
This image ships the dedicated server, not the game client. Each Mindustry release publishes four artefacts, and only one of them is a server: server-release.jar. Mindustry.jar is the desktop client and is useless as a hosted server. The build asserts the artefact it installed really is the server — it must carry mindustry/server/ServerLauncher.class, its manifest Main-Class must be mindustry.server.ServerLauncher, and it must carry no desktop-client classes — before the image is ever captured.
Your players do not need this image. They install Mindustry normally on their own machines and use Join Game → Add Server with this VM's address. This VM is the world they all share, and it keeps running when they log off.
What you get
| Capability | What it means in practice |
|---|---|
| A persistent world | The map, the factory and the wave count live on the server. Players come and go; the world does not reset. |
| Automatic saves | The server autosaves every five minutes and keeps the five most recent, so a crash costs minutes, not a session. A reboot resumes the most recent autosave by itself. |
| A real admin console | mindustryctl drives the server's own console — host and stop maps, list players, grant admin, ban and unban, run waves, change settings. |
| Anti-abuse defaults | A player limit, chat rate limiting, strict mode, spam kicks and modified-client rejection are configured — not left at upstream's defaults, several of which are off. |
| Closed administration | The console is bound to loopback and requires a per-VM token. The game port is the only thing published. |
Licence
Mindustry is free software licensed under the GNU General Public License, version 3. That is upstream's licence, verified from the LICENSE file at tag v159.7 — not from a badge — and it applies to the game itself, which cloudimg does not modify.
The image ships the obligations with the binary:
/usr/share/doc/mindustry/LICENSE— the full GPL-3.0 text./usr/share/doc/mindustry/SOURCE.txt— the exact upstream tag and artefact digest the installed binary came from, a link to the corresponding source tree, and a written offer for that source.
cloudimg's own additions (the console bridge, the first-boot bootstrap, the protocol probe and the audit scripts) are kept in separate files under /usr/local, are themselves GPL-3.0-or-later, and the protocol probe's source ships next to its compiled classes. Mindustry is a trademark of its authors; cloudimg is not affiliated with or endorsed by the Mindustry project.
Before you start
| Item | Value |
|---|---|
| Recommended VM size | Standard_B2s (2 vCPU / 4 GiB) — comfortable for ~30 players on a standard map |
| Game port | 6567 TCP and UDP — this must be reachable from the internet |
| Admin console | 127.0.0.1:6859 — loopback only, never published |
| SSH | 22, restricted to your own address |
| OS disk | 30 GiB or more; saves and logs are capped, but maps and mods add up |
Measured on this image: the server used 162 MB of RSS with a 256×256 world hosted and a client connected, against the 4 GiB of a Standard_B2s. The JVM heap is capped explicitly at 1024 MB rather than left to the JVM's default share of host RAM. There is no swap in the image — Azure manages swap on the ephemeral resource disk.
Network security group
The game port is the one thing that genuinely must be public, and nothing else should be. Mindustry uses 6567 for both TCP and UDP, so you need two rules:
# Run from your workstation, not the VM. Replace the names with your own.
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
--name mindustry-game-tcp --priority 1010 \
--access Allow --protocol Tcp --direction Inbound \
--source-address-prefixes Internet --destination-port-ranges 6567
az network nsg rule create --resource-group <your-rg> --nsg-name <your-nsg> \
--name mindustry-game-udp --priority 1011 \
--access Allow --protocol Udp --direction Inbound \
--source-address-prefixes Internet --destination-port-ranges 6567
Do not open 6859. That is the admin console, it is bound to loopback, and it is reached over SSH — see Administering the server.
First boot
On first boot the VM generates its own console token, prepares a clean state directory, starts the server, applies the shipped configuration, hosts a map, and then proves a real Mindustry protocol client can join before it declares itself ready. Nothing is baked into the image: no token, no world, no save, no admin list and no ban list.
Give it two or three minutes after the VM reports Running, then check it:
systemctl is-active mindustry-firstboot.service mindustry.service mindustry-console.service
All three should report active. Then read this VM's credentials:
sudo cat /root/mindustry-credentials.txt
That file is 0600 root:root and contains your console token, this VM's addresses and the address your players connect to. Keep it as you would any other secret.

Checking the server is up
sudo mindustryctl status
You should see Playing on map <name> / Wave <n>, the seconds until the next wave, and the connected players. If it says server closed, no world is hosted — see Hosting a map.
Connecting from the game
Your players need nothing from this guide except an address.
- Open Mindustry on their own machine (Steam, itch.io or the official build — the same version, v159.7).
- Play → Join Game → Add Server.
- Enter the address from
MINDUSTRY_GAME_ADDRESSin the credentials file — your VM's public IP, with:6567if their client asks for a port separately. - The server appears with its name, map and player count. Click it to join.
Version matters. The server rejects clients on a different build, by design — that is how Mindustry avoids desynchronised worlds. It also rejects modified clients, because allowCustomClients is set to false in this image.
This is not a claim taken on trust. The image carries a Mindustry protocol probe built from upstream's own classes — arc-net's client, Mindustry's packet serializer and Mindustry's ConnectPacket — and it completes the real handshake, sends connectConfirm (which is what actually makes the server register a player), and reports what happened:

You can run that check yourself at any time — see Verifying your deployment.
Administering the server
Mindustry is administered from a console. Upstream also offers a remote console over TCP (socketInput), but it has no authentication of any kind: whoever can reach that socket can grant admin, ban players and stop the server. This image therefore leaves it off, and the appliance refuses to start if it is ever switched on.
Instead, administration goes through a cloudimg console bridge that:
- binds only
127.0.0.1:6859, and is fenced at the kernel withIPAddressAllow=localhoston its own systemd unit; - refuses to forward a single command without this VM's console token;
- is driven by
mindustryctl, which is root-only (0750 root:root).
sudo mindustryctl help
Everyday commands
sudo mindustryctl status # what is hosted, wave, players
sudo mindustryctl players # who is connected, with their IDs
sudo mindustryctl maps all # the built-in maps you can host
sudo mindustryctl playerlimit # the current player cap
Making someone an admin
Mindustry admins are granted by player ID, and the ID is shown in players while they are connected:
sudo mindustryctl players
Take the value after ID: for the player you want to promote, then:
sudo mindustryctl admin add <their-player-id>
sudo mindustryctl admins
The player must be connected when you run admin add, and should reconnect afterwards for the client to pick up their new status. admin remove <id> reverses it.
Bans and anti-grief
sudo mindustryctl bans # everyone currently banned
To act on a griefer:
sudo mindustryctl kick <player-name>
sudo mindustryctl ban id <their-player-id>
sudo mindustryctl unban <id-or-ip>
The server also ships with enableVotekick on, so your players can remove a griefer themselves without waiting for you, and with strict mode on, which corrects positions and rejects duplicate player IDs.
Why administration is closed, and how that is proven

Two details in that capture are worth calling out, because they are the difference between a security control and a decorative one:
- The refusal is proven by consequence, not by a message. The check reads the player limit, sends a limit-changing command with a wrong token, and confirms the limit did not move — then sends the same command with the real token and confirms it did.
- The negative has a positive control. "The console port is unreachable from off-box" would equally be explained by a broken probe, so the same check confirms the game port on that same address is reachable.
An authentication failure costs a fixed one-second penalty and deliberately not a lockout — a lockout would make that positive control ambiguous.
Hosting a map
The game port only exists while a world is hosted: Mindustry opens the listener when you host and closes it when you stop. If status says server closed, your players cannot connect.
sudo mindustryctl maps all
Host one of them:
sudo mindustryctl host Ancient_Caldera survival
Loading a large map takes up to a minute or two on a Standard_B2s. Wait for it, then confirm:
sudo mindustryctl status
Other game modes are available in place of survival — attack, pvp, sandbox and editor. To end the current world, sudo mindustryctl stop, then host another. To force the next wave immediately, sudo mindustryctl runwave.
Saves, restarts and retention
The world is real state, and this image treats it as such.
- Autosave is on (upstream ships it off), every 300 seconds, keeping the five most recent. Older autosaves are replaced, so saves cannot fill the disk.
- A reboot resumes the most recent autosave automatically, and falls back to hosting the default map if there is nothing to resume.
- Named saves are yours to make and keep.
sudo mindustryctl save my-world
sudo mindustryctl saves
Saves live in /var/lib/mindustry/config/saves as .msav files. Copy them off the VM with scp if you want a backup you control, and sudo mindustryctl load my-world to bring one back.
This survives a full service restart, not just a reload — the map and the wave number come back identical:

Logs
The server log is at /var/log/mindustry/mindustry.log and is rotated daily or at 32 MB, keeping seven compressed generations for at most 30 days. Upstream's second log stream under the state directory is switched off, so there is exactly one log to reason about.
sudo tail -20 /var/log/mindustry/mindustry.log
Mindustry writes colour escapes to its console. mindustryctl strips them, so its output is plain text; the raw log keeps them, which cat and less -R render correctly.
Tuning the server
Configuration is a plain text file, /etc/mindustry/server-commands.conf, which the service replays into the server's console on every start. Mindustry's own settings.bin is a binary file that also holds the admin list, the ban list and the player database, so this image deliberately ships none — configuration stays auditable as text and no build-time state can ride along in it.
sudo cat /etc/mindustry/server-commands.conf
The anti-abuse defaults it sets, and what upstream ships instead:
| Setting | This image | Upstream default |
|---|---|---|
playerlimit |
30 | no limit at all |
messageRateLimit |
2 seconds | 0 — chat rate limiting disabled |
messageSpamKick |
3 | 3 |
allowCustomClients |
false |
true on desktop builds |
autosave |
true, 5 kept |
false |
socketInput |
false |
false (but unauthenticated when on) |
To change something, edit the file and restart:
sudoedit /etc/mindustry/server-commands.conf
sudo systemctl restart mindustry.service
You can also change a setting live, though it will revert to the file on the next restart — which is the point:
sudo mindustryctl config messageRateLimit
Making the server private
If you want an invite-only server, turn the whitelist on. Players must connect once to be known, then be whitelisted by ID:
sudo mindustryctl config whitelist true
sudo mindustryctl whitelist add <their-player-id>
Add config whitelist true to /etc/mindustry/server-commands.conf so it survives a restart.
Verifying your deployment
The image carries its own audit. It never checks that a service is running or that a port is open — it proves the things a customer actually cares about, and it takes a few minutes because it really does restart the server and reconnect a client:
sudo /usr/local/sbin/mindustry-roundtrip-check.sh
It proves, in order: that the admin credential is required and that a wrong one executes nothing (with the real one re-proven immediately afterwards, so the refusals are not a lockout); that the console answers on loopback and not on this VM's routable address while the game port on that same address does; that a genuine Mindustry protocol client completes the handshake and the server reports the connected player; that malformed clients are rejected; that the world survives a full restart; that logrotate really rotates; and it reports peak server memory under a hosted world with a client connected.
It ends with MINDUSTRY_ROUNDTRIP_OK and exits non-zero if anything failed.
Troubleshooting
Players cannot find or join the server.
Check a world is actually hosted — sudo mindustryctl status must not say server closed, because the game port only exists while a world is up. Then confirm both NSG rules exist: Mindustry needs 6567 on TCP and UDP, and opening only TCP is the single most common cause of "it shows up but will not connect".
A player is refused. Almost always a version mismatch: the client must be on v159.7, the same build as the server. Modified clients are refused too, by design. Check the log:
sudo tail -20 /var/log/mindustry/mindustry.log
mindustryctl says the console is unreachable.
The bridge follows the server. Check both units:
systemctl is-active mindustry.service mindustry-console.service
If mindustry.service is not active, look at why — the committed-secret guard runs before it opens a listener and will refuse to start the server if this VM's console token is missing or weak:
sudo /usr/local/sbin/mindustry-secret-guard.sh
The server was restarted and the world is gone. Resume the most recent autosave, or load a named save:
sudo mindustryctl loadautosave
Memory. The server is modest — 162 MB measured with a world hosted and a client connected — but a very large map with many players will grow it. The heap is capped at 1024 MB; if you genuinely need more, raise -Xmx in /usr/local/sbin/mindustry-run.sh and move to a larger VM size rather than adding swap, which Azure images must not carry.
Support
cloudimg images come with 24/7 support. Include the output of the audit above and the last lines of /var/log/mindustry/mindustry.log with any request.
Mindustry itself is community software — for gameplay questions, mods and map making, the upstream project and its community are the right place.