# ============================================================================= # nomad/jobs/edge.hcl — Edge proxy (Caddy + dispatcher sidecar) (Nomad service job) # # Part of the Nomad+Vault migration (S5.1, issue #988). Caddy reverse proxy # routes traffic to Forgejo, Woodpecker, staging, and chat services. The # dispatcher sidecar polls disinto-ops for vault actions and dispatches them # via Nomad batch jobs. # # Host_volume contract: # This job mounts caddy-data from nomad/client.hcl. Path # /srv/disinto/caddy-data is created by lib/init/nomad/cluster-up.sh before # any job references it. Keep the `source = "caddy-data"` below in sync # with the host_volume stanza in client.hcl. # # Build step (S5.1): # docker/edge/Dockerfile is custom (adds bash, jq, curl, git, docker-cli, # python3, openssh-client, autossh to caddy:latest). Build as # disinto/edge:local using the same pattern as disinto/agents:local. # Command: docker build -t disinto/edge:local -f docker/edge/Dockerfile docker/edge # # Not the runtime yet: docker-compose.yml is still the factory's live stack # until cutover. This file exists so CI can validate it and S5.2 can wire # `disinto init --backend=nomad --with edge` to `nomad job run` it. # ============================================================================= job "edge" { type = "service" datacenters = ["dc1"] group "edge" { count = 1 # ── Vault workload identity for dispatcher (S5.1, issue #988) ────────── # Service role for dispatcher task to fetch vault actions from KV v2. # Role defined in vault/roles.yaml, policy in vault/policies/dispatcher.hcl. vault { role = "service-dispatcher" } # ── Network ports (S5.1, issue #988) ────────────────────────────────── # Caddy listens on :80 and :443. Expose both on the host. network { port "http" { static = 80 to = 80 } port "https" { static = 443 to = 443 } } # ── Host-volume mounts (S5.1, issue #988) ───────────────────────────── # caddy-data: ACME certificates, Caddy config state. volume "caddy-data" { type = "host" source = "caddy-data" read_only = false } # ops-repo: disinto-ops clone for vault actions polling. volume "ops-repo" { type = "host" source = "ops-repo" read_only = false } # ── Conservative restart policy ─────────────────────────────────────── # Caddy should be stable; dispatcher may restart on errors. restart { attempts = 3 interval = "5m" delay = "15s" mode = "delay" } # ── Service registration ─────────────────────────────────────────────── # Caddy is an HTTP reverse proxy — health check on port 80. service { name = "edge" port = "http" provider = "nomad" check { type = "http" path = "/" interval = "10s" timeout = "3s" } } # ── Caddy task (S5.1, issue #988) ───────────────────────────────────── task "caddy" { driver = "docker" config { # Use pre-built disinto/edge:local image (custom Dockerfile adds # bash, jq, curl, git, docker-cli, python3, openssh-client, autossh). image = "disinto/edge:local" force_pull = false ports = ["http", "https"] # apparmor=unconfined matches docker-compose — needed for autossh # in the entrypoint script. security_opt = ["apparmor=unconfined"] } # Mount caddy-data volume for ACME state and config directory. # Caddyfile is mounted at /etc/caddy/Caddyfile by entrypoint-edge.sh. volume_mount { volume = "caddy-data" destination = "/data" read_only = false } # ── Caddyfile via Nomad service discovery (S5-fix-7, issue #1018) ──── # Renders staging upstream from Nomad service registration instead of # hardcoded staging:80. Caddy picks up /local/Caddyfile via entrypoint. template { destination = "local/Caddyfile" change_mode = "restart" data = <