All apps · 0 apps
Palisade
Docker app from shakes_63's Repository
Overview
Readme
View on GitHub
Palisade
Formerly ARK Server Manager — it outgrew the name. Old repo links redirect.
Now on Unraid Community Applications — search "Palisade" in the Apps tab.
A self-hosted, Docker-based control panel for game dedicated servers — built Unraid-first, but it runs on any Linux box with Docker. One lean manager container spawns and supervises a container per game server, manages every setting through schema-driven forms, and handles mods, backups, schedules, player administration, and even your router's port-forwards.
Quick start (Unraid)
- Apps tab → search "Palisade" → Install. The defaults are ready to go — the
only field to check is App data: point it at a folder on a real disk
(
/mnt/cache/appdata/palisade), not the/mnt/userFUSE share, so the game-file cache can reflink-clone between servers. - Leave the secrets and
HOST_DATA_DIRblank. The manager generates and persists its own keys on first start and auto-detects the host data path — there's nothing to generate in a terminal. - Open the WebUI, create your admin account on the first-run screen, and add a game server. That's it.
Not on Unraid? It's a single container — run the same image (
ghcr.io/shakes63/palisade) with/var/run/docker.sockand a data volume mounted; see the template inunraid/palisade.xmlfor the full env/mount list.
Supported games (27) — each has a per-game guide covering ports, joining, first boot, and gotchas:
| Game | Runtime | Console | Mods |
|---|---|---|---|
| ARK: Survival Ascended | Proton (POK image) | RCON | CurseForge browser |
| ARK: Survival Evolved | native | RCON | Steam Workshop browser |
| Conan Exiles | native | RCON | Steam Workshop browser |
| Palworld | native | RCON | UE4SS (Linux)/pak uploader [^pal] |
| Palworld (Wine — full mods) | Wine (ripps818) | RCON | UE4SS (Windows) + DLL mods/pak uploader [^palwine] |
| Minecraft (Java) | native (itzg) | RCON | CurseForge modpacks (auto-install) |
| Minecraft Bedrock | native (itzg) | — | add-on pack uploader |
| Icarus | Wine | — | .pak uploader |
| Valheim | native (lloesche) | — | Thunderstore + Hexium browser (auto-deps) |
| 7 Days to Die | native (LinuxGSM) | telnet (in-app) | mod-zip uploader |
| Enshrouded | Proton | — | — (game has no mod support) |
| Project Zomboid | native (Java) | RCON | Steam Workshop browser (auto Mod-ID) |
| V Rising | Wine | RCON (announce) | — (game has no official mod support) |
| Sons of the Forest | Wine | — | — (game has no official mod support) |
| Satisfactory | native | — (HTTPS API: auto-claim) | — (SFTP per upstream docs) |
| Life is Feudal: Your Own | Wine (+ bundled MariaDB) | — (in-game GM password) | — (file-based per upstream docs) |
| American Truck Simulator | native | — | — (optional mods via session host) |
| Euro Truck Simulator 2 | native | — | — (optional mods via session host) |
| Core Keeper | native | — | — (no ports needed: Steam-relay Game ID joins) |
| Terraria (TShock) | native | — (REST-powered counts) | plugin folder (TShock ServerPlugins) |
| Factorio | native | RCON | mods folder (+ mod-portal auto-update) |
| Rust | native | RCON | Oxide/uMod toggle (plugins folder) |
| BeamNG.drive (BeamMP) | native | — | client-mod + Lua plugin folders |
| OpenTTD | native (ich777) | in-game console | — (NewGRFs via in-game content) |
| Counter-Strike 2 | native (joedwards32) | RCON | Workshop maps/collections (by ID) |
| Don't Starve Together | native (jamesits) | — (in-game console) | — (Workshop mods via cluster files) |
| RuneScape: Dragonwilds | native (ferment9348) | — (owner-only in-game) | pak uploader (Nexus Mods) |
[^pal]: Palworld runs the native Linux server, so mods are .pak content mods plus
Lua/Blueprint mods loaded by UE4SS. Official UE4SS releases are Windows-only — there is no
libUE4SS.so there. Use the experimental
native Linux build
(UE4SS_0.0.0.zip) and upload it in the server's Mods tab. DLL-based mods (PalGuard,
PalDefender) cannot load into a Linux process; those require running the Windows server
under Wine, which this image does not do.
[^palwine]: The Palworld (Wine — full mods) variant runs the Windows server under Wine,
so DLL-based mods (PalGuard, PalDefender) load alongside Lua/Blueprint and .pak mods. The
Mods tab installs the official
UE4SS Windows build into
Pal/Binaries/Win64, where Wine auto-loads it via the dwmapi.dll proxy — no LD_PRELOAD.
Heavier and crashier than the native variant; pick it only when you need DLL mods.
Feature highlights
- Create → install → start with per-game readiness detection, graceful shutdown, a crash watchdog, and a RAM guard that offers to back up + swap servers when memory is tight.
- Full per-game settings catalogs with presets, copy-between-servers, and restart-needed tracking.
- Live player counts for every game (A2S / RakNet ping / RCON / Valheim status endpoint) with 1-hour sparklines, plus a Players roster captured from player lists and join logs — kick / ban / whitelist / admin per game.
- Backups: manual + scheduled with restore, browser download, and saves import. Retention is set per server (on its Backups tab; default 10, up to 500) and rotates only the automatic snapshots — scheduled ones and the safety copies taken before an update, restart or restore. Backups you take yourself are kept until you delete them, on the replication target too. The manager also snapshots its own database nightly, with its own retention in Settings → Backups.
- Schedules (restart / update / backup / stop / start) with in-game countdown warnings and pre-action snapshots. Two more actions talk to a live server over RCON: Announce sends an in-game chat message, and Run a console command sends a raw command, the same one you would type in the Console tab. Both skip unless the server is running. Any schedule can also be held to a player count — run only when at most N are online (at most 0 = only on an empty server), or only when at least N are, so an announcement lands when there is someone to read it. The condition can also be required to have held for up to an hour, so a server started a minute ago doesn't count as empty. A schedule can be copied to other servers, and a global schedule on the Schedules page runs on every server, or on the ones you pick.
- Version pinning from registry-populated dropdowns: pin the game version / Steam branch where the server image supports it (Minecraft, OpenTTD, 7DTD, Enshrouded, Valheim, Palworld, V Rising, Satisfactory, ATS/ETS2, LiF:YO), and — for advanced rollbacks — pin the Docker image tag on any game, with a per-game note explaining exactly what each pin does and doesn't control.
- A crashed server shows why it died (exit code / OOM + a log tail) right on its Overview, instead of a bare "Crashed" badge.
- ARK cluster support (shared transfer dir across servers).
- Discord/webhook notifications, host low-disk warnings, editable ports with a start-time port-conflict guard.
- Optional router integration (pfSense, UniFi, or MikroTik RouterOS): one-click WAN port-forward create/fix/enable/disable/delete per server via the router's API.
See PLANNING.md for architecture details.
Architecture at a glance
palisade (this app) ────/var/run/docker.sock──> Docker daemon
Next.js UI + NestJS API │ spawns
SQLite · config engine · RCON · scheduler ▼
one container per game server
The manager is a control plane only — it contains no game runtime. Each game server runs in its own container from a proven community image; the manager injects config, watches logs, and talks RCON/telnet/query protocols.
Installation
Prerequisites
Docker on a Linux host (Unraid, Debian/Ubuntu, etc.). 16 GB+ RAM recommended — a single populated game server wants 2–16 GB depending on the game.
A Docker bridge network,
palisade-net— the manager and the game containers it spawns share it (unless you use host networking). Palisade creates it on demand and attaches itself to it, so there is normally nothing to do — including when the manager runs on an Unraid custom/macvlan network, where it keeps its own IP and simply gains a route to the game containers. Do it yourself only if you setAUTO_CREATE_NETWORK=false, or if Docker access is locked down (a socket-proxy withNETWORKS=0can neither create networks nor attach to them):docker network create palisade-net docker network connect palisade-net <your Palisade container>Installs from before v1.11 use
ark-net. Nothing to do: each server moves across the next time you start it, and Palisade holds both networks until they all have. See Moving offark-net.Two secrets (generate once, keep safe —
SECRETS_KEYencrypts stored API keys/passwords, so losing it means re-entering them):echo "SECRETS_KEY=$(openssl rand -hex 32)" echo "JWT_SECRET=$(openssl rand -hex 32)"For the Wine/Proton games (Icarus, Enshrouded, V Rising, Sons of the Forest, LiF:YO), the host needs a larger mmap limit or the server crashes on boot:
sysctl -w vm.max_map_count=262144 # persist it: /etc/sysctl.conf, or on Unraid append to /boot/config/go
Option A — Unraid (Community Applications) ⭐ recommended
Palisade is in Community Applications: open the
Apps tab, search for Palisade, and install. The template pre-fills
everything except your two secrets (SECRETS_KEY, JWT_SECRET — generators
above) and the app-data path. The prerequisites above still apply: create the
palisade-net network and set the vm.max_map_count sysctl once.
Two Unraid-specific notes baked into the template:
- Use a path on the cache disk itself (
/mnt/cache/appdata/...), not/mnt/user/...— the ARK game-file cache reflink-clones between servers, which needs one real filesystem. - Spawned game servers appear on the Docker page with per-game icons and WebUI buttons that deep-link back into Palisade.
(Manual alternative: drop unraid/palisade.xml into
/boot/config/plugins/dockerMan/templates-user/.)
Option B — plain docker run
docker run -d \
--name palisade \
--network palisade-net \
--restart unless-stopped \
-p 8970:3000 \
-v /opt/palisade:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
--add-host host.docker.internal:host-gateway \
-e NODE_ENV=production \
-e DATA_DIR=/data \
-e DATABASE_URL=file:/data/db.sqlite \
-e HOST_DATA_DIR=/opt/palisade \
-e PUBLIC_BASE_URL=http://YOUR-LAN-IP:8970 \
-e SECRETS_KEY=<64 hex chars> \
-e JWT_SECRET=<random string> \
-e PUID=99 -e PGID=100 \
-e TZ=America/Chicago \
-e GAME_HOST_NETWORK=true \
ghcr.io/shakes63/palisade:latest
Then open http://YOUR-LAN-IP:8970 and complete the first-run wizard
(create the admin account; API keys are optional and can be added later in
Settings).
Option C — docker compose
A reference docker-compose.yml ships in the repo:
export SECRETS_KEY=... JWT_SECRET=... HOST_DATA_DIR=/opt/palisade
docker compose up -d
Environment variables
| Variable | Required | Default | What |
|---|---|---|---|
SECRETS_KEY |
yes | — | 64 hex chars (32 bytes). Encrypts stored secrets at rest. |
JWT_SECRET |
yes | — | Signs login tokens (sessions last 30 days). |
HOST_DATA_DIR ⚙ |
no¹ | auto-detected | The data dir as the host's Docker daemon sees it (e.g. /mnt/cache/appdata/palisade on Unraid). Game-container bind mounts resolve on the host, not inside the manager. Leave it unset: the manager reads it off its own /data mount at boot. |
DATA_DIR |
no | ./data |
Data dir inside the manager container (mount your volume here, conventionally /data). |
DATABASE_URL |
no | file:./data/db.sqlite |
SQLite path — keep it inside DATA_DIR. |
PUBLIC_BASE_URL ⚙ |
no | http://localhost:3000 |
The address you actually browse to. Used for links, Unraid WebUI buttons and the single sign-on redirect URI. |
GAME_HOST_NETWORK ⚙ |
no | false |
true = game containers use host networking (recommended — ASA/EOS and Steam query behave better without Docker NAT). Requires the --add-host host.docker.internal:host-gateway flag on the manager so it can still reach RCON/query. Individual servers can override this on their own General card. |
AUTO_CREATE_NETWORK ⚙ |
no | true |
Let Palisade manage its bridge: create it when a game server needs it, attach itself to it when it isn't already (added live — existing networks and a static IP are kept), and drop the legacy ark-net once nothing uses it. Set false to manage Docker networks yourself. |
SHARED_NETWORK |
no | palisade-net |
Name of that bridge. The default is chosen so Unraid resolves the manager's WebUI link correctly on a custom network (see Moving off ark-net); change it only if you manage the network yourself. |
DOCKER_HOST |
no | unix socket | Point at tcp://socket-proxy:2375 for least-privilege Docker access (docker-socket-proxy). |
PUID / PGID |
no | 99 / 100 |
Ownership for game files written by the manager (Unraid's nobody:users by default). |
TZ |
no | UTC |
Manager clock; also the default for game containers and schedules (overridable in Settings). |
⚙ Also settable in the UI (Settings → General → Host & networking). The
environment variable stays the default; a value saved there overrides it, and
clearing the field hands the decision back to the variable. Anything baked into a
game container — networking, the WebUI base URL — applies the next time that server
starts. HOST_DATA_DIR applies immediately, to the next server started.
¹ Auto-detected at boot from the manager's own /data mount, so leave it blank —
including on Unraid, where the template ships it empty. Only set it if the log says
auto-detection failed, and then it must equal the host path you mapped to /data
("App data" in the Unraid template). A value that disagrees with that mount sends
every game server's files to a directory you didn't choose; the manager warns about
this at boot and in GET /api/health, but it can't override an explicit setting.
Data layout
Everything lives under your data dir — one directory to back up:
/data
├── db.sqlite # server definitions, schedules, players, settings
├── instances/<id>/ # each server's game files + world saves
├── backups/<id>/ # world snapshots (automatic ones rotated per server)
├── backups/_manager/ # nightly self-backups of db.sqlite (retention in Settings)
└── clusters/<id>/ # ARK cluster transfer dirs
Deleting a server from the UI asks whether to wipe its instance + backups, and offers a browser download of the world saves first.
Updating
Releases are versioned: every vX.Y.Z tag builds and publishes
ghcr.io/shakes63/palisade:latest plus matching vX.Y.Z / vX.Y tags (see
Releases for changelogs). To
update: pull the new image and recreate the container (Unraid's update button
does both). Database migrations run automatically on boot. Prefer a fixed
version over latest? Point the template at a specific vX.Y.Z tag — any
published release remains pullable as a rollback pin.
Channels:
| Tag | Moves when | For |
|---|---|---|
latest |
a vX.Y.Z release is cut |
most people |
vX.Y.Z / vX.Y |
that release | pinning / rollback |
nightly |
every merge to main |
early testing, bleeding edge |
sha-<short> |
every build | immutable pin of an exact build |
nightly is a prerelease of unreleased main code (versioned like
1.3.2-nightly.202607110245) — expect rough edges, and note it may apply DB
migrations a later rollback to a stable release can't undo (Prisma migrates
forward only), so back up first. It never moves latest, so stable users
can't see it. Opting in and out is just which tag your container tracks:
point the image at ghcr.io/shakes63/palisade:nightly to ride prereleases,
and back at :latest to rejoin stable at the next release.
Troubleshooting
Player counts stay empty, or RCON says getaddrinfo ENOTFOUND <container name>.
The manager couldn't resolve the game container by name. Games with no usable query
protocol (Palworld, ASA, Minecraft) read their player list over RCON, so this shows
up as "no players" rather than an obvious error.
Palisade works out how to reach each container from the container itself — its IP on
a network you share, the host gateway when it uses host networking or publishes the
port — so this only bites when none of those apply. The usual cause is the manager
not being attached to palisade-net while game servers are. Check GET /api/health:
it reports a warning naming this exact condition. To fix:
docker network connect palisade-net <manager container>
On Unraid, make sure the Palisade container's Network Type is palisade-net
(the template sets it, but it's easy to change). If you use GAME_HOST_NETWORK=true,
the manager instead needs --add-host host.docker.internal:host-gateway.
The connect address on a server's card is wrong. It's the address players type into the game, and Palisade can't see the host's LAN IP from inside a container, so it works it out: the Address players connect to setting (Settings → General) if you set one, then your port-forward target IP, then the public base URL, then whatever address you're browsing Palisade at. That last fallback is the one that goes wrong — a manager on a custom/macvlan network has its own LAN IP, while host-networked game servers answer on the host's, so the panel's address is not the game's. Set the field and every connect card follows it.
Moving off ark-net
The shared bridge was called ark-net before v1.11. The rename is not cosmetic: on
Unraid, a container's WebUI link is built from the first of its networks sorted by
name, so as soon as Palisade attached itself to ark-net it sorted ahead of br0 and
the button on a custom/macvlan install started pointing at the bridge IP instead of the
LAN one. palisade-net sorts after br0, bond0 and eth0, so the link stays right.
It migrates itself, and a half-migrated install works throughout:
- New servers are created on
palisade-net. Existing ones move the next time you start them — Palisade recreates the container on every start anyway. - The manager holds both networks until the last server has moved, then drops
ark-neton its next restart. It never lets go early, and never at all when Docker is reached through a socket-proxy or another container is still on that network — in those cases the Servers page tells you the one step left. - To keep the old name, set
SHARED_NETWORK=ark-net. - An
ark-netyou built yourself as a macvlan or ipvlan network (a custom subnet or VLAN on Unraid) is left alone: Palisade keeps using it and never migrates, exactly as ifSHARED_NETWORK=ark-netwere set.
Moving off ark-manager
The default App data path is /mnt/cache/appdata/palisade. It used to be
/mnt/cache/appdata/ark-manager, left over from when the project itself was called
ark-manager and the folder didn't match the container.
Unlike ark-net, this one does not migrate itself — nothing on disk moves. Only
the defaults changed: the Community Applications template, docker-compose.yml, and
the first-install path in scripts/deploy-unraid.sh. Your container already has its
bind mount, and Unraid keeps your own copy of the template, so an update keeps the path
you have. Staying on ark-manager forever costs nothing.
The one case that bites. Delete the container and reinstall fresh from the Apps tab
and you get the new default, so Palisade comes up empty while your data still sits in
ark-manager. It looks like data loss and isn't — set App data back to your old
directory, or move the data:
- Stop every game server, then stop the Palisade container.
mv /mnt/cache/appdata/ark-manager /mnt/cache/appdata/palisade- Point the container's App data at the new directory and start it.
- Only if you set
HOST_DATA_DIRby hand: clear it (it auto-detects) or update it to the new path. A stale value is loud, not silent — the boot log andGET /api/healthboth reportHOST_DATA_DIR is set to "X", but this container's /data actually comes from "Y".
The database stores container-side paths (/data/backups/...), not host ones, so
backups, snapshots and instances all survive the move, as long as the whole directory
goes together and /data stays the mount target. Game containers get their bind paths
rebuilt from the data dir on every start, so each server picks up the new location the
next time it starts.
Integrations (all optional, all in Settings)
| Integration | What it enables | What you need |
|---|---|---|
| CurseForge API key | ASA mod browser, Minecraft modpack browser | Free key from https://console.curseforge.com/ |
| Steam Web API key | ASE/Conan Workshop browser | Free key from https://steamcommunity.com/dev/apikey |
| Discord webhook | State changes, crashes, backups, schedule events | A channel webhook URL |
| pfSense | Per-server WAN port-forward management (create / fix / enable / disable / delete, WAN IP display) | The free pfSense REST API package on your router + an API key (System → REST API). Works with any pfSense — nothing is network-specific. Test connection checks read and write access (see Checking write access). |
| UniFi | Same port-forward management on a UniFi OS console (Dream Machine, Cloud Gateway, Cloud Key) | An API key from the Network app (Settings → Control Plane → Integrations) on Network 9.0+, created by a full admin, plus the site name (default unless multi-site). Pick UniFi under Settings → Integrations → Port forwarding, then Test connection and Test write access (see Checking write access). |
| MikroTik RouterOS | Same port-forward management on RouterOS 7.1+ | Enable the www-ssl service (IP → Services) and create a dedicated user with the api, rest-api (7.13+), read, and write policies — the built-in read/write groups grant far more than needed, so make a custom group and restrict the user to the Palisade host's source address. REST uses HTTP Basic auth, so enter the user's name and password. The WAN field takes an interface or interface-list name (WAN in the stock config). Test connection reads; Test write access proves write (see Checking write access). |
CurseForge terms: the mod browser uses the CurseForge API read-only to search and display mods; it never downloads or redistributes mod files — the game servers fetch mods themselves through official integrations. Bring your own key; keys are non-transferable under CurseForge's 3rd-party API terms. This repo does not ship one.
Checking router write access
A router API key can pass a connection test and still be unable to change anything: a key inherits the role of the admin who created it, and a view-only key reads rules fine. None of the router APIs has a dry-run mode, and UniFi's port-forward endpoint does not validate its input (an empty body creates an empty rule), so the only honest write check is to make a real change and undo it.
Palisade does that with a disabled rule named
Palisade - write test (safe to delete) on TCP port 65535, pointed at the
target IP. It is created, then deleted by the id the router returned. Being
disabled, it never reaches the live firewall. If the delete fails, the message
says so and names the rule so you can remove it by hand.
- pfSense runs the probe as part of Test connection. The create and delete are never applied on their own, so the running ruleset is untouched. pfSense still flags the config as having pending NAT changes afterwards, so when the box had nothing pending before the probe Palisade applies once more to clear the flag (a reload of an identical ruleset). If something else was already pending, it is left pending for you to apply.
- UniFi keeps the probe behind a separate Test write access button. Each step is a real config change that the console pushes to the gateway (the device's config version changes and changes back). On a UDM running Network 10.6 that did not trigger a full re-provision, but UniFi can reload the firewall on a config push, so run it when a brief reload would be acceptable, not mid-session. A green result means the key can create and delete rules; a red one names the step that failed and, if the delete failed, the rule to remove by hand.
- MikroTik RouterOS also keeps the probe behind Test write access — the REST API applies each write immediately. The probe rule is disabled, so it never reaches the live firewall.
When Fix forwards later reports that the router accepted a change but the ports still are not forwarded, run this check first: it separates a permissions problem from a router-side one. Fix forwards shows the exact rules it will create or re-point — and asks you to confirm — before it writes anything.
Reverse proxy / TLS
The app is LAN-first but proxy-friendly:
- Front it with Nginx Proxy Manager / Traefik / Caddy with TLS; proxy
/,/api, and/socket.ioto the web port (container port 3000) — the web app forwards API + websocket traffic internally. - Set
PUBLIC_BASE_URLto the external origin. - The manager controls Docker via the host socket. If you expose the UI beyond your LAN, strongly consider the socket-proxy setup above.
Users and access
The first-run screen creates the admin account. After that, Settings → Users manages the rest. There are three roles:
- viewer: read-only. Dashboards, players, logs.
- operator: day-to-day ops. Start/stop, console, backups, mods, schedules.
- admin: everything, including settings, users, and deletes.
A viewer or operator can also be limited to specific servers. Tick "Restrict to selected servers" on the user, then pick servers and/or clusters. A cluster grant covers every server in that cluster, including ones added later. Restricted users only see what they were granted, and they cannot create or import servers. Admins always see everything.
Single sign-on (SSO)
Palisade can also sign users in through any OpenID Connect (OIDC) provider, such as Authentik, Keycloak, Authelia or Zitadel. Password sign-in keeps working alongside it. Set it up under Settings → Users → Single sign-on:
At the provider, create a confidential OAuth2/OpenID client and register the redirect URI shown on that card. It is built from the public base URL, so set that first if it isn't the address you open Palisade on. In Authentik, that means an OAuth2/OpenID Provider plus an Application that uses it.
Enter the issuer URL, client ID and client secret. For Authentik the issuer is
https://<authentik>/application/o/<app-slug>/.Optionally, name a provider group for each role. With any group set, the provider decides each SSO user's role at every SSO sign-in, and users in none of the groups are turned away. A change applies at the user's next SSO sign-in, which also signs them out everywhere else; a user who is turned away is signed out too. This only governs SSO sign-ins: a linked user who also has a password can still sign in with it. With none set, you assign roles under Settings → Users. Palisade reads groups from an ID token claim, set under "Groups claim":
Provider Groups claim Authentik, Authelia, Okta groups(the default; leave blank)Keycloak the name set on a "Group Membership" mapper, or realm_access.rolesfor realm rolesZitadel urn:zitadel:iam:org:project:rolesAuth0 the namespaced claim your Action adds, e.g. https://example.com/rolesThe claim must be in the ID token itself; Palisade does not call the userinfo endpoint. Microsoft Entra ID sends group object IDs rather than names, so enter those IDs as the group names.
By default only accounts linked to SSO can sign in with it. Turn on Create
accounts on first SSO sign-in to let anyone the provider lets in get an
account, so limit who may use the app at the provider first. The account is
named from the preferred_username claim, with a random suffix such as
magnus_3fa2c1 if that name is taken. Without groups it starts as a viewer
who sees no servers until an admin grants some.
A link is to one identity at one issuer, so pointing SSO at another provider leaves old links unused rather than matching them there. SSO never takes over an existing account just because the name matches: to use SSO for an account you already have, such as the first-run admin, sign in with its password and choose "Link SSO account" in the account menu at the top right. With groups set, a link that would lower the account's role is refused. The same menu unlinks your account, which takes your password; an admin can unlink anyone else from Settings → Users. An account that SSO created has no password, so it cannot be unlinked; delete it instead. Clear the issuer to turn SSO off, and clear the client ID to also forget the secret.
Two switches on the card make SSO the default way in:
- Hide password sign-in leaves only the SSO button on the login page.
Passwords still work, so this hides the form rather than disabling it: the
form comes back whenever SSO fails (the provider is down, the setup is
wrong, the user is turned away), and
/login?passwordalways shows it. - Sign in with SSO automatically sends the login page straight to the
provider. It stays put after an SSO error, right after you sign out, and at
/login?password, so you never get caught in a loop.
Security
What's built in:
- Auth: JWT auth (bcrypt cost 12) with roles and per-user server access
(see Users and access), 7-day tokens carrying a
version claim checked against the DB on every request —
POST /auth/logout-allinstantly invalidates every outstanding token. Login and first-run are rate-limited (5/min per client). Optional OIDC sign-in uses the authorization code flow with PKCE, state and nonce, and verifies the ID token's signature, issuer and audience. The realtime socket requires the same token; anonymous connections never receive log or console traffic. - API: helmet security headers; CORS denies cross-origin by default
(browsers reach the API same-origin through the web app). Serving the UI
from a different origin needs
CORS_ORIGINS(comma-separated allowlist). - Docker access: least-privilege via
docker-socket-proxy —
see
docker-compose.yml. The manager then only reaches container/image endpoints; exec, volumes, networks, build, and secrets are denied at the proxy. Note the proxy filters endpoints, not payloads: container creation stays possible, so this narrows the blast radius rather than eliminating it. - Game containers run with
no-new-privileges, a pids limit, and RAM caps; console/RCON arguments are sanitized against command injection from player-chosen names. - Supply chain: CI blocks the image build on high/critical production dependency vulnerabilities (pnpm audit) and Trivy-scans the built image for CRITICAL CVEs before pushing; the base image is digest-pinned.
- Backups are verified: the manager's nightly DB snapshot must pass
SQLite's
integrity_check, and world backups record their size and raise a warning event when a snapshot captures no files.
Trade-off to know about: game-server images are pulled by floating tags
(usually :latest, as their maintainers publish them). That's what keeps
game updates one click away, but it means those images change underneath you
outside Palisade's control. The game containers' runtime caps above (plus
per-container RAM limits) bound what a misbehaving image can do — but treat
game images with the same trust you'd give installing that community image
by hand.
Development
pnpm install
# generate secrets, then copy .env.example → .env
node -e "console.log('SECRETS_KEY=' + require('crypto').randomBytes(32).toString('hex'))"
node -e "console.log('JWT_SECRET=' + require('crypto').randomBytes(32).toString('hex'))"
pnpm --filter @ark/api db:push # create the dev SQLite db
pnpm dev # API on :8787 + web on :3000
pnpm --filter @ark/api test # unit tests
Monorepo layout: packages/shared (types + settings catalogs contract),
apps/api (NestJS orchestrator), apps/web (Next.js UI), docker/ (manager
entrypoint), unraid/ (CA template).
Tests
Two tiers:
pnpm test— the fast suite, fakes Docker. Runs on every PR and push tomain.pnpm --filter @ark/api test:docker— the*.docker.test.tstier, against a real Docker daemon. Gated onPALISADE_DOCKER_TESTS=1so it never touches your daemon uninvited. It creates uniquely-named networks and containers, removes them afterwards, and skips theark-netcases if you already have a network by that name. CI runs it on every PR too.
The second tier exists because the fake is where bugs hid: container-id length, Docker's JSON key ordering, and network membership all disagreed with the real daemon in ways that passed 500+ unit tests and were found on a live box.
Acknowledgements
Palisade is a control plane — the actual game servers run on excellent community-maintained images. Huge thanks to their maintainers; this project wouldn't exist without them:
| Game(s) | Image | Maintainer / project |
|---|---|---|
| ARK: Survival Ascended | acekorneya/asa_server |
Acekorneya (POK) |
| Conan Exiles | acekorneya/conan_enhanced_server |
Acekorneya (POK) |
| ARK: Survival Evolved | hermsi/ark-server |
Hermsi1337 |
| Palworld | thijsvanloef/palworld-server-docker |
Thijs van Loef |
| Palworld (Wine — full mods) | ripps818/docker-palworld-dedicated-server-wine |
ripps818 |
| Minecraft (Java) | itzg/minecraft-server |
itzg (Geoff Bourne) |
| Minecraft Bedrock | itzg/minecraft-bedrock-server |
itzg (Geoff Bourne) |
| Icarus | mornedhels/icarus-server |
mornedhels |
| Enshrouded | mornedhels/enshrouded-server |
mornedhels |
| Valheim | lloesche/valheim-server |
lloesche / community-valheim-tools |
| 7 Days to Die | vinanrra/7dtd-server |
vinanrra (built on LinuxGSM) |
| Project Zomboid | danixu86/project-zomboid-dedicated-server |
Danixu |
| V Rising | trueosiris/vrising |
TrueOsiris |
| Sons of the Forest | jammsen/sons-of-the-forest-dedicated-server |
jammsen |
| Satisfactory | wolveix/satisfactory-server |
wolveix |
| Life is Feudal: Your Own | ich777/steamcmd:lifyo |
ich777 |
| American Truck Simulator | ich777/steamcmd:ats |
ich777 |
| Euro Truck Simulator 2 | ich777/steamcmd:ets2 |
ich777 |
| Core Keeper | escaping/core-keeper-dedicated |
escaping.network |
| Terraria | ryshe/terraria |
Ryan Sheehan (built on TShock) |
| Factorio | factoriotools/factorio |
factoriotools |
| Rust | didstopia/rust-server |
Didstopia |
| BeamNG.drive | rouhim/beammp-server |
RouHim (built on BeamMP) |
Also standing on: SteamCMD, GE-Proton/Wine for the Windows-only servers, Thunderstore and Hexium (Valheim mod indexes), and the CurseForge + Steam Web APIs for mod browsing.
License
MIT © 2026 Jacob Neudorf. Not affiliated with Studio Wildcard, Overwolf/CurseForge, Valve, Iron Gate, The Fun Pimps, Keen Games, RocketWerkz, Funcom, Pocketpair, Mojang, or Netgate.
Categories
Related apps
Explore more like this
Explore allDetails
ghcr.io/shakes63/palisade:latestRuntime arguments
- Web UI
http://[IP]:[PORT:3000]/- Network
palisade-net- Privileged
- false
- Extra Params
--add-host host.docker.internal:host-gateway
Template configuration
Port for the web control panel. Open http://your-server-ip:this-port in a browser (the WebUI button uses it too). It maps to the app's internal port 3000 - only change this host number if it is already in use.
- Target
- 3000
- Default
- 8970
- Value
- 8970
Where the manager keeps everything: its database, your settings, and every game server's installed files and saves. Use a path on /mnt/cache (a real disk), not /mnt/user (the FUSE share), so the large game-file cache can reflink-clone between servers. Installs made before this release use /mnt/cache/appdata/ark-manager. Keeping that path is fine and nothing needs to move. If you deleted the container and reinstalled it, and Palisade now looks empty, set this field back to your old folder. The data is still there.
- Target
- /data
- Default
- /mnt/cache/appdata/palisade
- Value
- /mnt/cache/appdata/palisade
Lets the manager create, start, stop and monitor the game-server containers - it controls Docker through this socket. Leave as-is.
- Target
- /var/run/docker.sock
- Default
- /var/run/docker.sock
- Value
- /var/run/docker.sock
The address you actually reach the manager at, e.g. http://10.10.10.10:8970. Used for links and for each game server's WebUI button on the Unraid Docker page. Also settable later in Settings - General, where it overrides this field.
Optional. LEAVE BLANK and the manager generates a strong key on first start and saves it in App data, so it stays stable across restarts - nothing to do. Only set one yourself (openssl rand -hex 32, 64 hex chars) if you want to control it or reuse an App-data folder from another install. It encrypts saved passwords, so if it ever changes, previously saved passwords can't be decrypted.
Optional. LEAVE BLANK - the manager generates and persists one on first start. It signs your login session tokens; changing it just logs everyone out (no data loss). Only set one (openssl rand -hex 24) if you specifically want to.
Run game servers directly on the host network instead of a Docker bridge. true (recommended) gives more reliable ASA/EOS public server listing; false puts them on the palisade-net bridge. This is the starting value only - change it later in Settings - General, or per server on that server's General card.
- Default
- true
- Value
- true
How the manager connects to Docker. unix:///var/run/docker.sock uses the socket mounted above. Only change this if you front Docker with a least-privilege socket-proxy, in which case use tcp://socket-proxy:2375.
- Default
- unix:///var/run/docker.sock
- Value
- unix:///var/run/docker.sock
Let Palisade manage its Docker network by itself: create it when a game server needs it, attach this container to it when it isn't already, and let go of the old 'ark-net' network once no server uses it. true (recommended) means there is nothing to set up by hand - including on a custom network, where Palisade keeps its own IP and just adds a route to the game servers. Set false to manage networks yourself. Also settable later in Settings - General.
- Default
- true
- Value
- true
Optional. LEAVE BLANK. Name of the Docker network the manager and its game servers share; blank means 'palisade-net'. That name matters on Unraid: a container's WebUI link is built from the first of its networks sorted by name, so a network sorting before br0 would take the link with it. Installs made before v1.11 used 'ark-net' and move across on their own as each server is restarted.
Optional. LEAVE BLANK - the manager auto-detects the host-side path of App data from its own /data mount on start. It only needs this to bind-mount data into the game-server containers it spawns. Set it manually only if auto-detection fails (check the log), in which case use the exact App data path above - or fix it in Settings - General without editing this container.
User ID the manager owns its files as. Unraid's default is 99 (the nobody user). Leave as-is unless you specifically need another.
- Default
- 99
- Value
- 99
Group ID for file ownership. Unraid's default is 100 (the users group). Leave as-is.
- Default
- 100
- Value
- 100