Documentation · Deployment

Deploy Warden on a company subdomain

This guide deploys one Warden instance for a team behind a company hostname such as warden.company.com. It is self-contained: you do not need to install Cortex, Trestle or Watchpost to use it. Warden, Cortex, Trestle and Watchpost can be deployed together on sibling subdomains, and every project also works independently. Sharing a parent domain creates no implicit trust or shared authentication — credentials, cookies, databases, service accounts and authorization remain separate per application. Use one distinct hostname per application (for example warden.company.com rather than company.com/warden), because independent subdomains simplify proxying, cookie scope, origins, upgrades and operational isolation.

Ecosystem map.warden.company.com is the browser IDE and agent, cortex.company.com the coding agent, trestle.company.com the backend platform, and watchpost.company.com the monitoring control plane. Deploy only the projects you need.

Recommended topology

Internet or company network
        |
        v
DNS hostname (warden.company.com)
        |
        v
Caddy or nginx with HTTPS (TLS termination, hostname routing)
        |
        v
127.0.0.1:7332
        |
        v
systemd user service (warden.service)

Warden listens on loopback by default. The reverse proxy owns the externally reachable hostname and TLS termination; Warden itself stays bound to 127.0.0.1 unless your documented security model requires otherwise. Warden is a per-OS-user service — its processes run with the authority of the OS user that owns the unit, so treat that account as privileged.

1. Choose the hostname

Pick a stable internal or public name. warden.company.com is used throughout this guide.

2. Create the DNS record

With a fixed public IP, create an A record (and an AAAA record with the IPv6 address where applicable):

Type: A
Name: warden
Value: 203.0.113.10

When the hostname should point at another hostname instead, use a CNAME:

Type: CNAME
Name: warden
Value: apps.company.com

DNS only maps the hostname. It does not select the application port, provide HTTPS, or secure the service — Caddy or nginx performs hostname routing and TLS termination. Private deployments do not require public DNS: split-horizon DNS, internal DNS and VPN-only hostnames are valid and often preferable.

3. Install the Warden binary

Place the binary at a stable absolute path:

mkdir -p /opt/warden
install -m 0755 warden /opt/warden/warden

or use the official installer, which installs to ~/.local/bin by default: curl -fsSL https://warden-deck.github.io/install.sh | sh. A Downloads directory is disposable and will break the unit; the recorded unit contains an absolute executable path, so moving or deleting it breaks the service until you reinstall.

4. Install the service unit

Keep the listener on loopback and point the service at durable configuration, file root and configuration directory:

warden service install \
  --config /var/lib/warden \
  --host 127.0.0.1 --port 7332 \
  --root /srv/warden-files

The file-management root (--root) bounds Explorer and Editor paths; the PTY terminal deliberately runs with the Warden process user's OS authority and is not sandboxed by that root.

5. Keep the listener on loopback

The --host 127.0.0.1 --port 7332 value keeps Warden private; only the reverse proxy on the same host reaches it. Multi-user authentication is mandatory: every account signs in with a password (optionally TOTP and Google sign-in) and gets server-side sessions; browsers store only an HttpOnly session cookie.

6. Trusted proxy and secure cookies

Behind the proxy, create the durable configuration with proxy trust and secure cookies enabled. Warden reads these from its durable configuration directory, so set them at config creation via environment or edit them in the stored configuration file:

WARDEN_HOST=127.0.0.1 \
WARDEN_PORT=7332 \
WARDEN_TRUST_PROXY=true \
WARDEN_SECURE_COOKIES=true \
warden --config /var/lib/warden

WARDEN_TRUST_PROXY=true makes Warden honour forwarding headers only from the direct loopback proxy peer. WARDEN_SECURE_COOKIES=true marks the session cookie Secure, which is required when serving over HTTPS. The proxy must replace (not blindly forward) browser-supplied X-Forwarded-* headers.

7. Configure Caddy

warden.company.com {
    reverse_proxy 127.0.0.1:7332
}

Caddy forwards WebSocket upgrades (needed for interactive PTY terminals) and streaming responses natively through reverse_proxy. Add forwarded headers explicitly when the backend must see the peer and scheme:

warden.company.com {
    reverse_proxy 127.0.0.1:7332 {
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
}

Caddy's automatic HTTPS requires working public DNS and a reachable ACME challenge path. For private networks, use an internal certificate or your company PKI instead of presenting a development certificate as publicly trusted.

8. Configure nginx as an alternative

server {
    listen 80;
    server_name warden.company.com;
    return 301 https://warden.company.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name warden.company.com;

    ssl_certificate     /etc/letsencrypt/live/warden.company.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/warden.company.com/privkey.pem;

    client_max_body_size 200m;

    location / {
        proxy_pass http://127.0.0.1:7332;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Obtain the certificate with Certbot or your company PKI before starting nginx. Upgrade/Connection headers carry interactive terminal WebSockets; proxy_buffering off keeps streaming terminal and agent output flowing; the long timeouts accommodate long-lived sessions. The body limit covers uploads and archives; raise it only as far as your legitimate upload sizes require.

9. Obtain and verify HTTPS

curl -I https://warden.company.com

Confirm the certificate is issued for the hostname and the TLS handshake completes.

10. Verify the public health endpoint

curl -s https://warden.company.com/api/setup/status

Expect a JSON status object from the public, read-only health endpoint. warden service status reports the same endpoint and exits nonzero when the service is failed or missing.

11. Open the application and complete first-run setup

Open https://warden.company.com, complete setup, and create the first administrator account. Multi-user authentication means every additional account signs in separately; the host GitHub CLI is not shared by default and must be granted per account in System → Access → Accounts.

12. Configure lingering for unattended boot

loginctl show-user "$USER" -p Linger
loginctl enable-linger "$USER"

Warden never enables lingering automatically. Enable it only when the service must run after logout or start before login; it changes what the host runs without a login session.

13. Verify service status and logs

warden service status
warden service logs

status resolves the effective listen address from Warden's durable configuration and reports enabled/running state, PID, version, listen address and a live health check.

14. Troubleshoot

  • DNS — the hostname resolves but nothing loads: confirm the A/AAAA or CNAME points at the proxy host, and use internal DNS for VPN-only names.
  • TLS — certificate warnings: automatic HTTPS needs public DNS and reachable ACME paths; internal deployments need a company PKI/internal CA.
  • Proxy — reverse proxy connects but requests loop or cookies fail: the proxy must replace forwarding headers and WARDEN_TRUST_PROXY must be enabled only when the proxy is the direct loopback peer.
  • Cookies — login works on 127.0.0.1 but not through the proxy: WARDEN_SECURE_COOKIES=true is required over HTTPS and the cookie domain must match warden.company.com.
  • Terminals — terminal sessions disconnect immediately: the proxy must forward WebSocket Upgrade/Connection headers and must not buffer the stream.