Install
Docker install
A single container is the fastest way to stand Wardengate up on one host for a lab, a pilot, or a small team. This page walks through a standalone docker run deployment with an external Postgres and Redis. For a full stack with databases managed alongside the server, skip ahead to Docker Compose.
Prerequisites
- Docker Engine 24 or later, or a compatible runtime such as Podman 4.6+
- A Linux host matching the tiers in System requirements
- PostgreSQL 14+ reachable from the host, with an empty database owned by a dedicated role
- A Redis or Valkey instance (the default image can embed Valkey for trials; disable for production)
- TLS certificate and key for the public hostname, or an ACME account
Pull the image
Images are published to Docker Hub and to GitHub Container Registry. Both hold identical digests; pick whichever your environment can pull without extra firewall carve-outs.
docker pull wardengate/server:2.4
# or
docker pull ghcr.io/wardengate/server:2.4Pin to a specific minor tag in production. The 2.x tag follows the latest 2-series release and is useful for lab rebuilds but dangerous for anything you care about.
Prepare host directories
Two directories on the host hold durable state and the recording scratch buffer. Create them up front so the container does not end up owning root-owned paths.
sudo mkdir -p /var/lib/wardengate/{data,recordings}
sudo chown -R 10001:10001 /var/lib/wardengate
sudo chmod 750 /var/lib/wardengate/data /var/lib/wardengate/recordingsUID 10001 is the non-root user baked into the image.
Environment variables
Configuration is supplied via environment variables. The list below covers the required set for a first boot; see the CLI reference for the full catalogue.
| Variable | Purpose |
|---|---|
WG_DB_URL | Postgres DSN, for example postgres://wg:...@db:5432/wardengate?sslmode=require |
WG_REDIS_URL | Redis or Valkey URL; omit to use the embedded cache |
WG_SECRET_KEY | 32-byte base64 key used to seal vault material; generate once and keep safe |
WG_PUBLIC_URL | Canonical external URL, for example https://wg.example.com:8443 |
WG_ADMIN_EMAIL | First administrator; receives the bootstrap sign-in link |
WG_TLS_CERT_FILE | Path inside the container to the PEM certificate chain |
WG_TLS_KEY_FILE | Path inside the container to the private key |
Generate the secret key once and treat it like a database password:
openssl rand -base64 32Run the container
The run command below expects TLS material at /etc/wardengate/tls on the host. Adjust paths if you store certificates elsewhere.
docker run -d \
--name wardengate \
--restart unless-stopped \
-p 8443:8443 \
-p 2222:2222 \
-p 3389:3389 \
-p 4400:4400 \
-p 7443:7443 \
-v /var/lib/wardengate/data:/var/lib/wardengate/data \
-v /var/lib/wardengate/recordings:/var/lib/wardengate/recordings \
-v /etc/wardengate/tls:/etc/wardengate/tls:ro \
-e WG_DB_URL="postgres://wg:s3cret@db.internal:5432/wardengate?sslmode=require" \
-e WG_REDIS_URL="redis://cache.internal:6379/0" \
-e WG_SECRET_KEY="$(cat /etc/wardengate/secret.key)" \
-e WG_PUBLIC_URL="https://wg.example.com:8443" \
-e WG_ADMIN_EMAIL="ops@example.com" \
-e WG_TLS_CERT_FILE=/etc/wardengate/tls/fullchain.pem \
-e WG_TLS_KEY_FILE=/etc/wardengate/tls/privkey.pem \
wardengate/server:2.4On first boot the server runs database migrations, emits a CA certificate into the data volume, emails a sign-in link to WG_ADMIN_EMAIL, and begins accepting connections.
Volume layout
Only two paths matter for persistence. Everything else in the container is reconstructible from configuration and state.
/var/lib/wardengate/data— internal CA, host keys, bootstrap state. Back this up./var/lib/wardengate/recordings— scratch buffer that drains to object storage. Safe to lose on restart, but size it for your peak concurrency.
Health check
The container exposes an unauthenticated liveness probe at /healthz and a detailed readiness check at /readyz. The image ships a built-in Docker healthcheck that hits /readyz every 30 seconds.
docker ps --filter name=wardengate
curl -fsS --cacert /etc/wardengate/tls/fullchain.pem \
https://wg.example.com:8443/readyzA healthy readiness response looks roughly like this:
{
"status": "ready",
"version": "2.4.1",
"checks": {
"postgres": "ok",
"redis": "ok",
"tls": "ok",
"clock_skew_ms": 12
}
}Exposing proxy ports
Four proxy ports face the internet or private network: SSH (2222), RDP (3389), database (4400), and the admin/user web UI (8443). If you only use a subset of protocols, remove the corresponding -p flags — disabling an unused port on the host is easier than firewalling it later.
RDP on 3389 is a TCP pass-through; do not attempt to terminate TLS for it at an upstream proxy. The SSH proxy on 2222 speaks OpenSSH wire protocol and authenticates users with short-lived certificates issued by the Wardengate CA.
Upgrading the container
Upgrades are a pull, stop, and run with the new tag. State and configuration live on the host volumes, so the new container picks up where the old one left off. Migrations run automatically during startup of the new version.
docker pull wardengate/server:2.5
docker stop wardengate
docker rm wardengate
# re-run the same docker run command with the new tag
docker run -d --name wardengate ... wardengate/server:2.5For anything larger than a lab, prefer the procedure in Upgrading — pre-flight checks and rollback guidance matter more as you scale.
Uninstall
The container holds no persistent state of its own. Removing it leaves your database, recordings, and secret key intact.
docker stop wardengate
docker rm wardengate
# Optional — destroy persisted state
sudo rm -rf /var/lib/wardengate
# Drop the database and bucket out of bandRelated
- Docker Compose — multi-service stack on one host
- System requirements — sizing and supported platforms
- Upgrading — version policy and rollback