No description
  • Python 48.5%
  • CSS 17.8%
  • JavaScript 13.7%
  • Shell 13.2%
  • HTML 6.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-02 18:59:25 +01:00
apache Accidently put palworl rather than palworld. Correcting typos. 2026-08-06 18:51:01 +01:00
config Correcting app manifests detection and allows you to specify manual overide location on install 2026-08-06 20:55:20 +01:00
fail2ban Adding failtoban coverage 2026-08-08 00:27:49 +01:00
nginx Initial committing of palworld dashboard 2026-08-06 18:31:49 +01:00
public Improve desktop dashboard rendering performance 2026-08-13 11:53:13 +01:00
runtime Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00
scripts Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00
sudoers Initial committing of palworld dashboard 2026-08-06 18:31:49 +01:00
systemd Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00
tests Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00
.gitignore Release Palworld Dashboard 1.0.0 2026-08-16 03:18:59 +01:00
CHANGELOG.md Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00
README.md Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00
VERSION Add supervised Palworld screen service 2026-09-02 18:56:22 +01:00

Palworld Operations dashboard

A private dashboard for an existing Palworld dedicated server. It controls only the approved GNU screen session or systemd service owned by the configured service account; the web application cannot execute arbitrary shell commands.

Current release: 1.1.0

The package is preconfigured for:

Dashboard URL:  https://palworld.dwaynelowson.co.uk
Game endpoint:  palworld.dwaynelowson.co.uk:8211
Screen session: palworld
Install path:   /home/palworld/Steam/steamapps/common/PalServer
Steam account:  palworld
Steam app:      2394010
Dashboard port: 127.0.0.1:8767

Features

  • Online state, uptime, Palworld version, Steam build, player count, FPS, frame time, world day, base count, and player roster.
  • Start, restart, stop, update-check, and update buttons.
  • Optional systemd supervision of the named GNU screen session, with automatic crash recovery that still respects intentional dashboard stops.
  • Optional REST-assisted saves and player shutdown warnings.
  • Protected Pal/Saved backup before every installed update.
  • Live Palworld console output and update progress.
  • Original archipelago artwork, layered glass panels, and cyan/coral/leaf accents give the operator interface a more distinctive Palworld-inspired identity without changing any controls.
  • The update surface now uses the same teal glass treatment as the rest of the dashboard, with coral retained only as a restrained highlight.
  • A separate dashboard login with scrypt password hashing, login throttling, signed sessions, CSRF checks, HTTPS-only cookies, and strict browser headers.
  • Fail2ban protection for repeated login failures, sourced directly from the dashboard's systemd journal.
  • A bounded readiness check lets Fail2ban finish creating its control socket before the installer verifies the Palworld jail.
  • Loopback-only dashboard listener behind Apache and the existing wildcard certificate.

What happens to the existing screen session

The default screen control mode does not stop or replace the currently detached 2930696.palworld session. The dashboard finds it by the exact session name palworld and reports it as online.

The first dashboard stop or restart sends Ctrl-C to that session and waits up to 120 seconds for a clean exit. It deliberately refuses a forced screen quit. Subsequent starts launch this fixed command as the palworld account:

cd /home/palworld/Steam/steamapps/common/PalServer
screen -dmS palworld -L -Logfile Pal/Saved/Logs/palworld-screen.log ./PalServer.sh

Screen writes the server's console output to Pal/Saved/Logs/palworld-screen.log, but it is not involved when the dashboard reads that file. The root-owned helper drops to the palworld account and runs tail -F directly against the configured path.

If the server currently uses required launch arguments, pass them once with --launch-arguments '...'. They are stored only in the root-owned control configuration and are never accepted from the browser.

Optional crash supervision

Use --control-mode supervised-screen when systemd should own the screen server and restart Palworld after an unexpected exit. This mode starts GNU screen with -D -m, which remains in the foreground until its session ends; systemd can therefore track the real session instead of a launcher that has already exited.

The supervised unit uses Restart=always with a 20-second delay and stops retrying after five failures within five minutes. A dashboard Stop action uses systemctl stop, so an intentional stop remains stopped. The installer does not enable this unit at boot; boot startup remains a separate choice.

The first switch to supervised mode requires the old screen session to be stopped. This prevents the new unit from racing an unmanaged copy:

