NubleStation

Role

Caddy is the HTTP entry point for the entire NubleStation stack. It:

  • Terminates TLS (with automatic certificate generation via its internal CA)
  • Routes console.{org}.local → Console container
  • Routes api.{org}.local → API Gateway container
  • Serves deployed frontend bundles for all app subdomains (tasks.{org}.local, etc.)

Caddy is the only container that publishes ports 80 and 443 to the host.

Caddyfile

The Caddyfile is generated by the installer from infra/caddy/Caddyfile.template using envsubst:

{
    # Use Caddy's internal CA for LAN HTTPS
    local_certs
    auto_https disable_redirects
}

console.clinic.local {
    reverse_proxy console:3000
}

api.clinic.local {
    reverse_proxy gateway:3000
}

*.clinic.local {
    @app {
        not host console.clinic.local
        not host api.clinic.local
    }
    handle @app {
        root * /var/nuble/{labels.1}
        file_server
    }
}

{labels.1} is a Caddy placeholder that extracts the first DNS label from the subdomain. For tasks.clinic.local it yields tasks, so Caddy serves /var/nuble/tasks/.

Static file serving

The Deploy Service writes frontend bundles to /var/nuble/{appname}/ (a Docker volume shared between the Deploy Service container and Caddy). Caddy reads from this volume directly — no reload, no configuration change. A new deployment is live the instant the files are written.

HTTPS on the LAN

Caddy generates TLS certificates using its built-in CA. This avoids the need for a public CA (which requires a public domain and internet access).

One-time device setup: install Caddy’s CA root certificate on each device. The installer prints the certificate path. Browsers trust *.clinic.local HTTPS after this step.

# On the host, export the root certificate
docker exec nublestation-caddy cat /data/caddy/pki/authorities/local/root.crt > caddy-root.crt

# Then import caddy-root.crt into each device's system trust store
# macOS: Keychain Access → File → Import Items → Trust Always
# Windows: mmc → Certificates snap-in → Trusted Root CAs → Import
# Linux: cp caddy-root.crt /usr/local/share/ca-certificates/ && update-ca-certificates

Modifying the Caddyfile

If you need to add custom headers, enable gzip, or adjust timeouts:

  1. Edit infra/caddy/Caddyfile (or the template if you plan to re-run the installer)
  2. Validate: docker exec nublestation-caddy caddy validate --config /etc/caddy/Caddyfile
  3. Reload: docker exec nublestation-caddy caddy reload --config /etc/caddy/Caddyfile

No restart needed — Caddy reloads its configuration without dropping existing connections.

Logs

docker compose -f infra/docker-compose.yml logs caddy --follow

Caddy logs every request in structured JSON. Filter by subdomain:

docker compose -f infra/docker-compose.yml logs caddy | grep '"request"' | jq '.request.host'