AoW SMP runs on a single Hostinger KVM 4 VPS in a clean, opinionated stack: Ubuntu at the base, Temurin Java 21 as the runtime, PaperMC 1.21.8 as the server, and systemd babysitting the process. No Docker, no orchestrator, no panel — just the JVM, a unit file, and a console FIFO. The goal is “boring infrastructure” so the fun parts (gameplay, plugins) get all the attention.
1Hardware
The box is a Hostinger KVM 4 instance — full hardware-virtualized, not a shared-kernel container. That matters for Minecraft: the JVM benefits from real CPU pinning, real RAM, and predictable NVMe latency. The server was originally provisioned at 2 vCPU / 8 GB, but we hit translation lag from Geyser + ViaVersion under modest load, so it was upgraded mid-session to the current 4 vCPU / 16 GB tier. Headroom solved it.
| Spec | Value | Notes |
|---|---|---|
| Provider | Hostinger | KVM 4 plan |
| vCPU | 4 | Used for: Paper main thread, Geyser, ViaVersion, async chunk gen / I/O |
| RAM | 16 GB | 8 GB JVM heap, ~2 GB OS + Geyser/Floodgate overhead, 2 GB swap cushion |
| Disk | 200 GB NVMe | World, backups, plugin source all fit comfortably |
| Network | Public IPv4 | play.aowmc.com is the public-facing address for players |
| OS | Ubuntu | Long-lived LTS, kept patched with unattended-upgrades |
2OS & runtime
Ubuntu on the host. Standard package layout, systemd as PID 1, ufw for firewall,
journalctl for logs. Everything Minecraft-related lives under /opt/ so the rest of
the system stays vanilla.
The JVM is Temurin / OpenJDK 21 — the current LTS line. Paper 1.21.8 targets Java 21, and all six in-house packs are built with Java 21 release bytecode.
3Server software
The server jar is PaperMC 1.21.8. Paper is a high-performance fork of Spigot with better chunk handling, async chunk I/O, configurable patches for vanilla edge cases, and the most active plugin ecosystem in the 1.21.x line. Crucially for us, Paper is what Geyser, Floodgate, and ViaVersion all target as their first-class platform.
Two Paper config flags need to be off so Floodgate-authenticated Bedrock players don’t get rejected by Mojang’s profile checks:
# paper-global.yml
proxies:
velocity:
enabled: false
unsupported-settings:
allow-headless-pistons: false
# relevant flags
enforce-secure-profile: false
perform-username-validation: false
online-mode stays true — Java players still authenticate against Mojang normally.
Floodgate handles Bedrock auth out-of-band via Xbox Live.
4JVM tuning
The JVM is launched with a fixed 8 GB heap and the well-known Aikar GC flags for G1 — these are the de-facto standard for Paper servers and tune G1 for many short-lived allocations (which is exactly what Minecraft does each tick).
java \
-Xms8G -Xmx8G \
-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 paper-1.21.8.jar nogui
5Process management
The server runs as a dedicated unprivileged user named minecraft, managed by
a single systemd unit. systemd handles boot-on-startup, auto-restart on crash,
and log capture into the journal.
[Unit]
Description=AoW SMP - PaperMC 1.21.8
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=minecraft
Group=minecraft
WorkingDirectory=/opt/mcserver
ExecStart=/opt/mcserver/start.sh
Restart=on-failure
RestartSec=10
StandardInput=file:/opt/mcserver/console.in
StandardOutput=journal
StandardError=journal
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
The server’s STDIN is wired to a named pipe (FIFO) at
/opt/mcserver/console.in. To send a command into the running server,
you just echo into the pipe. There’s a one-line wrapper:
# Send a command to the live server
/opt/mcserver/cmd.sh "say Server reboot in 5 minutes"
/opt/mcserver/cmd.sh "lp user .Steve parent add member"
/opt/mcserver/cmd.sh "save-all"
# Status
systemctl status minecraft
# Tail live console (Paper stdout)
journalctl -u minecraft -f
# Restart cleanly (Paper handles save-all on SIGTERM)
sudo systemctl restart minecraft
# Stop / start
sudo systemctl stop minecraft
sudo systemctl start minecraft
kill -9 Paper. A clean SIGTERM via systemctl stop
lets Paper flush the world to disk. Hard-kills risk chunk corruption.
6Network
play.aowmc.com is a Cloudflare DNS-only A record pointing at the VPS
public IP. DNS-only (the grey cloud, not orange) is mandatory — Cloudflare’s proxy only handles
HTTP/S, and Minecraft is a raw TCP/UDP protocol. Proxying would break the connection entirely.
| Hostname | Type | Target | Proxy | Purpose |
|---|---|---|---|---|
play.aowmc.com | A | VPS public IP | DNS-only | Java + Bedrock connect address |
aowmc.com | A | VPS1 | Proxied | Apex; public website |
map.aowmc.com | A | VPS public IP | DNS-only | Public squaremap endpoint |
UFW is enabled with a deny-by-default policy. Only the Minecraft ports above and SSH are open.
map.aowmc.com points to the squaremap public endpoint. Internal map service traffic is
not exposed publicly.
The VPS is also reachable on a private admin network for SSH and tooling. Players never see this
address; all public traffic uses play.aowmc.com and map.aowmc.com.
7Storage layout
Everything Minecraft-related lives under /opt. The world data, pack source code,
and backups are deliberately separated so a wipe of one doesn’t touch the others.
/opt
├── mcserver/ # Runtime: the actual server
│ ├── paper-1.21.8.jar
│ ├── start.sh # JVM flags + jar launch
│ ├── cmd.sh # Wrapper to write into console.in
│ ├── console.in # FIFO -> server STDIN
│ ├── eula.txt
│ ├── server.properties # port 25565, max-players 20, MOTD, etc.
│ ├── paper-global.yml # enforce-secure-profile=false, etc.
│ ├── bukkit.yml spigot.yml # Vanilla bukkit/spigot config
│ ├── world/ world_nether/ world_the_end/
│ ├── plugins/
│ │ ├── Geyser-Spigot.jar
│ │ ├── floodgate-spigot.jar
│ │ ├── ViaVersion.jar ViaBackwards.jar
│ │ ├── EssentialsX.jar Vault.jar
│ │ ├── GriefPrevention.jar WorldGuard.jar WorldEdit.jar
│ │ ├── LuckPerms.jar CoreProtect.jar
│ │ ├── AuraSkills.jar AxGraves.jar
│ │ ├── QuickShop-Hikari.jar GlobalMarketplace.jar
│ │ ├── DecentHolograms.jar TAB.jar PlaceholderAPI.jar Plan.jar
│ │ ├── Chunky.jar squaremap.jar
│ │ └── AoW*.jar # 6 in-house packs (~21 third-party; ~27 total)
│ └── logs/ # Paper rolling logs
│
├── aow-plugins/ # Source: in-house pack code
│ ├── AoWMythic/
│ ├── AoWWorldGen/
│ ├── AoWMobs/
│ ├── AoWQoL/
│ ├── AoWContent/
│ └── AoWInfra/
│ └── (each: pom.xml, src/, target/, config.yml)
│
└── backups/ # Compressed world snapshots
├── world-YYYYMMDD.tar.zst
└── archive/ # Old Velocity-network worlds (kept for history)
| Path | Owner | Purpose |
|---|---|---|
/opt/mcserver/ | minecraft | Live runtime; what systemd executes |
/opt/aow-plugins/ | minecraft | Source for the 6 in-house packs; built in-place with Maven |
/opt/backups/ | root | World snapshots; old Velocity-network and Bedrock worlds archived here |
8Resource use
Day-to-day load is well within the box. With a handful of players online, the Paper main thread sits around 20 TPS (the cap) and the JVM uses 3–5 GB of its 8 GB heap. Spare CPU goes to Geyser’s translation workers and ViaVersion’s packet rewrite.
9Monitoring
Three layers of visibility, in order of how often they get used:
/tps + /mspt tell you instantly if the main thread is stressed. The TAB plugin shows ping & TPS in the tablist.map.aowmc.com; internal map service is not exposed.# Quick health
systemctl status minecraft
journalctl -u minecraft -n 100 --no-pager
htop # CPU + memory at a glance
df -h /opt # Disk free on the world volume
10Backups
World snapshots are written to /opt/backups/ as tar.zst archives
(zstd compresses fast and well for region files). Old worlds from the previous incarnations of
the box — the Velocity network and the brief native Bedrock period — are archived alongside,
so nothing is ever truly gone.
/opt/mcserver/world*,
start the service. The world layout hasn’t changed since 1.21, so restores are forward-compatible
within the current major.
11The history of this box
VPS3 has worn three hats. Knowing this helps explain a few odd paths and the
archive/ folder under backups:
- Velocity network. Originally a 4-server Java setup — Velocity proxy fronting a lobby, factions, and kit-PvP. Stopped and disabled when scope got refocused.
- Native Bedrock Dedicated Server. Briefly ran the official BDS binary for a Bedrock-only experiment. Stopped — the plugin ecosystem is incompatible with what we wanted.
- Paper crossplay SMP (today). Single Paper process, Geyser + Floodgate + ViaVersion for Bedrock support, 6 consolidated in-house packs on top. This is the current shape.
The 2→4 core / 8→16 GB upgrade happened mid-session during the move to the crossplay architecture, once Geyser + ViaVersion made the CPU work obvious.
12Quick reference
| Question | Answer |
|---|---|
| Where does the server live? | /opt/mcserver/ on a Hostinger KVM 4 VPS |
| Public address? | play.aowmc.com |
| What runs the JVM? | systemd unit minecraft.service as user minecraft |
| Java version? | Temurin OpenJDK 21 |
| Server jar? | PaperMC 1.21.8 |
| Heap? | Fixed 8 GB (-Xms8G -Xmx8G) with Aikar flags + 2 GB swap cushion |
| How to send a console command? | /opt/mcserver/cmd.sh "<command>" (writes to FIFO) |
| How to tail logs? | journalctl -u minecraft -f |
| Where are backups? | /opt/backups/ |
| Where is pack source? | /opt/aow-plugins/<Pack>/ |
13What’s next
See the Roadmap for the full list.