anvilsign in

collin/anvil

1# anvil on hagrid, deployed with `hag` -- the shared deploy tool for every
2# project on that host (build, push to the private registry, then pull and
3# recreate there). Both this machine and hagrid need
4# `docker login registry.vibe.richardscollin.com` once.
5#
6# The image carries a PREBUILT binary: the Dockerfile only COPYs in
7# deploy/anvild, which deploy/build.sh cross-compiles as a static x86_64-musl
8# executable. So `hag deploy` on its own is not enough -- use
9# ./deploy/deploy.sh, which stages the binary and then calls it.
10#
11# IMAGE_TAG picks which build runs. hag sets it to the git sha it just pushed,
12# so `hag status` names the exact build, and rolling back on the host is
13# `IMAGE_TAG=<sha> docker compose up -d --no-build`.
14services:
15 anvil:
16 image: registry.vibe.richardscollin.com/anvil:${IMAGE_TAG:-latest}
17 build:
18 context: .
19 # hagrid is x86_64 and the staged binary is x86_64-musl. Pinning the
20 # platform stops an arm64 workstation from producing an image whose
21 # base layers the host cannot run.
22 platforms:
23 - linux/amd64
24 container_name: anvil
25 restart: unless-stopped
26
27 # Host config that must not be baked into the image -- today that is
28 # ANVIL_OIDC_CLIENT_SECRET (docs/oidc.md). Optional so the container still
29 # starts where there is none, matching how run.sh only passed the secret
30 # when it found one: an empty value here would override the baked config
31 # and turn a confidential OIDC client into a public one.
32 env_file:
33 - path: .env
34 required: false
35
36 # [ci] deploy_webhook posts to a receiver on the host, so the container
37 # needs a route to it. Docker Desktop supplies this name; Linux does not.
38 extra_hosts:
39 - "host.docker.internal:host-gateway"
40
41 ports:
42 # Git-over-SSH only. The web port stays unpublished -- Caddy reaches
43 # anvil:3000 over the hagrid network and terminates TLS.
44 #
45 # Published on the droplet's DEFAULT public IPv4 alone. The reserved IP
46 # (137.184.249.48) arrives on the anchor address 10.15.0.6, where the
47 # host's own sshd listens, so binding one specific IP here keeps the two
48 # off each other.
49 - "${ANVIL_SSH_BIND_IP:-165.232.162.167}:${ANVIL_SSH_PORT:-22}:2222"
50
51 # NO DOCKER SOCKET. anvil does not execute CI any more -- runners dial in
52 # and run jobs on their own daemons (docs/remote-runners.md), so the
53 # container has no reason to reach Docker. Leaving the mount out is what
54 # removes the root-equivalent hold the internet-facing process used to have
55 # on the host. Agent sessions (crates/anvil-agent) are the one thing this
56 # gives up; they are off in deploy/anvil.toml and off by default. Read
57 # docs/untrusted-mode.md before putting the mount back.
58 volumes:
59 - anvil-data:/data
60
61 networks:
62 - hagrid
63
64volumes:
65 # `name:` pins the volume to the exact name the pre-compose deploys used, so
66 # this adopts the existing SQLite DB, bare repos and SSH host key rather than
67 # coming up against an empty `anvil_anvil-data` that compose would otherwise
68 # derive from the project name. A named volume (not a bind mount) keeps it
69 # owned by the in-container `anvil` user.
70 anvil-data:
71 name: anvil-data
72 # (Compose warns that it did not create this volume, then adopts it. That
73 # is the intended path off the `docker run` deploys; `external: true`
74 # would silence it but break a first install on a fresh host.)
75
76# Caddy runs on this network and reverse-proxies to the container by name.
77# The hagrid repo owns the network's lifecycle.
78networks:
79 hagrid:
80 external: true