Install
Kubernetes & Helm
The Helm chart is the canonical install path for production. It packages the control plane Deployment, the gateway workloads, the associated Services and Ingresses, RBAC, secret references, and a PodDisruptionBudget tuned for a three-replica setup.
Add the Helm repo
helm repo add wardengate https://charts.wardengate.io
helm repo update
helm search repo wardengatePin the chart version in CI with --version 2.4.x; floating to the latest minor is fine in lab.
Minimal values.yaml
Start small. Every other knob in the chart has a reasonable default. The example below assumes an external Postgres and Redis already exist — the preferred production shape.
# values.yaml
image:
repository: wardengate/server
tag: "2.4.1"
pullPolicy: IfNotPresent
replicaCount: 3
publicUrl: https://wg.example.com
secretKey:
existingSecret: wg-secret-key
key: secret
externalPostgres:
enabled: true
host: pg.shared.svc.cluster.local
port: 5432
database: wardengate
existingSecret: wg-postgres
usernameKey: username
passwordKey: password
sslMode: require
externalRedis:
enabled: true
url: "redis://redis.shared.svc.cluster.local:6379/3"
persistence:
data:
enabled: true
size: 20Gi
storageClass: standard-rwo
recordings:
enabled: true
size: 100Gi
storageClass: standard-rwo
ingress:
enabled: true
className: nginx
host: wg.example.com
tls:
secretName: wg-tls
proxies:
ssh:
enabled: true
service:
type: LoadBalancer
port: 2222
rdp:
enabled: true
service:
type: LoadBalancer
port: 3389
database:
enabled: true
service:
type: LoadBalancer
port: 4400
identity:
oidc:
enabled: true
issuer: https://corp.okta.example.com
clientId: wardengate
existingSecret: wg-oidc
clientSecretKey: clientSecret
resources:
requests: { cpu: 500m, memory: 1Gi }
limits: { cpu: "2", memory: 4Gi }Create the prerequisite Secrets
kubectl create ns wardengate
kubectl -n wardengate create secret generic wg-secret-key \
--from-literal=secret="$(openssl rand -base64 32)"
kubectl -n wardengate create secret generic wg-postgres \
--from-literal=username=wardengate \
--from-literal=password="$(openssl rand -base64 24)"
kubectl -n wardengate create secret generic wg-oidc \
--from-literal=clientSecret=...Install the chart
helm upgrade --install wardengate wardengate/wardengate \
--namespace wardengate \
--version 2.4.1 \
--values values.yamlThe first install provisions a Job that runs schema migrations, seeds the internal CA, and prints a bootstrap URL in the Job logs. Subsequent helm upgrade runs idempotently.
Verify the install
kubectl -n wardengate get pods,svc,ingress
kubectl -n wardengate logs deploy/wardengate
kubectl -n wardengate logs job/wardengate-migrate
# one-shot CLI against the in-cluster service
kubectl -n wardengate run wgctl --rm -it --restart=Never \
--image=wardengate/wgctl:2.4 \
-- system health --server https://wardengate:8443 --insecureA healthy cluster shows three Running pods, ready Services for each enabled proxy, and an Ingress with an address populated.
StatefulSet or Deployment
The chart renders the control plane as a Deployment by default. The pods are effectively stateless — all durable state lives in Postgres and object storage — so rolling updates and arbitrary pod rescheduling are safe.
Switch to kind: StatefulSet only when you specifically want stable pod identity — for example, to pin each replica to a local PVC for fast recording scratch space on a bare-metal cluster. Set kind: StatefulSet at the top level of values.yaml and the chart will switch workload kinds and emit a headless Service.
Ingress examples
ingress-nginx
ingress:
enabled: true
className: nginx
host: wg.example.com
annotations:
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
tls:
secretName: wg-tlsThe long proxy timeouts matter — the web console uses WebSockets for live session streaming, and a 60-second default will cut RDP viewers in the middle of a session.
Traefik
ingress:
enabled: true
className: traefik
host: wg.example.com
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/service.serversscheme: https
tls:
secretName: wg-tlsFor protocols that are not HTTP (SSH, RDP, database proxy), do not try to route through Ingress. Use a separate LoadBalancer Service per protocol, as shown in the values example above.
Persistence
Two PersistentVolumeClaims are created per pod when persistence is enabled: one for durable data and one for the recording scratch buffer. The data PVC holds internal CA material and host keys — back it up. The recording buffer is transient; sessions drain to object storage continuously.
Size the recording PVC for peak concurrency, not total volume. A host that brokers 200 active sessions with full RDP capture needs roughly 20–40 GB of buffer before the upload pipeline drains.
Scaling
helm upgrade wardengate wardengate/wardengate \
--namespace wardengate \
--reuse-values \
--set replicaCount=5The chart exposes a ready-made HPA template if you want horizontal autoscaling on CPU or on the custom wg_active_sessions metric. Keep at least 3 replicas to survive a node failure without dropping quorum-sensitive background jobs.
Upgrading
Upgrades are rolling. Bump the image tag, run helm upgrade, and the Deployment rolls one pod at a time honouring the PDB. Schema migrations run via a pre-upgrade Helm hook Job.
helm repo update
helm upgrade wardengate wardengate/wardengate \
--namespace wardengate \
--version 2.5.0 \
--reuse-values \
--set image.tag=2.5.0
kubectl -n wardengate rollout status deploy/wardengateIf the migration Job fails, the upgrade is aborted and the old pods keep serving. See Upgrading for rollback guidance.
Uninstall
helm uninstall wardengate -n wardengate
# PVCs and Secrets are kept by design — remove if you want a clean wipe
kubectl -n wardengate delete pvc -l app.kubernetes.io/name=wardengate
kubectl delete ns wardengateExternal Postgres, Redis, and the object store are not touched. Drop the database and bucket manually if you want to wipe state.
Related
- High availability — multi-AZ topology
- Offline install — air-gapped Helm
- Upgrading — version policy and rollback