# Stop the old bootstrap wrapper if it currently owns the session. Otherwise,
# use Stop in the dashboard before continuing.
sudo systemctl disable --now palworld-screen.service
sudo -u palworld screen -ls

sudo bash ./scripts/install.sh \
  --control-mode supervised-screen \
  --public-host palworld.dwaynelowson.co.uk \
  --public-endpoint palworld.dwaynelowson.co.uk:8211

# Or use Start in the dashboard.
sudo systemctl start palworld-dashboard-screen.service

Do not enable palworld-dashboard-screen.service if the server should remain off after a host reboot. If boot startup is wanted later, enable it explicitly with sudo systemctl enable palworld-dashboard-screen.service.

Prerequisites

  • Ubuntu/systemd, Python 3.10+, sudo, GNU screen, SteamCMD, Apache 2, and Fail2ban.
  • PalServer.sh at the configured install path.
  • appmanifest_2394010.acf from the same Steam installation. SteamCMD may put it in the parent steamapps directory or in PalServer/steamapps; the installer detects both layouts.
  • The DNS CNAME for palworld.dwaynelowson.co.uk and the wildcard certificate at /etc/letsencrypt/live/dwaynelowson.co.uk/.
  • Optional: Palworld REST API on port 8212 for live metrics, world saves, and player warnings.

Install

Extract the package on the server, enter its directory, and run:

python3 -m unittest discover -s tests -p 'test_*.py'
bash -n ./scripts/install.sh ./scripts/uninstall.sh

sudo bash ./scripts/install.sh \
  --public-host palworld.dwaynelowson.co.uk \
  --public-endpoint palworld.dwaynelowson.co.uk:8211

The other defaults already match the supplied server layout. The installer locates SteamCMD at /usr/games/steamcmd, /usr/bin/steamcmd, or /usr/local/bin/steamcmd, discovers the matching manifest, verifies app 2394010, and prompts for a new dashboard passphrase. Use --app-manifest only when SteamCMD stored the manifest outside either detected layout.

It also prompts for the Palworld AdminPassword. Leaving it blank keeps basic screen controls and updates working, but live players/FPS/world metrics and REST-assisted saves/warnings are unavailable. For a non-interactive install, put each secret in a root-readable file and use:

sudo bash ./scripts/install.sh \
  --dashboard-password-file /root/palworld-dashboard-password \
  --rest-password-file /root/palworld-admin-password

Do not paste either password into shell history or chat. Re-running the installer upgrades the dashboard and preserves its existing login unless --reset-auth is passed. A successful install or upgrade restarts the dashboard service, so newly deployed interface assets are available immediately.

The installer also validates the existing Fail2ban configuration, installs the palworld-dashboard journal filter and jail, enables Fail2ban at boot, restarts it, and confirms the jail is active. If the new configuration does not validate, the previous service-specific files are restored and installation stops.

The jail bans an address after five failed login requests within ten minutes for one hour. Check it with:

sudo fail2ban-client status palworld-dashboard
sudo journalctl -u palworld-dashboard.service -f -o cat

Apache reverse proxy

The ready-to-use vhost is apache/palworld.dwaynelowson.co.uk.conf. It points at 127.0.0.1:8767 and uses the existing wildcard certificate paths.

sudo cp ./apache/palworld.dwaynelowson.co.uk.conf \
  /etc/apache2/sites-available/palworld.dwaynelowson.co.uk.conf
sudo a2enmod ssl proxy proxy_http headers
sudo a2ensite palworld.dwaynelowson.co.uk.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Only reload Apache after Syntax OK. The /api/events proxy rule has the long timeout needed for the live activity stream.

Optional Palworld REST API

For live game metrics and graceful operations, configure these values in the server's PalWorldSettings.ini and restart Palworld:

RESTAPIEnabled=True
RESTAPIPort=8212
AdminPassword=<strong unique password>

The dashboard calls only http://127.0.0.1:8212/v1/api. Do not publish TCP 8212 through Apache, the router, or a public firewall rule.

All dashboard clients share one REST status sample for ten seconds, so opening the web dashboard and Nexus does not multiply calls to Palworld. The shared log event stream removes blank lines and successful dashboard polling entries for info, metrics, and players; REST failures and administrative operations remain visible. Palworld still records the reduced set of REST audit entries in its raw screen log.

Update behavior

