Menu
 

Vintage Story Dedicated Server on Linux (1.22.7): Install, systemd, Docker, Proxmox

Vintage Story Dedicated Server Setup on Linux

Vintage Story ships a separate server build. It is its own download, not the client with a flag, and it is not on Steam, so there is no steamcmd install and no app ID to chase. The server is a .NET application: install a runtime, unpack an archive, point the process at a data directory. Everything below is detail around those steps.

Linux first, because that is where most long-lived Vintage Story servers end up. Current stable at the time of writing is 1.22.7, verified against the official version feed in September 2026.

Server requirements, and what drives them

The .NET runtime is version specific

This is the most common reason a fresh server exits within a second of launching. Each build declares the .NET major version it needs.

Server versionRequired .NET runtimePackage on Debian and Ubuntu
1.22.0 through 1.22.7.NET 10dotnet-runtime-10.0
1.21.x, including 1.21.6 and 1.21.7.NET 8dotnet-runtime-8.0

Every archive contains VintagestoryServer.runtimeconfig.json, and the framework version in it is authoritative for that build:

cat VintagestoryServer.runtimeconfig.json
# "tfm": "net10.0", "version": "10.0.0"  ->  install dotnet-runtime-10.0

Install the runtime, not the SDK; the server never touches the SDK.

RAM, and why the numbers look high

Vintage Story holds every loaded chunk in the .NET managed heap. The server keeps chunks open around each player out to MaxChunkRadius, which defaults to 12, so at that setting each player carries a footprint roughly 25 chunks square. Three things follow:

  • Spread beats headcount. Eight players in one base load a single overlapping chunk set. Eight players in eight bases load eight nearly disjoint sets, and need far more RAM at the same player count.
  • Entities in loaded chunks tick. Large animal pens and dense spawns cost CPU and memory in proportion to how much map is held open.
  • Mods add a fixed floor. Assets load once at startup and stay resident, so a heavy pack can cost a gigabyte before anyone connects.

Know one .NET behaviour before filing a bug report: the garbage collector does not return memory to the operating system promptly, so resident size climbs for a few hours then sits high. Judge health by tick rate, not by resident size.

Figures to allocate for a 1.22 server, not a hard floor:

Players and play styleChunk radiusRAM to allocate
1 to 4, vanilla, one shared base124 GB
1 to 4, vanilla, scattered bases10 to 126 GB
5 to 10, vanilla10 to 128 GB
5 to 10, moderate mods1012 GB
10 to 20, vanilla8 to 1012 to 16 GB
10 to 20, heavy mods820 GB
20 or more, modded6 to 824 GB and up

The 1.22 line added river simulation and a reworked fishing and entity layer, so a world that sat comfortably on 1.21 wants headroom after the upgrade. For a large community, running a large Vintage Story server goes deeper.

CPU and storage

  • CPU: the main simulation tick is effectively single threaded. Worldgen uses other threads, so cores help during exploration, but the tick rate players feel tracks single core clock speed. A fast four core chip beats a slow sixteen core chip here.
  • Storage: worlds are SQLite databases with a .vcdbs extension, and each autosave flushes the dirty chunk set into one. On spinning disks that is a periodic stutter, so use SSD or NVMe. A world of a few hundred hours reaches several gigabytes, and backups multiply it.

Where the server files come from

Builds are published per version, so you can pin one. That matters: client and server must match, and you often want to stay put until players and mods catch up. Three official endpoints:

EndpointWhat it gives you
https://api.vintagestory.at/lateststable.txtCurrent stable version as a bare string, for example 1.22.7. Ideal for a scripted check.
https://api.vintagestory.at/stable.jsonEvery stable version with per platform filenames, sizes, MD5 sums and download URLs.
https://api.vintagestory.at/unstable.jsonThe same for the unstable line.

The Linux server package sits under linuxserver:

https://cdn.vintagestory.at/gamefiles/stable/vs_server_linux-x64_1.22.7.tar.gz

Swap the version to pin any published stable build. The archive is about 51 MB. Downloads and your account live on the official Vintage Story site, and stable.json lists an account mirror per file.

One gotcha that bites everyone once. The archive has no top level directory. It unpacks assets, Lib, Mods and VintagestoryServer.dll straight into the current directory, so create the target directory first and extract with -C, or you scatter thousands of files across your home folder.

Installing on Linux, start to finish

Commands assume Debian 12, run as root.

1. Install the .NET runtime

wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O /tmp/ms-prod.deb
dpkg -i /tmp/ms-prod.deb
rm /tmp/ms-prod.deb
apt-get update
apt-get install -y dotnet-runtime-10.0
dotnet --list-runtimes

On Ubuntu, replace debian/12 with your release path, such as ubuntu/24.04. The last command must print a Microsoft.NETCore.App 10.x line, or nothing below will work.

2. Create a dedicated user and two directories

adduser --system --group --home /opt/vintagestory --shell /usr/sbin/nologin vintagestory
mkdir -p /opt/vintagestory/server
mkdir -p /srv/vintagestory/data

Two directories, deliberately. The binaries directory is disposable and gets replaced on every update. The data directory holds world, mods, player data and configuration, and survives untouched. That split makes upgrades and rollbacks boring.

3. Download and unpack

cd /opt/vintagestory/server
VS_VERSION=1.22.7
wget "https://cdn.vintagestory.at/gamefiles/stable/vs_server_linux-x64_${VS_VERSION}.tar.gz" -O /tmp/vs_server.tar.gz
tar -xzf /tmp/vs_server.tar.gz -C /opt/vintagestory/server
rm /tmp/vs_server.tar.gz
chown -R vintagestory:vintagestory /opt/vintagestory /srv/vintagestory

4. First run, in the foreground

Run it by hand once before writing service files. The entry point is the DLL:

sudo -u vintagestory dotnet /opt/vintagestory/server/VintagestoryServer.dll --dataPath /srv/vintagestory/data

Without --dataPath the server falls back to ~/.config/VintagestoryData for whichever user launched it, which is how people lose worlds they were sure they saved. The useful arguments are few, since most settings live in the config file:

ArgumentPurpose
--dataPathDirectory for world, mods, config, player data and logs. Always set this.
--portListening port. Overrides the config value. Default 42420.
--ipBind address, to pin the server to one interface.
--maxclientsPlayer slot limit. Overrides the config value.

First launch generates the world and pegs a core for a minute or two. Stop it with Ctrl+C, a clean shutdown.

What first boot creates

The data directory is now the whole operating surface of the server:

Path in the data directoryContents
serverconfig.jsonEvery server setting. The file you edit most.
servermagicnumbers.jsonEngine tuning values. Leave alone.
Saves/World databases. A fresh server creates default.vcdbs.
BackupSaves/Automatic backups. Grows steadily, prune on a schedule.
Mods/Server side mods, as zip files.
Playerdata/Per player inventories, positions and roles.
Logs/All server logs.
Cache/ and RiverCache/Regenerable. Safe to delete while stopped.

Settings to change before anyone joins

Stop the server and open serverconfig.json. It is plain JSON, so a trailing comma stops the boot and says so in server-main.log.

KeyWhy it matters on day one
ServerNameWhat appears in the public server list.
PasswordSimplest gate for a private server. Empty means open.
MaxClientsSlot limit. Match it to your RAM, not your hopes.
MaxChunkRadiusServer side view distance, default 12. Strongest lever over RAM and CPU.
PortDefault 42420. Change only for a second server on the host.
IpShips as *, all interfaces. An address binds one NIC.
AutoSaveIntervalSeconds between autosaves, 300 by default. Shorter loses less, stutters more.
AdvertiseServerWhether to appear in the public list.
WhitelistMode, OnlyWhitelistedWhitelist enforcement. Enable on anything advertised.
VerifyPlayerAuthValidates accounts against the auth servers. Keep on.
AllowPvP, AllowCreativeMode, AllowTeleportationSet the social rules before players form expectations.
CorruptionProtectionIntegrity checking on chunk writes. Leave enabled.

World generation settings such as climate, temporal storm frequency and ore density live under WorldConfiguration in the same file, and many only apply to a world that has not been generated yet. The server configuration guide works through the file key by key, and the worldconfig command reference covers what you can change on a live world.

Running it under systemd

Write /etc/systemd/system/vintagestory.service:

[Unit]
Description=Vintage Story Dedicated Server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=vintagestory
Group=vintagestory
WorkingDirectory=/opt/vintagestory/server
ExecStart=/usr/bin/dotnet /opt/vintagestory/server/VintagestoryServer.dll --dataPath /srv/vintagestory/data
Restart=on-failure
RestartSec=15
KillSignal=SIGINT
TimeoutStopSec=180

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now vintagestory

