Skip to content
Guide

Minecraft server nogui: run it headless on Linux

What the nogui flag does, the exact java command and JVM flags to go with it, how to stop the server without losing chunks, and three ways to keep it running headless: tmux, systemd, and a container.

· 10 min read

What nogui actually does

The vanilla server jar has two faces. Start it on a desktop with java -jar server.jar and it opens a small Swing window: a player list, a memory and tick-time graph, a log pane, and a command box. Add nogui and that window is never created. The server talks to the terminal you started it from instead, reading commands from standard input and writing its log to standard output.

bash
java -Xms4G -Xmx4G -jar server.jar nogui

The server opens the window only when two things are true: you did not pass nogui (the server accepts --nogui as well), and Java reports a display it can draw on. The second condition is why a server started over a plain SSH session often comes up without a window even when you forget the flag: there is no display, Java runs headless, and the GUI step is skipped. Do not rely on that. With X11 forwarding on, in a desktop session, or anywhere DISPLAY happens to be set, the window appears, and over a forwarded session it is drawn across the network. nogui makes the behavior the same wherever the command runs.

Why you want it on a server:

  • There is no screen. A VPS, a home server in a closet, or a container has no display. The terminal console is the interface.
  • The terminal console is the better tool anyway. It works over SSH, lives on inside tmux or screen, can be supervised by systemd, and everything it prints also lands in logs/latest.log.
  • A little less overhead. The window keeps a graph and a player list updated. That costs a little memory and CPU, which is small, but it buys nothing on a machine nobody looks at.

The command, and the JVM flags that matter

A complete headless start for a vanilla server:

bash
cd /opt/minecraft
java -Xms4G -Xmx4G -jar server.jar nogui

Everything before -jar is for the JVM; everything after the jar name is for Minecraft. nogui is a server argument, so it has to come after server.jar. Put it before -jar and Java takes it for the name of a class to run and fails with Could not find or load main class nogui.

  • -Xmx is the maximum heap, and it is the number that matters. Too small and the server spends its time in garbage collection or dies with OutOfMemoryError; too large and the host starts swapping. 2G suits a solo vanilla world, 4G a group of friends, 6G to 10G a modpack.
  • -Xms is the starting heap. Setting it equal to -Xmx makes the JVM claim the memory once, at startup, instead of growing the heap while players are online. Paper's documentation recommends keeping the two equal.
  • Leave headroom. The JVM uses memory outside the heap (thread stacks, class metadata, native buffers), so the process sits above -Xmx. On an 8 GB machine that also runs an operating system, a 6G heap is about the ceiling.

Aikar's flags, honestly

Aikar's flags are a set of G1 garbage-collector settings tuned for the way a Minecraft server allocates memory: a flood of short-lived objects every tick. They are the most widely copied server JVM flags. PaperMC's documentation hosts the current set under the name "Aikar's flags", and the itzg Docker image applies the same list when you set USE_AIKAR_FLAGS. For a heap under 12G:

bash
java -Xms4G -Xmx4G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 \
  -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch \
  -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M \
  -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 \
  -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 \
  -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem \
  -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true \
  -jar server.jar nogui

What to expect: shorter and more predictable garbage-collection pauses, which players notice as fewer lag spikes. What not to expect: more TPS from a server that is overloaded by entities or redstone, or a rescue for a heap that is simply too small. The flags date from 2018, and Java's G1 collector has improved a lot since, so on Java 17 and 21 the gap to a plain -Xms/-Xmx start is smaller than it once was. Measure before and after rather than assume. Two side effects are worth knowing: -XX:+AlwaysPreTouch makes the JVM touch the whole heap at startup, so the process holds its full memory from the first second and boots a little slower, and for heaps of 12G and more the recommended percentages change (the PaperMC page lists them).

What you will see in the console

The very first start creates eula.txt and server.properties and then exits, with a line like:

text
[ServerMain/INFO]: You need to agree to the EULA in order to run the server. Go to eula.txt for more info.

