Docs · Deployment

Deploy Watchpost on a company subdomain

This guide deploys one central Watchpost node for a fleet of Watchpost Agents behind a company hostname such as watchpost.company.com. It is self-contained: you do not need to install Cortex, Warden or Trestle to use it. Watchpost, Cortex, Warden and Trestle 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 watchpost.company.com rather than company.com/watchpost), because independent subdomains simplify proxying, cookie scope, origins, upgrades and operational isolation.

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

Recommended topology

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

Central Watchpost is normally the externally reachable fleet endpoint: Agents are expected to deliver telemetry outbound to this hostname. Watchpost listens on loopback by default; the reverse proxy owns the externally reachable hostname and TLS termination.

1. Choose the hostname

Pick a stable internal or public name. watchpost.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: watchpost
Value: 203.0.113.10

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

Type: CNAME
Name: watchpost
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 Watchpost binary

Place the binary at a stable absolute path:

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

The recorded unit contains an absolute executable path; moving or deleting it breaks the service until you reinstall.

4. Install the service unit

Keep the listener on loopback, mark session cookies Secure for HTTPS, and load a protected WATCHPOST_* environment file:

watchpost service install \
  --host 127.0.0.1 --port 7334 \
  --data-dir /var/lib/watchpost \
  --secure-cookies \
  --env-file /etc/watchpost/watchpost.env

The environment file is loaded as an owner-only EnvironmentFile; it must be owned by the service user with restrictive permissions. Central Watchpost is the externally reachable fleet endpoint, so Agents deliver telemetry to https://watchpost.company.com over the same HTTPS listener.

5. Keep the listener on loopback

--host 127.0.0.1 --port 7334 keeps Watchpost private; only the reverse proxy on the same host reaches it. First-administrator setup requires a bootstrap token when the node is externally reachable or when WATCHPOST_SETUP_TOKEN / WATCHPOST_SETUP_TOKEN_FILE is set — the operator supplies the token before creating the first administrator.

6. Trusted proxy and secure cookies

--secure-cookies marks session cookies Secure, which is required over HTTPS. Configure forwarding-header trust in the protected environment file so only the direct loopback proxy peer is trusted (for example WATCHPOST_TRUSTED_PROXIES=127.0.0.1/32). The proxy must replace, not blindly forward, browser-supplied X-Forwarded-* headers.

7. Configure Caddy

watchpost.company.com {
    reverse_proxy 127.0.0.1:7334 {
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}
    }
    request_body {
        max_size 100MB
    }
}

Agents and browsers both send POSTs to this listener; size the request limit for your telemetry payloads and any file uploads, while retaining Watchpost's own per-ingest limits. Caddy's automatic HTTPS requires working public DNS and a reachable ACME challenge path; private networks should use an internal certificate or company PKI.

8. Configure nginx as an alternative

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

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

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

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:7334;
        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_buffering off;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Obtain the certificate with Certbot or your company PKI before starting nginx. The proxy deliberately replaces X-Forwarded-* from $remote_addr rather than trusting a client-supplied header. The long timeouts accommodate long-running checks and large agent deliveries.

9. Obtain and verify HTTPS

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

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

10. Verify the public health endpoint

curl -s https://watchpost.company.com/healthz

Expect a JSON health response. /healthz is the public, read-only liveness endpoint the service health check targets.

11. Open the application and complete first-run setup

Open https://watchpost.company.com and complete first-run setup. When the node is externally reachable or a WATCHPOST_SETUP_TOKEN is configured, provide the bootstrap token before creating the first administrator.

12. Confirm boot enablement

The machine service starts at boot without any login (systemd system unit, WantedBy=multi-user.target); no systemd lingering is required. Confirm enablement structurally:

systemctl is-enabled watchpost.service
grep -q 'WantedBy=multi-user.target' /etc/systemd/system/watchpost.service

Watchpost never enables lingering because it does not use user units.

13. Verify service status and logs

watchpost service status
watchpost service logs

status reports enabled/running state, PID, version, listen address and a live health check of /healthz.

14. Troubleshoot

Watchpost Agent deployment and trust boundary

The Watchpost Agent normally lives on an individual monitored machine and initiates telemetry delivery outbound to central Watchpost. Its administration interface must not be casually exposed to the public Internet as watchpost-agent.company.com: that hostname would represent one Agent, not the fleet, and remote Agent exposure remains experimental.

Normal recommended topology

Agent UI:       127.0.0.1 only
Telemetry:      outbound to https://watchpost.company.com
Remote admin:   SSH tunnel, VPN, or private proxy when deliberately needed

The Agent's UI listens on 127.0.0.1 by default. Do not bind it to a public address as a routine fleet pattern, and never assume that 127.0.0.1 on the Agent host refers to another machine.

Safe remote administration via an SSH tunnel

To reach a single Agent's UI from your workstation without publishing it to the network, tunnel the loopback port:

ssh -L 7335:127.0.0.1:7335 operator@monitored-host

Then open http://127.0.0.1:7335 in your local browser. The Agent UI is never exposed to the network; only the SSH connection carries it. The Agent interface defaults to loopback 127.0.0.1:7335 and is configured with --host/--port or the legacy --listen form.

Advanced remote administration (experimental)

Where remote Agent administration is deliberately required, use a private hostname reachable only through a VPN, private network, authenticated access proxy, or tightly restricted reverse proxy — for example watchpost-agent-hostname.internal.company.com. If the public-style watchpost-agent.company.com example is used at all, label it as an advanced single-Agent deployment, not the ordinary fleet model, and do not imply that one hostname represents all Agents.

Every remote-exposure requirement from the server guide applies, plus: