Palworld Dedicated Server on Linux with SteamCMD

Palworld dedicated server on Ubuntu via SteamCMD: native Linux build, systemd unit, the empty PalWorldSettings.ini trap, steamclient.so fix, backups.

Share
Palworld Dedicated Server on Linux with SteamCMD

Palworld ships a native Linux dedicated server, which already puts it ahead of half the survival games I host. No Wine, no Proton. What it doesn't ship is sane defaults: the config file you're supposed to edit starts out empty, and the server quietly ignores your settings if you break one parenthesis.

TL;DR: app ID 2394010, anonymous login, UDP 8211, copy the default settings file before you edit anything.

sudo add-apt-repository multiverse
sudo dpkg --add-architecture i386
sudo apt update && sudo apt install steamcmd
sudo adduser --system --group --home /opt/palworld palworld
sudo -u palworld -H /usr/games/steamcmd +force_install_dir /opt/palworld/server \
  +login anonymous +app_update 2394010 validate +quit
sudo ufw allow 8211/udp

Then the steamclient.so fix, the config, a systemd unit, backups. Same SteamCMD skeleton as Valheim, different traps.

Install details that matter

steamcmd on Ubuntu lives in multiverse and needs i386 libs, hence the first three lines. The installer asks you to accept the Steam license — say yes, otherwise apt just aborts.

Note the full path /usr/games/steamcmd. On Ubuntu sudo uses secure_path, which doesn't include /usr/games, so sudo -u palworld steamcmd can come back with "command not found" even though steamcmd works fine in your own shell. Full path, no guessing.

I install the server into /opt/palworld/server and keep the service user's home at /opt/palworld. SteamCMD dumps its own junk (~/.steam, ~/Steam) into the home directory, and I don't want that mixed with the game files I back up.

If you haven't set up a dedicated service user before, the general pattern is in Linux user management. Short version: the game never runs as root.

The steamclient.so fix

First start, you'll very likely see something like this in the log:

dlopen failed trying to load:
/opt/palworld/.steam/sdk64/steamclient.so

The server looks for the Steam client library in ~/.steam/sdk64/ of the user running it, and SteamCMD doesn't put it there. Pull the Steamworks SDK Redist (app 1007) and copy the lib over:

sudo -u palworld -H /usr/games/steamcmd +force_install_dir /opt/palworld/sdk \
  +login anonymous +app_update 1007 +quit
sudo -u palworld mkdir -p /opt/palworld/.steam/sdk64
sudo -u palworld cp /opt/palworld/sdk/linux64/steamclient.so /opt/palworld/.steam/sdk64/

Some guides tell you to symlink from wherever SteamCMD's own copy ended up. That path differs between the distro package and the tarball install, so I just pull 1007 into a fixed directory and copy. Boring, deterministic.

Config: the empty file trap

After the first start, this exists:

/opt/palworld/server/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini

And it's empty, or nearly. The actual defaults live in DefaultPalWorldSettings.ini in the install root. Editing the default file does nothing — the server reads the one under Pal/Saved/Config/LinuxServer/. So: stop the server, copy, then edit.

sudo systemctl stop palworld
cd /opt/palworld/server
sudo -u palworld cp DefaultPalWorldSettings.ini Pal/Saved/Config/LinuxServer/PalWorldSettings.ini

The file is two lines: a section header and one enormous OptionSettings=(...) line with every setting comma-separated inside the parentheses. It has to stay one line. Break a quote or a paren and the server doesn't complain loudly — it just runs with defaults, and you wonder why your password isn't asked.

I edit individual keys with sed instead of opening the monster line in an editor:

INI=/opt/palworld/server/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini
sudo -u palworld sed -i \
  -e 's/ServerName="[^"]*"/ServerName="My Pals"/' \
  -e 's/ServerPassword="[^"]*"/ServerPassword="changeme"/' \
  -e 's/AdminPassword="[^"]*"/AdminPassword="a-long-random-string"/' \
  -e 's/ServerPlayerMaxNum=[0-9]*/ServerPlayerMaxNum=16/' \
  "$INI"
grep -o 'ServerName="[^"]*"\|ServerPlayerMaxNum=[0-9]*\|PublicPort=[0-9]*' "$INI"

The grep -o at the end is my sanity check that the edits landed and the line is still intact.

Keys worth knowing about:

  • ServerName, ServerDescription — what shows in the list.
  • ServerPassword — join password. Empty = open server.
  • AdminPassword — used for in-game admin login and for RCON / the REST API. Make it long.
  • PublicPort — leave at 8211 unless you run several servers on one IP.
  • RCONEnabled / RCONPort and RESTAPIEnabled / RESTAPIPort — remote admin. More on that below.

Key names have shifted between game versions, so diff your DefaultPalWorldSettings.ini after big updates instead of trusting a three-month-old forum post. Including this one.