Two lines do the important work. KillSignal=SIGINT makes systemctl stop a clean shutdown rather than a kill: Vintage Story installs one handler for both signals, so SIGINT and SIGTERM take the same shutdown path; the setting that actually decides whether the world is saved is TimeoutStopSec, which must be long enough for the save to finish before systemd escalates to SIGKILL. TimeoutStopSec=180 lets a large world finish that save before systemd escalates.

For an interactive console, run the same command inside screen or tmux. The archive ships an example init script, server.sh, that does this.

Ports

Vintage Story listens on 42420 and needs both TCP and UDP. Opening only TCP is the classic half working setup: the server appears in the list, then connections hang.

ufw allow 42420/tcp
ufw allow 42420/udp
ufw reload

Several servers on one machine need one port and one data directory each: 42420, 42421, 42422. Behind a router, forward both protocols to the machine's LAN address. The full walkthrough, with firewalld, Windows Firewall, dynamic DNS and how to test a port from outside, is on the port configuration page, and the unable to connect guide covers what is left.

VPN and bind address traps. If outside players cannot reach a server you set up over a VPN, check Ip in serverconfig.json. A private VPN address means only VPN and LAN clients will ever connect; reset it to * to bind every interface. If that does not fix it, your ISP is probably using carrier grade NAT, and no amount of port forwarding will help.

Docker

There is no official Vintage Story server image, so you build your own. The server has no native dependencies beyond what the Microsoft runtime image carries.

FROM mcr.microsoft.com/dotnet/runtime:10.0
ARG VS_VERSION=1.22.7
RUN useradd --create-home --uid 1000 vintagestory
RUN mkdir -p /opt/vintagestory /data
ADD https://cdn.vintagestory.at/gamefiles/stable/vs_server_linux-x64_${VS_VERSION}.tar.gz /tmp/vs.tar.gz
RUN tar -xzf /tmp/vs.tar.gz -C /opt/vintagestory
RUN rm /tmp/vs.tar.gz
RUN chown -R vintagestory:vintagestory /opt/vintagestory /data
USER vintagestory
VOLUME ["/data"]
EXPOSE 42420/tcp 42420/udp
STOPSIGNAL SIGINT
ENTRYPOINT ["dotnet", "/opt/vintagestory/VintagestoryServer.dll", "--dataPath", "/data"]
docker build -t vintagestory:1.22.7 .
docker run -d --name vs -p 42420:42420/tcp -p 42420:42420/udp -v /srv/vintagestory/data:/data --stop-timeout 180 vintagestory:1.22.7

Three details separate a working container from one that quietly eats worlds:

  • The data volume is the point. Everything mutable lives under --dataPath, so one host directory at /data keeps the world outside the container lifecycle and a version bump becomes a rebuild plus a restart.
  • Publish UDP as well as TCP. A bare -p 42420:42420 is TCP only, producing the half working symptom above.
  • Get the stop signal right. The half that matters here is --stop-timeout. Docker's default is ten seconds, and a world that needs longer than that to write is killed mid-save. Raise the timeout to cover your save, and match the signal to whatever your entrypoint expects.

The container writes as UID 1000, so the bind mounted host directory must be writable by that UID, or the server cannot create Saves/.

Proxmox

Vintage Story needs no kernel modules, no device passthrough and no privileged capabilities, so an unprivileged LXC container is the right default on Proxmox and costs far less overhead than a full VM. A Debian 12 template, the RAM from the sizing table, two to four vCPU on SSD backed storage, and the Linux steps above run inside it unchanged. What catches people out is memory and snapshots:

  • Turn off ballooning, or set the minimum equal to the target. The .NET heap grows and holds, so the balloon driver sees a guest that never gives memory back and keeps squeezing. The result is swapping inside the guest and stutter with no visible cause.
  • If the host runs ZFS, cap zfs_arc_max. The ARC will otherwise expand into the RAM you meant for the guest.
  • Snapshot with the server stopped. A live snapshot catches the SQLite database mid write. It will often restore fine and occasionally will not, and you find out only when you need it.
  • Keep the data directory on a bind mount or its own dataset, so you can rebuild the container from the template without touching the world.
  • Forward 42420 TCP and UDP through the Proxmox host firewall to the container IP if the container sits on an internal bridge.

Pick a VM over LXC only for hard tenant isolation, or a kernel the host lacks.

Stopping the server without corrupting the world

Chunk data is a live SQLite database, and killing the process mid write is the most reliable way to lose a world. In order of preference:

  1. /stop in the server console. Saves and shuts down in order.
  2. systemctl stop vintagestory, with the KillSignal=SIGINT unit above.
  3. SIGINT to the process, which is what Ctrl+C sends in a foreground session.
  4. kill -9, only when the process is genuinely wedged. Expect to lose everything since the last autosave.

/autosavenow forces an immediate save, which is what you run before maintenance. /announce warns everyone online instead of dropping them without notice. The admin commands reference has the rest.

Before anything risky, stop and copy the world. Saves/default.vcdbs is a single file, so a backup is one cp. The save management guide covers rotation, restoring from BackupSaves/ and worlds that will not load.

Logs

Everything lands in Logs/ inside the data directory:

  • server-main.log: startup, shutdown, joins, leaves, config parse errors. Start here.
  • server-debug.log: verbose engine detail and full exception traces.
  • server-crash.log: written when the server dies from an unhandled exception.
  • server-audit.log: administrative actions, useful after griefing.
  • server-chat.log, server-worldgen.log and server-build.log: chat, generation and build detail.

Under systemd, journalctl -u vintagestory -f streams the same output live, the fastest way to watch a start attempt fail. To read a crash file, see where Vintage Story stores server crash logs. For stutter rather than crashes, see the chunk loading lag fix.

Updating to a new version

The split between binaries and data is what makes updates safe. The shape never changes:

  1. Announce, then stop the server cleanly.
  2. Copy the data directory off the machine.
  3. Replace the binaries directory with the new version, extracted fresh rather than over the old one.
  4. Check whether the new build needs a different .NET major version.
  5. Confirm your mods have releases for the new version before starting back up.
  6. Start, watch server-main.log, and only then tell players it is back.

Extracting over an old version leaves stale files behind, and the server refuses to run rather than mix two builds. Updating a Vintage Story server without losing your world covers rollback too, and since mods are where most upgrades go wrong, read mod related save corruption during an update first.

Mods, clients and Windows

Server side mods are zip files in Mods/, loaded at startup. Anything that changes content rather than just server behaviour must be on every client too, at the same version. Installing mods on a server covers load order and the console install commands. Still weighing options? LAN, VPN or hosted compares them and what a Vintage Story server costs puts numbers on it.

The Windows package is the same application: download it from your account page, extract, and launch VintagestoryServer.exe, which handles the .NET invocation for you. Same runtime rule, same data layout, same port rules, except the default data path sits under %appdata%. Migrating a Vintage Story server from Windows to Linux covers the path and permission differences.

When the server will not start

SymptomMost likely cause
Exits within a second, no log writtenWrong or missing .NET runtime. Compare dotnet --list-runtimes with runtimeconfig.json.
Refuses to run and mentions old filesA new build extracted over an old one. Delete the binaries directory and unpack cleanly.
Stops at startup with a config complaintInvalid JSON in serverconfig.json. The line is named in server-main.log.
Starts, but no world where you expected--dataPath omitted, so it used ~/.config/VintagestoryData.
Cannot create Saves/ or write logsOwnership mismatch. The service user must own the data directory.
Appears in the list, connections hangUDP not open or not forwarded. TCP alone is not enough.
Version mismatch on connectClient and server must match exactly, patch number included.
Killed by the OOM killer under loadAllocation too small for the player spread. Lower MaxChunkRadius or add RAM.

For crashes specific to the current line rather than setup mistakes, 1.22 server stability issues collects the patterns we have seen.

Looking for managed Vintage Story server hosting? Supercraft runs Vintage Story dedicated servers on the current stable build with daily backups, instant setup and four region options, so the runtime version, the port rules and the clean shutdown on this page are already handled.

Launch a Vintage Story server with this setup

Pick a preset and your new server boots preconfigured - rates, rules and mods already dialed in. Change anything later in the panel.

Browse all Vintage Story recipes →

Vintage Story Creative Builder

Peaceful builder preset: creative game-mode allowed, passive creatures, no temporal storms, doubled harvest, …

Vintage Story Hardcore Survival

High-stress survival preset: aggressive creatures, frequent temporal storms, slower harvest acceleration, per…

Tired of fighting this issue every patch?

Run a managed Vintage Story server with us. We handle the patches, mod-version pinning, save backups, and DDoS protection. Set up in minutes, multiple datacenter regions, no contract.

See Vintage Story hosting plans →
Top