TL;DR: grab SteamCMD, pull app 2394010, drop a systemd unit, open 8211/udp.

sudo apt install steamcmd
steamcmd +login anonymous +app_update 2394010 validate +quit
sudo systemctl enable --now palworld
sudo ufw allow 8211/udp

Rest of this post is the stuff you wish the Steam page told you.

Prereqs

  • Ubuntu 22.04 or 24.04, x86_64
  • 16 GB RAM recommended. 4 GB is the absolute floor and the server will swap itself into a coma. 8 GB works for 4 players if nobody builds anything
  • 4 CPU cores — Palworld only really uses one of them hard but the engine spawns threads
  • glibc 2.31+ (Ubuntu 22.04 is 2.35, you're fine)
  • Non-root user for the server. Don't run game servers as root

Create the user first:

sudo adduser --system --group --home /opt/palworld palworld

Install SteamCMD

SteamCMD is in the multiverse repo. Enable it, then install:

sudo add-apt-repository multiverse
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install steamcmd

Accept the Steam license when it prompts. You'll only get the prompt once.

Install the Palworld server

The Steam app ID is 2394010. Run SteamCMD as the palworld user so file ownership is right:

sudo -u palworld -H steamcmd \
  +force_install_dir /opt/palworld \
  +login anonymous \
  +app_update 2394010 validate \
  +quit

First run downloads about 3 GB. validate re-checks file hashes — slower but catches corrupted downloads. I always use it.

First run (smoke test)

Before touching systemd, boot the server once by hand so you can see the output:

sudo -u palworld -H bash -c 'cd /opt/palworld && ./PalServer.sh'

If you get error while loading shared libraries: libsteam_api.so, the LD_LIBRARY_PATH isn't set. The PalServer.sh wrapper normally handles this — if it doesn't, your install is incomplete. Re-run SteamCMD with validate.

Once it says Setting breakpad minidump AppID = 2394010 and sits there, it's up. Ctrl+C to kill it, then we'll do it properly with systemd.

systemd unit

Drop this at /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
ExecStart=/opt/palworld/PalServer.sh -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS
Restart=on-failure
RestartSec=10
LimitNOFILE=100000

[Install]
WantedBy=multi-user.target

The three flags on ExecStart are the ones the devs actually recommend for dedicated servers. They do real work — leave them in.

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now palworld
sudo systemctl status palworld
journalctl -u palworld -f

More on systemd units if you want the pattern for other game servers: Writing systemd service unit files.

PalWorldSettings.ini

The settings file lives here after the first boot:

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

There's a default template at /opt/palworld/DefaultPalWorldSettings.ini. Don't edit that one — copy what you need out of it into the LinuxServer copy. Minimal useful overrides:

[/Script/Pal.PalGameWorldSettings]
OptionSettings=(Difficulty=None,DayTimeSpeedRate=1.000000,NightTimeSpeedRate=1.000000,ExpRate=1.500000,PalCaptureRate=1.200000,DeathPenalty=None,ServerPlayerMaxNum=8,ServerName="My Palworld Server",ServerDescription="",AdminPassword="CHANGEME",ServerPassword="",PublicPort=8211,PublicIP="",RCONEnabled=True,RCONPort=25575,bIsMultiplay=True)

Settings that actually matter for ops:

  • AdminPassword — set it. Without it you can't kick anyone or save the world remotely
  • DeathPenalty=None — friends will stop playing if they lose their Pals on death. Trust me
  • ServerPlayerMaxNum — hard cap. Each player is roughly 1 GB of extra RAM
  • ExpRate, PalCaptureRate — tune for your group. 1.5–2.0x makes the early game not miserable
  • RCONEnabled=True — enables the admin console. Keep the port firewalled

After editing, sudo systemctl restart palworld. The server rewrites parts of this file, so edit it with the service stopped.

Firewall

Palworld needs 8211/udp open to the internet. Don't open 25575 (RCON) to the internet. Ever.

sudo ufw allow 8211/udp
sudo ufw status

Full UFW primer if you need it: UFW firewall rules on Ubuntu.

Verify

sudo ss -ulnp | grep 8211
journalctl -u palworld -n 50 --no-pager

From another machine, try joining via the in-game "Join via IP" dialog: SERVER_IP:8211. If the server responds but you time out, 99% of the time it's the firewall — either your server's UFW or the hoster's network ACL above it.

Updates

Palworld updates break save compatibility less than they used to, but they still happen. Updating is just SteamCMD again:

sudo systemctl stop palworld
sudo -u palworld -H steamcmd +force_install_dir /opt/palworld +login anonymous +app_update 2394010 validate +quit
sudo systemctl start palworld

Back up the save directory before every update. Yes, every update.

Backups

The save dir is /opt/palworld/Pal/Saved/SaveGames/. A simple daily rsync to another disk is enough for most groups:

rsync -a --delete /opt/palworld/Pal/Saved/SaveGames/ /backup/palworld/$(date +\%F)/

Full rsync pattern (with retention) is in rsync backups on Linux. Throw the command in a cron job or a systemd timer and forget it.

Gotchas

Server eats all the RAM

It will. Set a hard cap with systemd if the host runs other things:

[Service]
MemoryMax=14G

Drop into a /etc/systemd/system/palworld.service.d/override.conf so you don't lose it on an unattended-upgrades pass.

Save corruption after crash

Palworld sometimes dies mid-save. If players log in to an old world state, the current Players/*.sav files are truncated. Restore from backup. This is why the backup is not optional.

EOS login errors

Palworld uses Epic Online Services. If players get "connection timeout" with no server-side logs, EOS is down or blocked. Nothing to do on your end, wait it out.

Next

If you've done the Hytale server prep this pattern is identical: SteamCMD, systemd unit, config file, firewall. Every Steam-based dedicated server follows this shape — once you've done one, the next ones are just different app IDs.


Related posts