Palworld Dedicated Server in Docker: a Tested Compose File and Which Image to Use
To run a Palworld dedicated server in Docker, save the compose file below as compose.yaml in an empty folder, change the two passwords and the server name, and run docker compose up -d. The first start downloads the 4.9 GB game server and took 12 minutes 54 seconds to ready in our test; the second start took 9.3 seconds. The file uses the community image thijsvanloef/palworld-server-docker; Pocketpair's official image is compared with it right after the file.
services:
palworld:
image: thijsvanloef/palworld-server-docker:v2.8.0
container_name: palworld-server
restart: unless-stopped
stop_grace_period: 90s
mem_limit: 12g
ports:
- "8211:8211/udp"
- "27015:27015/udp"
- "127.0.0.1:8212:8212/tcp"
- "127.0.0.1:25575:25575/tcp"
environment:
PUID: 1000
PGID: 1000
TZ: "UTC"
PORT: 8211
PLAYERS: 16
SERVER_NAME: "My Palworld Server"
SERVER_DESCRIPTION: "Docker test world"
SERVER_PASSWORD: "change-me-join"
ADMIN_PASSWORD: "change-me-admin"
COMMUNITY: false
CROSSPLAY_PLATFORMS: "(Steam,Xbox,PS5,Mac)"
REST_API_ENABLED: true
REST_API_PORT: 8212
RCON_ENABLED: true
RCON_PORT: 25575
UPDATE_ON_BOOT: true
BACKUP_ENABLED: true
BACKUP_CRON_EXPRESSION: "0 */6 * * *"
DELETE_OLD_BACKUPS: true
OLD_BACKUP_DAYS: 7
EXP_RATE: "1.5"
DEATH_PENALTY: "Item"
volumes:
- ./palworld:/palworld/
How this file was tested. We booted it on 4 October 2026 with Palworld v1.0.5 (the server reported v1.0.5.102999) and image tag v2.8.0, on a Linux workstation with about 45 GB of free RAM, using a Docker-compatible container engine and Docker Compose v2.5.0. For the test every port was bound to 127.0.0.1 on different host port numbers and the container had a different name; nothing else differed. Because the ports were loopback only, we did not test joining from another machine or appearing in the community list. Every number on this page comes from that run unless the sentence says it comes from the image documentation.
Official image or community image
Pocketpair publishes an official image, ghcr.io/pocketpairjp/palserver, with an example compose file in its pocketpairjp/palworld-dedicated-server-docker repository (201 stars, last push 15 September 2026). Both images run the native Linux server, and the build our test server reported, v1.0.5.102999, is the official image's current tag. Where they differ, according to each project's own README and documentation on 4 October 2026:
| What you get | Official: ghcr.io/pocketpairjp/palserver | Community: thijsvanloef/palworld-server-docker |
|---|---|---|
| Game server | Inside the image, one tag per game build (a 5.2 GB pull) | Downloaded by steamcmd on first start, checked for updates on every start |
| Server settings | You edit Saved/Config/LinuxServer/PalWorldSettings.ini; the defaults are in the image at /pal/Package/DefaultPalWorldSettings.ini | Environment variables, written into PalWorldSettings.ini on every start |
| Game updates | Back up, docker compose down, change the tag to the new game version, docker compose up -d | On start, or on a schedule that warns players first |
| Backups and restarts | No built-in feature; the README says to back up before every update | backup and restore commands, scheduled backups, scheduled restarts |
| Admin helpers | None in the image; the server's own REST API and RCON are switched on in the ini file | rest-cli and rcon-cli, Discord webhook messages, pausing the server while nobody is online |
| CPU | x64 only | x64 and ARM64 |
| Example compose file | Publishes 8211/udp, mounts ./Saved and runs a small helper.sh that fixes the folder's ownership; no restart policy and no stop grace period, so Docker's 10 second default applies | Restart policy and a 30 second stop grace period |
Pick the official image if you run an x64 host, want Pocketpair's own package with nothing added, and are happy to edit the ini file and write your own backup job; its README calls the compose file a sample and asks you to verify that saves are written before you rely on it. Pick the community image if you want settings in the compose file, built-in backups, scheduled updates and restarts, or ARM64; it is also the image every command on this page was tested with. Pocketpair also advises against running its image under Docker Desktop on Windows or macOS: disk access there is slow, which its server guide says raises the risk of corrupted saves.
Among community images, thijsvanloef/palworld-server-docker has the most adoption: 3,210 GitHub stars and 1,876,452 Docker Hub pulls on 4 October 2026, against 1,010 and 433,792 for the next one, which has no tagged GitHub releases and last pushed a commit on 1 August 2026. It released 2.7.2 on 15 August, 2.7.3 on 25 August and 2.8.0 on 30 September 2026, with at least 100 commits in the last three months. Its documentation is at the palworld-server-docker documentation site, and every variable on this page was checked against it on the day of the test.
The compose file, line by line
- image: the community image, pinned to a release tag so the image only changes when you edit this line. The game itself updates on start (UPDATE_ON_BOOT below).
- container_name: the name you type in every
docker execanddocker logscommand below. - restart: unless-stopped: brings the server back after a crash or a host reboot, and the image documentation requires it for the scheduled update and restore features.
- stop_grace_period: 90s: how long Docker waits for the save and shutdown before it kills the process. Our stop took 7.1 seconds and the image's own example allows 30. The extra margin costs nothing, because Docker stops waiting as soon as the server exits.
- mem_limit: 12g: a hard memory ceiling. Without it a leaking or overloaded server can push the whole host into swap. Choose the number with the RAM section below.
- 8211/udp: the game port players connect to. This is the one to forward on your router.
- 27015/udp: the Steam query port, needed only if you want the server in the in-game community list.
- 127.0.0.1:8212/tcp: the REST API, bound to the host's loopback so only the machine itself can reach it. The image documentation says plainly not to port forward it.
- 127.0.0.1:25575/tcp: RCON, also loopback only. You will use it through
docker exec, not from the internet. - PUID / PGID: the user and group the server runs as inside the container, so the files in
./palworldbelong to your normal user. Use the numbersidprints for you; 1000 is the usual first user on Linux. - TZ: the timezone used for backup file names and the backup schedule.
- PORT / PLAYERS: the game port and the player cap, the two settings the image docs mark as highly recommended.
- SERVER_NAME / SERVER_DESCRIPTION: what players see in the server list.
- SERVER_PASSWORD: the join password. Keep one if you ever set COMMUNITY to true.
- ADMIN_PASSWORD: the password for in-game admin, RCON and the REST API.
- COMMUNITY: false keeps the server out of the public community list; you join by IP.
- CROSSPLAY_PLATFORMS: which client platforms may connect. See our crossplay guide.
- REST_API_ENABLED / REST_API_PORT: turns on the HTTP admin API. The image's graceful stop, backup and update scripts call this API, so leave it on.
- RCON_ENABLED / RCON_PORT: turns on the classic RCON console; the image's variable table lists it as off by default, so set it explicitly.
- UPDATE_ON_BOOT: checks Steam for a newer game build on every container start and installs it.
- BACKUP_ENABLED / BACKUP_CRON_EXPRESSION: an automatic save archive every six hours instead of the default once a night.
- DELETE_OLD_BACKUPS / OLD_BACKUP_DAYS: prunes archives older than a week so the disk does not fill.
- EXP_RATE / DEATH_PENALTY: two gameplay examples showing how any line of
PalWorldSettings.inibecomes an environment variable. - volumes: everything (game files, world, config, backups) lives in
./palworldnext to the compose file, so it survives container rebuilds and image updates.
First boot: what happens
| Step | Measured |
|---|---|
| Image pull (v2.8.0) | 918 MB, 11 seconds |
| Game server download by steamcmd (app 2394010) | 4,932,070,576 bytes |
docker compose up to "Success! App '2394010' fully installed." | 12 minutes 46 seconds |
| Server start to ready line | 7 seconds |
| Total first boot, up to ready | 12 minutes 54 seconds |
Disk used by ./palworld afterwards | 4.7 GB |
| Second boot (game already installed) | 9.3 seconds to ready |
Most of that time is the download, so it depends on your connection. These are the lines from the end of our first boot:
Success! App '2394010' fully installed. ****GENERATING CONFIG**** Using Env vars to create PalWorldSettings.ini ****Compiling PalWorldSettings.ini**** Compiling PalWorldSettings.ini done! ****GENERATING CRONTAB**** BACKUP_ENABLED=true Adding cronjob for auto backups Cronjobs started ****Starting Server**** /palworld/PalServer.sh -port=8211 -queryport=27015 Setting breakpad minidump AppID = 2394010 [S_API FAIL] Tried to access Steam interface SteamUser021 before SteamAPI_Init succeeded. Game version is v1.0.5.102999 REST API started on port 8212 Running Palworld dedicated server on :8211 REST API(8212) port is open, player logging started
The line that means ready is Running Palworld dedicated server on :8211. The image documentation treats the earlier Setting breakpad minidump AppID = 2394010 as proof the server process is alive, which is true, but the admin API is not answering yet at that point. Wait for the "Running" line before you run commands. The [S_API FAIL] lines are harmless noise; we explain them in S_API FAIL and steamclient errors.
Follow the boot with docker logs -f palworld-server. One oddity we saw: the container printed "New version available: 2.8.0 Mod Support" even though it was already running the 2.8.0 tag. Compare the tag in your compose file with the release list before acting on that message.
RAM: the part that kills most home setups
What our test world used, against what the image documentation and Pocketpair ask for:
| Source | Figure |
|---|---|
| Measured: container memory right after ready, empty fresh world | about 860 MB |
| Measured: same, sampled every 30 seconds for 6 minutes | 859.8 MB to 860.7 MB, flat |
| Measured: resident size of the PalServer process just after boot | about 984 MiB |
| Measured: after a restart of the same world | 894 MB |
| Image documentation: minimum | 16 GB |
| Image documentation: recommended | over 32 GB "for stable operation" |
| Pocketpair's server guide | 16 GB, more than 32 GB recommended; 8 GB boots but makes out of memory crashes more likely |
Under 1 GB is the floor: no players, no bases, no Pals working, nothing loaded beyond the spawn area. It is not what a server uses on day ten of a busy world. Memory grows as players explore and load more of the map, as bases and working Pals pile up, and over long uptimes. The image documentation does not publish a per player figure, and neither does Pocketpair's requirements page. For player count tiers built from running servers, see our performance optimization guide and the server requirements page.
Practical rules for the compose file:
- Size
mem_limitfor the busiest day, not for what the server uses today; for Palworld that means what the machine can spare. On a 16 GB machine, 12 GB leaves room for the operating system; on 32 GB you can go higher. - Do not set a limit near the idle figure. We tried 600 MB on purpose and the kernel killed the server during startup (details in Troubleshooting).
- Schedule restarts. The image has
AUTO_REBOOT_ENABLEDandAUTO_REBOOT_CRON_EXPRESSION, which warn players and restart on a schedule; by default it skips the restart while players are online. Our restart scheduling guide explains why. - Watch it:
docker stats palworld-servershows usage against the limit live.
The settings file and how the variables reach it
Palworld reads one file: /palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini inside the container, which is ./palworld/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini on your disk. It has a single section, [/Script/Pal.PalGameWorldSettings], and every setting sits on one very long line that starts like this in our test:
[/Script/Pal.PalGameWorldSettings] OptionSettings=(Difficulty=None,RandomizerType=None,RandomizerSeed="",bIsRandomizerPalLevelRandom=False,DayTimeSpeedRate=1.000000,NightTimeSpeedRate=1.000000,ExpRate
On every start the image rebuilds that line from your environment variables. The naming rule, from the image documentation: capital letters, an underscore between words, and drop the leading lowercase b of a true or false setting. So PalSpawnNumRate becomes PAL_SPAWN_NUM_RATE and bIsPvP becomes IS_PVP. This is what our variables produced in the generated file:
| Variable | Key it writes in OptionSettings | Value we used | Written as |
|---|---|---|---|
| PLAYERS | ServerPlayerMaxNum | 16 | ServerPlayerMaxNum=16 |
| SERVER_NAME | ServerName | My Palworld Server | ServerName="My Palworld Server" |
| SERVER_DESCRIPTION | ServerDescription | Docker test world | ServerDescription="Docker test world" |
| SERVER_PASSWORD | ServerPassword | change-me-join | ServerPassword="change-me-join" |
| ADMIN_PASSWORD | AdminPassword | change-me-admin | AdminPassword="change-me-admin" |
| PORT | PublicPort (and the -port start argument) | 8211 | PublicPort=8211 |
| CROSSPLAY_PLATFORMS | CrossplayPlatforms | (Steam,Xbox,PS5,Mac) | CrossplayPlatforms=(Steam,Xbox,PS5,Mac) |
| RCON_ENABLED | RCONEnabled | true | RCONEnabled=true |
| RCON_PORT | RCONPort | 25575 | RCONPort=25575 |
| REST_API_ENABLED | RESTAPIEnabled | true | RESTAPIEnabled=true |
| REST_API_PORT | RESTAPIPort | 8212 | RESTAPIPort=8212 |
| EXP_RATE | ExpRate | 1.5 | ExpRate=1.5 |
| DEATH_PENALTY | DeathPenalty | Item | DeathPenalty=Item |
| USE_BACKUP_SAVE_DATA (not set) | bIsUseBackupSaveData | image default | bIsUseBackupSaveData=True |
Three things we confirmed by changing values and restarting:
- Changes need a recreate. Edit the compose file, then
docker compose up -d. Compose recreates the container and the image rewrites the file. A plaindocker restartkeeps the old environment. - Quote decimals. We set
EXP_RATE: 2.0without quotes and the file gotExpRate=2, because YAML turned it into a number first. It works, but quoting ("2.0") keeps exactly what you typed. - Hand edits get overwritten. If you prefer to edit
PalWorldSettings.iniyourself, setDISABLE_GENERATE_SETTINGS: trueand only edit while the server is stopped. Otherwise the variables win at the next start. The image documentation lists this first among its known issues.
For what each Palworld key does in gameplay terms, use our PalWorldSettings.ini guide; the image documentation lists the full variable for every key.
REST API and RCON commands we ran
The image ships two helpers inside the container: rest-cli for the REST API and rcon-cli for RCON. Both read the password from the environment, so you never type it. These are the real outputs from our test world.
$ docker exec palworld-server rest-cli info
{
"version": "v1.0.5.102999",
"servername": "My Palworld Server",
"description": "Docker test world",
"worldguid": "36B882106A4F4EBCAA16940C167F791E"
}
$ docker exec palworld-server rest-cli metrics
{
"currentplayernum": 0,
"serverfps": 59,
"serverfpsaverage": 59.759998321533203,
"serverframetime": 16.732744216918945,
"days": 0,
"maxplayernum": 16,
"basecampnum": 0,
"uptime": 13
}
$ docker exec palworld-server rest-cli players
{
"players": []
}
$ docker exec palworld-server rcon-cli Info
Welcome to Pal Server[v1.0.5.102999] My Palworld Server
$ docker exec palworld-server rcon-cli ShowPlayers
name,playeruid,steamid
$ docker exec palworld-server rcon-cli Save
Complete Save
$ docker exec palworld-server rcon-cli "Broadcast Hello world"
Broadcasted: Hello
The RCON broadcast carried only the first word. The image documentation lists this as a known issue and points to the REST version, which sends the full sentence: docker exec palworld-server rest-cli announce "Hello world". In our test rest-cli announce and rest-cli save printed nothing on success, and the server log confirmed each one with lines such as REST accessed endpoint /v1/api/save OK.
From the host itself you can call the API directly with HTTP basic auth, user admin and your admin password:
curl -u admin:change-me-admin http://127.0.0.1:8212/v1/api/info
Without the password the server answered 401 and logged REST accessed endpoint / Unauthorized. The full command list (kick, ban, unban, shutdown with a countdown) is in our RCON and admin tools page and the admin command list.
Stopping safely, and proof the world survives
Use docker compose stop or docker compose down. The image catches Docker's stop signal, saves through the REST API, asks the server to shut down, and waits up to 30 seconds for the process to exit before it forces it. Our stop took 7.1 seconds end to end and printed:
[LOG] REST accessed endpoint /v1/api/save OK [LOG] REST accessed endpoint /v1/api/shutdown OK REST API stopped Shutdown handler: cleanup. ****Ending Server**** Stopping cronjobs
Between those lines the game also prints a long block of HTTPS connection detail as it reports its session to its built-in error reporting service; it is part of the game, not a problem. The container then shows exit code 143, which is the normal code for a process ended by the stop signal.
We started the container again and asked for the world identity: the same worldguid, 36B882106A4F4EBCAA16940C167F791E, came back, and the world folder in ./palworld/Pal/Saved/SaveGames/0/ kept the same name. We checked again after a recreate with changed settings and after a deliberate out of memory kill: the same world loaded both times.
Two warnings. Never use docker kill or a power cut as a stop button; that skips the save. And if you disable the REST API, the image's stop script cannot save first, so a stop falls back to sending the server process a plain termination signal with no save call in front of it.
Updates
There are two separate things to update.
- The game. With
UPDATE_ON_BOOT: true, every container start asks Steam whether a newer build exists. On our second boot it logged****Checking Installation****thenThe server is up to date!and moved on in under a second. When Palworld patches, adocker compose restartpulls the new build. If you want this done for you, the image'sAUTO_UPDATE_ENABLEDwithAUTO_UPDATE_CRON_EXPRESSIONchecks on a schedule, warns playersAUTO_UPDATE_WARN_MINUTESahead, takes a backup, and restarts. It relies on the restart policy above to come back up. - The image. Change the tag in the compose file to the new release, then run
docker compose pullanddocker compose up -d. Your world is in./palworld, so nothing is lost. The image documentation's own method isdocker compose down --rmi allfollowed bydocker compose up -d.
Before any big Palworld patch, run the backup command from the next section; our restore guide covers getting back.
Backups
The image has a backup command. We ran it on the live server:
$ docker exec palworld-server backup ****Creating backup**** Backup created at /palworld/backups/palworld-save-2026-10-04_16-44-03.tar.gz ****Removing Old Backups**** Removing backups older than 7 days
It saves the world through the REST API first, then packs the whole Pal/Saved folder (the world in SaveGames plus the Config folder with your settings) into a dated archive in ./palworld/backups/. On our fresh world it took 0.7 seconds and the archive was 18,360 bytes; a lived in world is far bigger. The schedule in our compose file runs the same command every six hours.
Separately, the game keeps its own rolling copies because the image sets bIsUseBackupSaveData=True: we found timestamped folders under SaveGames/0/<world id>/backup/world/ after each save.
Both of those live on the same disk as the server. A real backup leaves the machine. The simple version:
docker compose stop
tar -czf palworld-saved-$(date +%F).tar.gz palworld/Pal/Saved
docker compose start
Copy the archive somewhere else. To restore, the image offers an interactive docker exec -it palworld-server restore; done by hand, stop the server, put the SaveGames/0/<world id> folder back, and make sure DedicatedServerName in GameUserSettings.ini matches that folder name, then start again. In our test that file held DedicatedServerName=36B882106A4F4EBCAA16940C167F791E, the same id as the world folder. We did not run the restore command itself. More detail: restore and fix saves.
Troubleshooting
The server died and the log just says "Killed"
That is the out of memory killer. We forced it by lowering mem_limit to 600 MB, below the idle footprint. The log ended like this:
Waiting for REST API(8212) port to open to show player logging... Killed ****Ending Server**** Stopping cronjobs The server may have stalled while starting up.
Confirm it with:
docker inspect palworld-server --format 'oom={{.State.OOMKilled}} exit={{.State.ExitCode}} limit={{.HostConfig.Memory}}'
Ours returned oom=true exit=0 limit=629145600. The exit code was 0, not the 137 you might look for, because the image's wrapper script outlived the killed game process and exited on its own. OOMKilled is the field to trust. The fix is a higher mem_limit, fewer players, scheduled restarts, or more RAM in the machine. If it happens mid session on a long running world, memory growth over time is the usual cause; see the RAM section.
Port already in use
When we started a second container on a host port the server already held, it refused to start:
Error: pasta failed with exit code 1: Failed to bind port 28611 (Address already in use) for option '-u 127.0.0.1/28611-28611:8211-8211'
That wording is from the engine we tested with; Docker Engine phrases it differently, but either way the message names the port. Find what holds it with sudo ss -ulpn | grep 8211 (use -tlpn for the TCP ports 8212 and 25575). Usually it is a second copy of the server or an old container; docker ps -a shows it. To run two servers, give the second one different left side ports and a different PORT.
The server does not show in the community list
We could not test this, since our ports were loopback only. According to the image documentation you need all of: COMMUNITY: true (and a SERVER_PASSWORD, which the docs insist on), port 27015/udp published and forwarded alongside 8211/udp, and your firewall letting both through. If your public port differs from the container's, set PUBLIC_PORT, and PUBLIC_IP if the address is detected wrongly. The docs add that the "breakpad minidump" line means the server is up, so a server that prints it but cannot be reached is a firewall or port forward problem. Players can always join by IP and port without the list.
My settings keep resetting
The variables rewrite PalWorldSettings.ini on every start unless DISABLE_GENERATE_SETTINGS is true; see the settings section.
Health status
The image defines a Docker health check that tests whether the PalServer process is running, with a five minute start period. On Docker Engine, docker ps shows it as healthy or unhealthy. Our test run did not report it, so we have not seen it ourselves.
For log reading in general, our server logs guide and the known issues page cover the messages that are not Docker specific.
When Docker at home is the wrong tool
The container part is easy: we had a working server in under 13 minutes, most of it download. The hard parts are the ones Docker does not solve:
- RAM. If the machine is also your gaming PC, a 16 player world competing with the game client for memory is the classic way both of them stutter and the server gets killed.
- Uptime. Friends in other time zones want the world up when your PC is asleep, updating, or rebooting.
- Your home connection. Port forwarding, a changing home IP, carrier grade NAT on some providers, and upload bandwidth are all on you.
- Patch days. Palworld updates land for everyone at once, and clients cannot join until the server matches. Someone has to be home to restart it.
For a weekend with two or three friends on a spare machine with 16 GB or more, the compose file above is a fine answer. For a world that should be online every day for a bigger group, it usually is not.
Skip the setup: a Palworld server that is just running
If you would rather play than manage containers, memory limits and patch day restarts, a managed server does that part for you, with daily backups and a control panel for your server settings. Get a Palworld dedicated server from Supercraft and have your world online in minutes.