The systemd unit

/etc/systemd/system/palworld.service:

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

[Service]
Type=simple
User=palworld
Group=palworld
WorkingDirectory=/opt/palworld/server
ExecStart=/opt/palworld/server/PalServer.sh -port=8211 -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS
Restart=on-failure
RestartSec=15
RuntimeMaxSec=86400
TimeoutStopSec=60

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now palworld
journalctl -u palworld -f

The three performance flags are the ones Palworld's own server docs recommend for multi-core boxes. Leave them in.

RuntimeMaxSec=86400 restarts the server once a day. The Palworld server's memory use grows the longer it runs — that's widely reported and I see the same pattern. A daily restart at a quiet hour beats an OOM kill in the middle of a boss fight. If you want the restart at a fixed time instead of "24h after last start", drop RuntimeMaxSec and use a systemd timer that runs systemctl restart palworld.

RAM: the official docs ask for 16 GB for a dedicated server. Small groups get by with less for a while, but memory climbs with players, Pals and base count. Don't put this on a 4 GB VPS and expect it to last a weekend.

Generic unit-file background is in systemd service unit files.

Ports

sudo ufw allow 8211/udp
# only if you want to appear in the community server list:
sudo ufw allow 27015/udp

8211/UDP is the game. Players connect by IP:port directly. The community list needs the server started with -publiclobby plus the query port reachable — I don't bother, I hand out the IP.

Do not open RCON (25575/TCP) or the REST API (8212/TCP) to the internet. Both are protected by the admin password and nothing else. If you need remote admin, bind access to a VPN — WireGuard or Tailscale — and keep the UFW rules to the game port. UFW basics: UFW firewall rules on Ubuntu.

Saves and backups

World data lives here:

/opt/palworld/server/Pal/Saved/SaveGames/0/<WORLD-ID>/
├── Level.sav
├── LevelMeta.sav
├── Players/
└── backup/

<WORLD-ID> is a long hex string. Which world loads is decided by DedicatedServerName= in Pal/Saved/Config/LinuxServer/GameUserSettings.ini. That's also how you move a world in: drop the folder in, stop the server, point DedicatedServerName at it.

With the REST API enabled (RESTAPIEnabled=True in the ini, listening on 8212 by default), I trigger a save before copying, so the backup isn't a half-written file:

#!/usr/bin/env bash
# /opt/palworld/backup.sh — run as root from a timer
set -euo pipefail
ADMIN_PW='a-long-random-string'
SRC=/opt/palworld/server/Pal/Saved/SaveGames/
DST=/backup/palworld/$(date +%F_%H%M)

curl -fsS -u "admin:${ADMIN_PW}" -X POST http://127.0.0.1:8212/v1/api/save || true
sleep 10
mkdir -p "$DST"
rsync -a "$SRC" "$DST/"
find /backup/palworld -mindepth 1 -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +

The REST API uses HTTP basic auth with user admin and your AdminPassword. If the API is off, the || true lets the backup run anyway — it's still a copy, just less guaranteed consistent. Safest backup is still: stop server, copy, start. I do that before every update. Retention pattern is from rsync backups on Linux.

Updates

Clients can't join a server on an older version, so when the game patches, you patch:

sudo systemctl stop palworld
/opt/palworld/backup.sh
sudo -u palworld -H /usr/games/steamcmd +force_install_dir /opt/palworld/server \
  +login anonymous +app_update 2394010 validate +quit
sudo systemctl start palworld

Updates don't touch Pal/Saved/, so config and world survive. After big updates, check the default ini for new keys — the old file keeps working, new options just stay at their defaults.

Gotcha: settings silently ignored

You edited PalWorldSettings.ini, restarted, nothing changed. Three usual causes, in order of how often I've seen them:

  1. You edited DefaultPalWorldSettings.ini in the install root. Wrong file.
  2. You edited while the server was running and it got overwritten or ignored. Stop first, edit, start.
  3. The OptionSettings=(...) line got broken — a newline from an editor, an unbalanced quote, a missing comma. Run the grep -o check from above; if keys you know exist don't match, the line's damaged. Re-copy from the default and redo the sed edits.

Gotcha: players can't connect

ss -ulpn | grep 8211 on the server — if PalServer is listening, the box side is fine. Then it's the firewall upstream (cloud provider ACL, router port-forward), or players are typing the wrong port. Also check the version: a client one patch ahead of the server gets a generic connection failure, not a helpful "please update" message.

Why self-host it

A Palworld server for a group of friends is one VM, one systemd unit and a backup script. You decide when it updates, you hold the save files, and moving it to a different box is an rsync of Pal/Saved/. Once you've done this and Valheim, Enshrouded is the same evening's work with a Wine layer on top.


Related posts