The update button compares the local manifest with Steam app 2394010. When a new build is approved, the root-only updater:

  1. asks Palworld to save and warn players when REST credentials are available;
  2. stops only the configured screen session or supervised service;
  3. copies Pal/Saved to a timestamped, root-only backup under /home/palworld/backups;
  4. runs SteamCMD as palworld with +force_install_dir ... +app_update 2394010 validate;
  5. verifies the installed build ID; and
  6. starts the server again only if it was running before the update.

While this runs, the dashboard follows the one-shot update service through checking, saving, backup, installation, and restart. It waits for a terminal success or failure report and shows a final refresh button instead of assuming that the first SteamCMD response means the update has finished. Reloading the page during an active update resumes the same progress view.

Without REST credentials, startup health is checked through the approved screen session. Backups are never deleted automatically. A failed update keeps its backup path in the update status; it does not attempt an unsupported Steam build rollback.

Operational checks

systemctl status palworld-dashboard.service --no-pager
systemctl status palworld-dashboard-screen.service --no-pager
sudo journalctl -u palworld-dashboard.service -n 100 --no-pager
sudo /usr/local/libexec/palworld-dashboard-control status
sudo -u palworld screen -ls
curl -H 'Host: palworld.dwaynelowson.co.uk' \
  http://127.0.0.1:8767/healthz

In default screen mode, helper status shows "service":"screen:palworld". In supervised mode it shows "service":"palworld-dashboard-screen.service" and includes the exact screen session. A health request with any unexpected Host header is rejected.

Security boundary

The public web process runs as the non-login palworld-dashboard account. Its sudo policy permits a root-owned helper with only eight fixed arguments. The helper revalidates the root-owned paths, account, session name, executables, manifest, log location, and optional fixed launch arguments before every operation.

The browser never supplies a path, session name, service name, or command-line argument. Dashboard and REST secrets remain in /etc/palworld-dashboard.env with mode 0600. Keep the dashboard behind HTTPS and consider the same IP allow-list or VPN policy used by the other dashboards.

Fail2ban trusts only the validated client address appended by the loopback reverse proxy. Do not expose port 8767 directly, and preserve the supplied X-Forwarded-For proxy behaviour if the reverse-proxy configuration changes.

The dashboard systemd service must keep NoNewPrivileges disabled because its only privileged operation is the fixed sudo helper above. Enabling a systemd hardening option that implicitly forces NoNewPrivileges will block every control button before the helper can run.

Files installed

/opt/palworld-dashboard/                 dashboard, updater, and web files
/etc/palworld-dashboard.env              dashboard and REST secrets (0600)
/etc/palworld-dashboard-control.conf     approved screen and paths (0600)
/usr/local/libexec/palworld-dashboard-control
/etc/systemd/system/palworld-dashboard.service
/etc/systemd/system/palworld-dashboard-update.service
/etc/systemd/system/palworld-dashboard-screen.service  supervised mode only
/etc/sudoers.d/palworld-dashboard
/etc/fail2ban/filter.d/palworld-dashboard.conf
/etc/fail2ban/jail.d/palworld-dashboard.local
/var/lib/palworld-dashboard/             cached Steam release state
/home/palworld/backups/                  protected update backups

Uninstall

sudo bash ./scripts/uninstall.sh

The uninstaller removes the dashboard application, its dashboard systemd units, helper, sudo policy, and service-specific Fail2ban filter and jail. It stops the dashboard-owned supervised screen unit when present, while preserving both configuration files, the Steam release cache, Palworld itself, and all backups.

For an explicit complete dashboard purge, including its configuration, secrets, release cache, and service account:

sudo bash ./scripts/uninstall.sh -a
# --all is equivalent

Even the complete dashboard purge never removes the Palworld installation, world saves, an external screen service, or protected update backups.

Production package layout

The downloadable production archive intentionally contains only the dashboard runtime, static interface, installer, service definitions, fixed sudo policy, reverse-proxy examples, configuration examples, tests, and this guide. Preview site tooling and web-build metadata are excluded because the production service never reads or installs them.

The production dashboard does not run, embed, or call an AI service. It is a local Python application controlling the fixed Palworld helper described above.

To build the same server-only archive from a full source checkout:

bash ./scripts/package-production.sh ./palworld-dashboard-production.tar.gz