🔑 Key Insights
✦ AI·GENA hands-on account of standing up a Palworld dedicated server for friends with Docker on an always-on home box, written as a followable guide. The friends had been on Radmin VPN, but such P2P services fall back to a third-party relay when they can't punch NAT, so the author self-hosts on their 24/7 machine instead. It first busts a myth: hosts' spec pages all say "16GB minimum," but a small group is light day to day, measured around 1.2GB for 3–5 players; what you actually guard against is Palworld's notorious memory leak, pinned by mem_limit plus a daily restart. Then a trimmed docker-compose (remember /udp on the ports, bind RCON to 127.0.0.1) and the UDP connection's three gates: ufw allow udp, router port forwarding (must be UDP), DDNS — plus two misconceptions cleared up: Cloudflare must be grey-cloud (the orange-cloud proxy doesn't forward UDP, enable it and you're locked out), and UDP game packets never touch nginx. The back half covers the world-settings gotcha (editing the ini gets regenerated over), fixing a friend's "version mismatch" (compare buildid, restart to trigger a SteamCMD update, broadcast plus save via RCON first), and not letting 18GB of game files hit your backup drive daily. Ends with a self-hosting checklist.
A friend saw Palworld hit 1.0 and pulled me in. They'd cleared it once two years ago and wanted a fresh run through the big update, and I happened to have some free time.
Honestly my first impression of the game wasn't great — there was that whole "ripped off Pokémon" controversy back then. But it later won the lawsuit, and the 1.0 update really is substantial, so I figured maybe it wasn't so bad and picked it up too.
My friend had everyone connected through Radmin VPN at first. That approach isn't wrong exactly, but it always leaves me with a security itch. Services like Radmin run on P2P: two ends connect directly when they can, and when they can't punch through the other side's NAT or firewall, they fall back to the provider's own . Given my friends' home networks, I doubt every pair punches through, so it'd likely land on the relay. That costs you on two fronts: it's slower (packets take a longer detour), and your game traffic passes through a free, closed-source service's server. It is fully encrypted, sure — but whether to hand my connection to a third party for a round trip is, to me, an unnecessary risk. And since I had a home machine running 24/7 anyway, I jumped in before they got far: let me just stand up a dedicated server, everyone connects straight to my place.
Hence this write-up. Standing up a Palworld dedicated server with Docker isn't hard — docker compose up and it's running in minutes. The hard part is the next step: it's up, but your friends can't get in. Far more people get stuck here than on "how to set it up." This walks the whole path: resource sizing, the docker-compose, the UDP connection that trips everyone up, and how to fix the "your friend says version mismatch, can't join." Every command is runnable; passwords and IPs are placeholders you swap for your own.
First, which image, and how much memory it actually eats#
One thing up front: most people self-host with the official dedicated-server tool (SteamCMD pulling PalServer directly), not necessarily Docker. I go with Docker purely because I'm used to it and it keeps things consistent with the other services on the box; if you also have an always-on server and Docker feels natural, this route is comfortable. The image I use is the community-maintained , which bundles the SteamCMD download, config generation, backups, and RCON.
On memory, first bust a myth: pull up any host's spec page and they all say "16GB minimum, 32GB for comfort" — that number is conservative (they're also selling RAM). In practice a small group is light day to day. On my Linux Docker box with 3–5 players, I measured it sitting around 1.2GB (not long-term monitored, just a casual glance).
So why still care about memory? Because Palworld has a chronic : the process itself slowly bloats over time, unrelated to how much you've built in-game. Community testing shows a 4-player server left running with no restarts can climb from 5G to 8–12G within a week. So the right move isn't provisioning 32G — it's setting a mem_limit to cap it, plus a daily scheduled restart (which frees the memory, deferred if someone's online), keeping the leak in a sane range so it never drags down the other services on the box.
A minimal, workable docker-compose#
The core config is short. Here's a version stripped to the lines that actually matter:
services:
palworld:
image: thijsvanloef/palworld-server-docker:latest
restart: unless-stopped
container_name: palworld
stop_grace_period: 30s # 30s to save after a stop signal
ports:
- "8211:8211/udp" # game connection, UDP (key point, more below)
- "27015:27015/udp" # Steam query, UDP
- "127.0.0.1:25575:25575" # RCON, localhost only, untouchable from outside
environment:
PLAYERS: "16"
SERVER_NAME: "My Palworld"
SERVER_PASSWORD: "<your server password>"
ADMIN_PASSWORD: "<your admin password>"
UPDATE_ON_BOOT: "true" # check for game updates on container start
RCON_ENABLED: "true" # auto backup/restart needs it for graceful shutdown
BACKUP_ENABLED: "true"
AUTO_REBOOT_ENABLED: "true" # daily restart, frees the bloated memory
mem_limit: 16g
volumes:
- ./data:/palworld # game files + saves both live hereA few things that aren't obvious now but bite later:
- Don't drop the
/udpafter8211and27015. Palworld runs on UDP; write it as TCP by mistake and the server starts, you can connect locally, but not a single outsider gets in. - RCON bound to
127.0.0.1: it's the management channel that lets auto backup/restart broadcast a warning and shut down gracefully — localhost only, never exposed. mem_limitandAUTO_REBOOT_ENABLEDare a pair, aimed squarely at the memory bloat above.
The real blocker is networking: three UDP gates#
After docker compose up -d, the server runs on 8211, and connecting to LAN-IP:8211 from the same network usually just works. Then a friend tries, and can't get in.
Because a packet has to travel from the internet to that container in your home, and there are three gates in between — miss one and no one gets through:
First, let the packet reach the host. Open two UDP ports with :
sudo ufw allow 8211/udp comment 'Palworld game'
sudo ufw allow 27015/udp comment 'Palworld query'The protocol must be udp. This is the gate most often mis-written as tcp.
For a packet to reach your home from the internet, the router has to forward it to that host. Add two forwarding rules in the router admin:
| External (WAN) | → Internal host | Protocol |
|---|---|---|
| 8211 | 192.168.x.x : 8211 | UDP |
| 27015 | 192.168.x.x : 27015 | UDP |
A home network's public IP usually changes, so hang a on it, so friends memorize one fixed address instead of asking you for the IP each time. If you manage the domain on Cloudflare, there's a fatal detail in the next section.
WARNING
On Cloudflare, use the "grey cloud" (DNS only), not the "orange cloud" (Proxied) Cloudflare's orange-cloud proxy only forwards HTTP/HTTPS traffic — it does not forward arbitrary UDP packets. Set your Palworld domain to orange cloud and your friends are 100% locked out, and it fails silently (web-type services work fine; only the game won't connect, hard to trace back to here). It must be grey cloud, so the domain resolves straight to your home IP.
Two frequent misconceptions, cleared up before you waste time:
NOTE
No need to touch nginx — not one line
People used to hosting web services reach for a reverse proxy the moment they hear "external." But , while Palworld is UDP game packets going 'router → ufw → container' directly, entirely unrelated to nginx. Not a single line of /etc/nginx changes here.
So does 27015 need to be open? It's the , affecting only "whether your server's status shows up in Steam's server browser." If you want minimal, open just 8211 first; friends connect directly to your-address:8211 and play. Add 27015 later if someone actually wants to see live status from their Steam favorites.
A world-settings gotcha: editing the ini directly gets overwritten#
Once the server's reachable, you'll probably want to tweak the XP rate and such. Here's a guaranteed trap: this image regenerates PalWorldSettings.ini from environment variables on every boot. Edit that ini file by hand and the next container restart overwrites it — your change is as good as gone.
The right way is to put the settings in the environment block of docker-compose.yml, so they're baked in at generation time. The common ones:
Common world settings (put in compose's environment)
| Env var | Effect | Example |
|---|---|---|
EXP_RATE | XP multiplier | 3.0 |
COLLECTION_DROP_RATE | gathering drop multiplier | 3.0 |
DEATH_PENALTY | death penalty | Item (drop items only, keep gear) |
PAL_EGG_DEFAULT_HATCHING_TIME | egg hatch time (hours) | 1 (default 72) |
BASE_CAMP_WORKER_MAX_NUM | base worker Pal cap | 20 |
SERVER_PLAYER_MAX_NUM | player cap | 32 |
PVP / ENABLE_FRIENDLY_FIRE | PvP / friendly fire | true / false |
After editing, docker compose up -d rebuilds the container, regenerates the ini from the new env vars, and the settings take effect. To verify they took, look at the generated ini inside the container (docker exec palworld cat .../PalWorldSettings.ini) and confirm your values before wrapping up.
Your friend says "version mismatch": the server fell behind#
A few days in, a friend reports "can't join, says version mismatch." It's almost always the same cause: their client already auto-updated to a new version, while your server is still on the old one.
How to confirm? Compare the : your local server's buildid against Steam's latest; different means behind.
There's a nasty illusion to bust here: the container log might say container is up to date and you assume you're current — that line refers to the Docker image being latest, not the game files. Game updates are pulled by UPDATE_ON_BOOT running SteamCMD once at container start; if your container has run for several days without a restart, it never had the chance to grab these last few days' version.
The fix is to restart the container, triggering a SteamCMD update:
docker compose up -d --force-recreateBut a restart kicks online players off — don't just do it. The graceful way is to broadcast and save via RCON first, then restart:
TIP
Broadcast + save before updating, don't just kick people mid-session
Send a broadcast via RCON ("server restarting for update in 5 minutes") to online players, trigger a save, then --force-recreate. Kicked players just reconnect once their client finishes updating too (usually automatic, a few minutes); saves are untouched. The update takes a minute or two (~5GB download), after which your local buildid matches your friend's client.
If you don't want to babysit it every time, turn on AUTO_UPDATE_ENABLED and the server checks for updates on a schedule (also broadcast-before-update if someone's online), so a new game version never locks a friend out again.
Don't let 18GB of game files hit your backup drive daily#
One last easily-missed ops trap. If, like me, you have a daily rsync backup of the whole server directory, note this after adding Palworld: that data/ folder holds the entire game install, nearly 18GB, and left unhandled it gets stuffed into your backup drive every day.
The only thing that actually needs backing up is the saves, not those 18GB of game files (which can be re-downloaded anytime). So:
- Exclude
palworld/data(the game install) from the mainrsync - Back up the save directory (
Pal/Saved) separately — that's the part it hurts to lose - Fold the Palworld saves into your existing old-backup cleanup loop (e.g. keep 7 days)
Also check .gitignore has data/ excluded, so 18GB of game files don't wander into version control.
One-page checklist#
The whole path, condensed into a copyable order:
Palworld self-hosting checklist
- Don't be scared by RAM: a small group measures fine at 1–2G (the hosts' 16G is conservative); what you actually guard against is the memory leak, pinned by
mem_limit+ daily restart. - compose: remember
/udpon the ports; bind RCON to127.0.0.1; passwords go in env vars. - Three network gates: ufw allow
udp→ router port forwarding (UDP!) → DDNS. - Cloudflare grey cloud — the orange-cloud proxy doesn't forward UDP; enable it and you're locked out.
- Don't touch nginx — UDP game packets don't go through the reverse proxy.
- World settings in compose's environment, don't hand-edit the ini (it gets regenerated over).
- Friend's version mismatch = server behind; compare buildid, restart to trigger a SteamCMD update, broadcast + save via RCON first.
- Exclude the game install from backups (18GB), back up only the save directory.
- thijsvanloef/palworld-server-docker — the Docker image used hereGitHub
- Palworld official dedicated server guidePalworld Wiki
- Cloudflare — why the proxy (orange cloud) only carries HTTP/HTTPSCloudflare Docs
No comments yet
✨ Be the first to comment