Open eula.txt, change eula=false to eula=true (which means you accept Mojang's EULA), and start again. This time the log runs through the startup steps:

text
[Server thread/INFO]: Starting minecraft server version 1.21.1
[Server thread/INFO]: Loading properties
[Server thread/INFO]: Default game type: SURVIVAL
[Server thread/INFO]: Starting Minecraft server on *:25565
[Server thread/INFO]: Preparing level "world"
[Server thread/INFO]: Done (6.512s)! For help, type "help"

After Done, the terminal is the server console. Type commands without the leading slash: list, say Back in five, whitelist add Steve, op Steve, save-all. They are the same commands an operator runs in game, executed with full permissions. Output from the game, joins, chat, and errors scroll past in the same window, so a busy server can bury your typing; the command still goes through when you press Enter.

Stopping it cleanly

Type stop in the console. The server kicks players with a shutdown message, saves player data and every loaded dimension, and exits:

text
[Server thread/INFO]: Stopping the server
[Server thread/INFO]: Stopping server
[Server thread/INFO]: Saving players
[Server thread/INFO]: Saving worlds
[Server thread/INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:overworld

Wait for the prompt to come back before you close the terminal or touch the world folder. A large world can take tens of seconds.

Why not Ctrl-C? Ctrl-C sends SIGINT, and the server does catch it: a JVM shutdown hook runs the same save-and-stop. Most of the time that works. But it is the emergency path, not the normal one. Players get no warning, and if whatever sent the signal follows up with SIGKILL before the save finishes (a supervisor that ran out of patience, a kill -9, a host that is powering off), region files are left half-written, and half-written region files are how chunks reset. stop takes the normal path every time, and you can see in the log when it is done.

Running it headless for real

A server started in an SSH session goes down when that session ends. There are three common ways to keep it running, in rising order of how unattended they are.

tmux or screen

A terminal multiplexer keeps the console alive after you disconnect and lets you come back to it later.

bash
tmux new -s minecraft
cd /opt/minecraft && java -Xms4G -Xmx4G -jar server.jar nogui
# detach: Ctrl-b, then d
tmux attach -t minecraft

With screen the equivalents are screen -S minecraft, Ctrl-a then d to detach, and screen -r minecraft to reattach. tmux can also type into the console without attaching, which is handy in scripts: tmux send-keys -t minecraft 'say Restarting in one minute' Enter. The weak spot is that nothing restarts the server after a crash or a reboot. That is fine for a weekend world, not for one people expect to find online.

A systemd service

On a dedicated Linux box, systemd is the tool that starts the server at boot, restarts it after a crash, and collects its log. Create a user and a directory for it first:

bash
sudo useradd --system --create-home --home-dir /opt/minecraft --shell /usr/sbin/nologin minecraft

Put server.jar and an accepted eula.txt in /opt/minecraft, owned by that user, then save this as /etc/systemd/system/minecraft.service:

ini
[Unit]
Description=Minecraft server
After=network-online.target
Wants=network-online.target

[Service]
User=minecraft
WorkingDirectory=/opt/minecraft
ExecStart=/usr/bin/java -Xms4G -Xmx4G -jar server.jar nogui
Restart=on-failure
RestartSec=10
TimeoutStopSec=120
SuccessExitStatus=143
NoNewPrivileges=true
ProtectSystem=full
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Then sudo systemctl daemon-reload, sudo systemctl enable --now minecraft, and journalctl -u minecraft -f to follow the log.

The lines worth understanding:

  • Restart=on-failure brings the server back after a crash, but not after a clean stop, which exits with status 0.
  • systemd stops a service with SIGTERM. The server catches it and runs the shutdown hook described above, and TimeoutStopSec=120 gives it two minutes to save before systemd escalates to SIGKILL. The default is 90 seconds, which a big world can outlast.
  • SuccessExitStatus=143 exists because a JVM that exits on SIGTERM reports status 143. Without the line, every systemctl stop shows up as a failure.
  • Under systemd the server has no terminal, so there is no console to type into. Turn on RCON in server.properties (enable-rcon=true, a long rcon.password, rcon.port=25575) and use an RCON client on the same machine, for example mcrcon -H 127.0.0.1 -P 25575 -p 'your-password' list. Running stop over RCON before systemctl stop gives you the normal shutdown path instead of the signal. Keep 25575 closed in the firewall; RCON sends its password in plain text.

A container

The itzg/minecraft-server image is the shortest route to a headless server with restarts, the right Java, and a clean stop already handled. The full setup, with a compose file explained line by line, backups, and Podman notes, is in Run a Minecraft server in Docker on Linux. The one-line version:

bash
docker run -d -it --name minecraft-server \
  -e EULA=TRUE -e TYPE=VANILLA -e VERSION=1.21.1 -e MEMORY=4G \
  -e RCON_PASSWORD=change-this-to-something-long \
  -p 25565:25565 -p 127.0.0.1:25575:25575 \
  -v "$PWD/data:/data" \
  --restart unless-stopped --stop-timeout 120 \
  itzg/minecraft-server:latest

For Podman, replace docker with podman and use the full image name, docker.io/itzg/minecraft-server:latest. Podman has no daemon, so a restart policy alone does not bring the container back after a reboot; the Docker guide's Podman section shows the Quadlet unit that does.

Do you need nogui here? No. A container has no display, so Java runs headless and the server never opens a window whether or not the flag is passed. If you want the flag on the command line anyway, set GUI=FALSE and the image appends nogui itself. What the image adds on top is the stop handling: docker stop sends SIGTERM, the image turns it into a stop on the server console and waits, and --stop-timeout 120 keeps Docker from killing the container before the save ends. For commands, docker exec -i minecraft-server rcon-cli opens an RCON session, and docker attach minecraft-server reaches the console itself (detach with Ctrl-p then Ctrl-q; Ctrl-C there signals the server).

Paper, Fabric, and Forge

The flag is not vanilla-only, but where it goes depends on how the server software starts.

  • Paper and Purpur take it the same way: java -Xms4G -Xmx4G -jar paper.jar --nogui. Paper's documentation spells it --nogui; plain nogui works too, because Paper keeps vanilla's check for both forms. Paper's console is also nicer to live in, with colored output and tab completion when it runs in a real terminal.
  • Fabric starts from the launcher jar you download from fabricmc.net, named like fabric-server-mc.1.21.1-loader.<version>-launcher.<version>.jar. Fabric then boots the vanilla server, so nogui goes at the end exactly as above.
  • Forge for Minecraft 1.17 and newer has no single server jar to java -jar. The installer (java -jar forge-<version>-installer.jar --installServer) writes run.sh, run.bat, user_jvm_args.txt, and a libraries folder. JVM flags such as -Xmx and Aikar's set go in user_jvm_args.txt. nogui is a program argument, so pass it to the script, ./run.sh nogui, or add it to the java line inside run.sh, whose own comment says to put arguments like nogui before the "$@":
bash
java @user_jvm_args.txt @libraries/net/minecraftforge/forge/1.20.1-47.4.10/unix_args.txt "$@"

NeoForge's installer produces the same layout. Forge for 1.16.5 and older ships a single jar and starts like vanilla, with nogui after the jar name. In the itzg image you never edit these files: it runs Forge's run.sh and writes your memory and JVM settings into user_jvm_args.txt for you.

Choosing between them is its own question, covered in Paper vs Fabric vs Forge.

Where MineUI fits

MineUI is a desktop app for running Minecraft servers on the computer it is installed on, and it runs every server headless. In Simple mode (Add server, then Plain Minecraft server) it downloads the official vanilla jar, checks its SHA-1, and starts it with java -Xms<memory>M -Xmx<memory>M -jar server.jar nogui, streams the console output into a log view, and stops the server by typing stop into its console, as this guide recommends. For Paper, Fabric, Forge, NeoForge, or a modpack, it creates or attaches to an itzg/minecraft-server container and reaches the server through RCON, behind an allowlisted Console page. If your server lives on a box with no desktop, the systemd unit or the container above is the right tool. If it lives on the machine you sit at, the Getting Started page covers both flows.

Troubleshooting

Could not find or load main class nogui. nogui is in the wrong place. It must come after -jar server.jar, not before it. For Forge 1.17 and newer it goes after ./run.sh, never in user_jvm_args.txt, which is for JVM options only.

A window still opens. The flag is missing from the command that actually runs. Check the script, the unit file, or the hosting panel's start command, not the one you typed last.

Error: Unable to access jarfile server.jar. Java is running in a different directory from the jar. Start it from the server folder, or in systemd check WorkingDirectory.

**** FAILED TO BIND TO PORT! Another process already listens on 25565, very often a second copy of the same server left running in a forgotten tmux session. sudo ss -ltnp | grep 25565 shows which one.

The server stops when I close SSH. It was started in the foreground of that session. Run it in tmux or screen, or as the systemd service above.

systemctl status shows failed after a normal stop. The JVM exited with status 143 after SIGTERM. Add SuccessExitStatus=143 to the unit, then sudo systemctl daemon-reload.

Next steps

Related guides

Frequently asked questions

What does nogui do in a Minecraft server?
It tells the server not to open its Swing window (the player list, stats graph, and log pane you get when you double-click the jar on a desktop). The server runs in the terminal instead: it reads commands from standard input and writes its log to standard output and logs/latest.log. Put it after the jar name: java -Xmx4G -jar server.jar nogui.
Do I need nogui in Docker?
No. A container has no display, so Java runs headless and the server never opens a window either way. With the itzg/minecraft-server image you can set GUI=FALSE if you want nogui passed explicitly, but it changes nothing you can see.
How do I stop a nogui server?
Type stop in its console, or send stop over RCON. The server kicks players, saves player data and every dimension, and exits. Ctrl-C and SIGTERM also trigger a save through the JVM's shutdown hook, but that is the emergency path: a SIGKILL that follows too soon leaves half-written region files.
Can I still use commands without the GUI?
Yes. The terminal you started the server from is the console: type commands without the leading slash. If the server runs under systemd or in a container, there is no terminal to type into, so enable RCON in server.properties and use an RCON client such as mcrcon on the same machine, or rcon-cli through docker exec for the itzg image.
Does nogui improve performance?
Slightly. The window costs a little memory and some CPU to keep its graph and player list updated, and nogui saves that. The changes that actually move TPS and lag spikes are elsewhere: a heap sized for the world, Xms equal to Xmx, sensible GC flags such as Aikar's, a lower view distance, and Paper instead of vanilla for busy servers.