How to Host a Vintage Story Dedicated Server on Linux
Vintage Story ships a proper native Linux dedicated server, which makes it one of the cleaner survival games to self-host. The parts that catch people out are not the download: they are the .NET version the archive actually asks for, the fact that current servers need both TCP and UDP on the same port, and the whitelist default that changed in 1.20. This guide covers all three, plus how to move an existing world without corrupting it.
Skip the setup with managed Vintage Story hosting, mods, files and a live consoleWhat you need
An x86_64 Linux host that stays online, one public port open on both TCP and UDP, and the .NET runtime the server build requires. Upstream's own guidance is roughly 1 GB of base memory plus about 300 MB per player, with four CPU threads recommended. Chunk radius and how far apart players roam drive memory as much as the player count does.
1. Check the runtime the archive actually wants
Do not trust a wiki page here, and do not trust this guide either once a new major release lands. Every server archive carries the answer inside it, in VintagestoryServer.runtimeconfig.json. On the 1.22.6 archive it reads:
{
"runtimeOptions": {
"tfm": "net10.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
}
}
}
That is .NET 10, and the server.sh shipped in the same archive refuses to start without Microsoft.NETCore.App 10.0. On Ubuntu 24.04 the runtime is in the distribution's own package feed, so no extra apt source is needed:
sudo apt-get update
sudo apt-get install -y ca-certificates curl tar dotnet-runtime-10.0
dotnet --list-runtimes | grep 'Microsoft.NETCore.App 10.'
Check this again before every version bump. A future release can change its .NET major version while the download name stays almost identical.
2. Download the stable server, and verify it
The official release feed at api.vintagestory.at/stable-unstable.json lists every build with its CDN URL and an md5. Two entries are marked latest: one stable and one unstable, and the only reliable way to tell them apart is the CDN path. Stable builds live under /gamefiles/stable/.
sudo adduser --disabled-password --gecos "" vintagestory
sudo install -d -o vintagestory -g vintagestory /srv/vintagestory/runtime /srv/vintagestory/data
cd /tmp
VERSION=1.22.6
curl -fL "https://cdn.vintagestory.at/gamefiles/stable/vs_server_linux-x64_${VERSION}.tar.gz" -o vs.tar.gz
# md5 from the linuxserver entry for this version in the release feed
echo "84c59ac1bd4a3ceb7e0b040a0e90428f vs.tar.gz" | md5sum -c -
sudo tar -xzf vs.tar.gz -C /srv/vintagestory/runtime
sudo chown -R vintagestory:vintagestory /srv/vintagestory
The archive extracts flat, so give it its own directory. Keep runtime replaceable and keep your data somewhere else, which the next step sets up.
3. Keep runtime files and your world apart
Vintage Story treats the installation and the data path as separate concepts, and upstream explicitly says an update should replace the installation files rather than extract a new release on top of the old one. Pass the data path on the command line:
cd /srv/vintagestory/runtime
sudo -u vintagestory dotnet VintagestoryServer.dll --dataPath /srv/vintagestory/data
Run it from the runtime directory. The first entry in the server's ModPaths is the relative Mods, which is where the game's own base mods live, so starting from somewhere else means the server cannot find them.
The data path fills with serverconfig.json, Saves, Mods, ModConfig, Playerdata, Backups, Logs and a disposable Cache. That whole tree is what you back up, and what an update must never touch.
4. Let the server write its own config first
This one is worth stating plainly because it wastes an evening otherwise: do not hand-write a minimal serverconfig.json. The default config has around 77 fields, and DefaultRoleCode points at a role called suplayer that lives in nine default Roles entries. A file without them fails immediately:
[Server Fatal] You have configured a default group code suplayer
but no such group exists! Killing server
Start the server once with no config present, let it write the file, stop it, then edit the fields you care about. The ones most people want:
{
"ServerName": "Copper Hollow",
"ServerDescription": "Private survival world",
"Password": "pick-something",
"MaxClients": 8,
"Port": 42420,
"Ip": null,
"Upnp": false,
"AdvertiseServer": false,
"WhitelistMode": 1,
"MaxChunkRadius": 12,
"VerifyPlayerAuth": true
}
Leave Ip null to listen on every interface. Keep VerifyPlayerAuth true so joining players own the game and are logged in. Anything a mod adds to this file should be left alone when you edit it.
5. Open the port on TCP and UDP
The default is 42420. From 1.20 onwards a server needs the same port forwarded on both protocols, and this is the single most common reason a server looks healthy in its own logs while nobody can join. A TCP-only port check will happily go green on a server that no player can actually play on.
sudo ufw allow 42420/tcp
sudo ufw allow 42420/udp
Confirm both listeners locally before blaming the network:
ss -Hltn 'sport = :42420'
ss -Hlun 'sport = :42420'
You can sanity check the TCP side from outside with our game server port checker. A pass there proves TCP only, so check UDP separately.
6. Know what the whitelist default means now
Since game version 1.20, a dedicated server with WhitelistMode on Default is invite-only. Default does not mean off. The values are Default, Off and On, stored as numbers in the order the game declares them, so Default is 0, Off is 1 and On is 2. The clearer way to set it is from the console:
/serverconfig whitelistmode off
/serverconfig whitelistmode on
/whitelist add SomeExactAccountName
Whitelist entries use exact Vintage Story account names. If you keep a whitelist on, add everyone before your group tries to join rather than during.
7. Run it as a service and stop it cleanly
The important part of the unit is the shutdown. A Vintage Story world is a SQLite database, and you want the server to checkpoint and close it rather than be killed mid-write. Send /autosavenow, then /stop, and give it time:
[Unit]
Description=Vintage Story server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=vintagestory
WorkingDirectory=/srv/vintagestory/runtime
ExecStart=/usr/bin/dotnet VintagestoryServer.dll --dataPath /srv/vintagestory/data
Restart=on-failure
TimeoutStopSec=90
[Install]
WantedBy=multi-user.target
A clean stop logs its own trail, which is what you want to see before you trust a restart:
[Server Notification] Server stop requested, begin shutdown sequence.
[Server Event] World saved! Saved 0 chunks, 0 mapchunks, 4 mapregions.
[Server Event] Stopped the server!
The line that means the server is genuinely ready for players is the last one it writes at startup, after chunk pre-generation: Dedicated Server now running on Port 42420 and all ips!. Both sockets are bound well before that, so watch for the line rather than the port.
8. Install mods as ZIP files
Put mod ZIPs in data/Mods and do not unpack them. On the next start the server lists what it loaded:
[Server Notification] Mods, sorted by dependency: game, pei, creative, survival
[Server Event] [pei] started 'Place Every Item' mod (755 ms)
Vintage Story documents that Server and Universal mods can be offered to joining clients automatically, while Client-only mods still have to be installed by each player. Match mod versions to the server version, and take an offline backup before you change a mod set: removing a mod that added world content can damage an established world in ways no restart fixes.
9. Move an existing world without breaking it
A world is a single .vcdbs file in data/Saves, and it is a SQLite database rather than a plain save. Two rules follow from that, and both matter:
- Stop both the source and the destination server before copying. A world copied while something has it open can arrive inconsistent.
- Copy the .vcdbs file only. The
-waland-shmfiles beside it are live database state, not part of your world, and moving one on its own is worse than not moving it at all.
Generate advanced worlds in the client where the world-creation options actually live, then bring the file across, point WorldConfig.SaveFileLocation at it while the server is stopped, and start once to check inventories, claims, spawn and mods before you delete the original. Bring matching Playerdata across only when you need the old server's roles, whitelist, bans or group state.
10. Take backups you can actually restore
The server can write one for you on demand, without pausing:
/genbackup MyBackupName
It lands in data/Backups named exactly what you passed, with no extension added. Omit the name and it generates one from the date and time. Take one before every version bump and before every mod-set change, and keep a copy off the machine, because a backup that lives only on the server that broke is not a backup.
Managed hosting
All of the above is a weekend of setup and then an ongoing job: keeping .NET current, doing update swaps that leave the data path alone, watching both protocols, and remembering to back up before every change. GHosting's Vintage Story hosting does that part, with managed stable updates, panel settings, a writable console, full file access and SFTP, in Europe or North America. Terms are prepaid and nothing renews on its own.
GHosting is an independent hosting provider and is not affiliated with or endorsed by Anego Studios.