Concurrency-safe save synchronization for alternating Palworld dedicated-server hosts without keeping one gaming PC online 24/7.
Status:
v2.2.2is the stable Palworld reference release. This repository is in maintenance mode: bug fixes, security updates, dependency maintenance, documentation and Palworld compatibility. The broader multi-game / device-sync product will be developed separately.
Website · Wiki · Docs · Releases · Discussions
Spanish documentation: the repository itself is maintained in English. The Wiki keeps a complete Spanish section alongside the English pages.
Palworld is a trademark of Pocketpair, Inc. This community project is not affiliated with, sponsored by or endorsed by Pocketpair. It does not distribute game files or save content.
A shared ZIP, network folder or cloud directory does not establish a single source of truth. Two hosts can start divergent copies, timestamps can change while files are copied, and a perfectly valid directory may belong to a different world.
Save Sync separates the game server from the authoritative save store:
flowchart LR
A[Host PC A] -->|HTTPS + Bearer| API[Save Sync]
B[Host PC B] -->|HTTPS + Bearer| API
API --> DB[(SQLite WAL)]
API --> FS[(Versioned ZIPs)]
API -. optional backup .-> EXT[(restic / external storage)]
A -->|localhost REST| PA[PalServer]
B -->|localhost REST| PB[PalServer]
The active gaming PC runs PalServer. Save Sync keeps the authoritative version, arbitrates the session lock and rejects stale uploads or saves from another world.
- backend-assigned monotonically increasing integer versions;
- optimistic concurrency through
baseVersion; - exclusive lock with
sessionId, TTL and heartbeat; - adapter-defined save identity; Palworld uses
worldGuid; - server-side SHA-256 verification;
- defensive ZIP validation against path traversal, symlinks, entry floods and ZIP bombs;
- immutable publication before SQLite moves the authoritative pointer;
- restore creates a new version instead of rewriting history;
- per-machine Bearer tokens stored only as hashes;
- Windows DPAPI for client-side secrets;
- configurable per-slot retention;
- durable external-backup queue supervised outside Gunicorn, with timeout, retry and audit trail;
- backup status that reports what is actually known instead of pretending a remote snapshot was checked live.
Save Sync does not merge divergent worlds. If two copies were independently modified, one must be chosen explicitly.
For production, use the same product release for the client and backend. Do not deploy the moving tip of main on a real world.
Requirements:
- Docker Engine + Docker Compose v2;
- HTTPS;
- private filesystem storage;
- Traefik + ForwardAuth/AuthentiK for the private panel, or API-only mode.
git clone --branch v2.2.2 --depth 1 https://github.com/Ayerdi/dedicated-server-save-sync.git
cd dedicated-server-save-sync
config/deploy.sh --init-envReview .env, then deploy:
config/deploy.shTo validate the stack locally without a reverse proxy, domain or PalServer:
bash scripts/local-e2e.sh- Download
dedicated-server-save-sync-client-v2.2.2.zipand its.sha256file from Releases. - Verify the checksum before extracting the archive.
- Copy
client/config.example.jsontoclient/config.json. - Configure the public URL, PalServer path and
Adapter=palworld. - Run
client/Configure-Secrets.cmd. - Run
client/Test-Connection.cmd. - Start sessions through
client/Start-PalworldSync.cmd.
The older Spanish command filenames remain in the package as compatibility aliases, so existing installations and scripts do not break.
PowerShell checksum verification:
$zip = 'dedicated-server-save-sync-client-v2.2.2.zip'
$expected = ((Get-Content "$zip.sha256") -split '\s+')[0].ToLowerInvariant()
$actual = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne $expected) { throw 'Client SHA-256 mismatch.' }Normal session flow:
status → lock → download if needed → start → heartbeat
→ REST save/shutdown → ZIP/SHA-256 → upload
The client verifies the real worldGuid through Palworld's local REST API before starting and before publishing. Do not expose Palworld's REST port through your router.
Read client/README.md and docs/OPERATIONS.md before first production use.
SAVE_SYNC_RETENTION_PER_SLOT limits operational versions per host/slot.
SAVE_SYNC_POST_PUBLISH_COMMAND defines an external backup after a confirmed publication. The web process only queues the work in SQLite; a separate backup-supervisor service executes it, applies the timeout, audits the result and retains the row for retry if the supervisor crashes before recording a final outcome.
A version with a pending backup stays protected from retention. Physical ZIP deletion occurs only after retention metadata is committed and current references are revalidated.
GET /backup-status and the panel distinguish current backup configuration from historical results. latestVersionBackedUp=true means the configured hook reported success for the current version; it does not mean the restic repository was queried live.
Never publish real .env, client configuration/secrets, tokens, passwords, saves, ZIP archives, SQLite databases, complete logs, production paths, GUIDs, IPs, domains or personal names.
CI runs Ruff, pytest with an 85% coverage floor, pip-audit, Docker E2E —including supervisor crash/restart—, Pester and Gitleaks over Git history.
Use SECURITY.md for vulnerabilities. Use GitHub Discussions or issues for non-sensitive support.
The backend retains generic primitives (gameKey, saveIdentity, adapters), and the repository keeps technical documentation for that design. However, the stable product in this repository supports Palworld.
A broader product covering multi-game installations, automatic save discovery, multiple server instances, device-to-device cloud save synchronization and a cross-platform agent is intentionally outside this repository's maintenance scope.
python3 -m venv .venv
. .venv/bin/activate
pip install --require-hashes -r requirements-dev.txt
bash -n scripts/*.sh config/*.sh
ruff check save_sync tests wsgi.py scripts/check-docs.py
python scripts/check-docs.py
python -m pytest -q
pip-audit -r requirements.txt --progress-spinner=off
docker compose config --quiet
bash scripts/run-gitleaks.sh
bash scripts/local-e2e.shWindows client tests:
Import-Module Pester -RequiredVersion 5.9.0
Invoke-Pester -Path .\client -CI- Documentation index
- Architecture and invariants
- HTTP API
- Operations
- Local development
- Migrations
- Multi-game design reference
- Release process
- Publication checklist
- Maintainer guide
- Security
- Support
- Contributing
Code and documentation: Apache License 2.0.