collin/anvil
a60d607b17fdb7f62a29e6bb2cbfc02dae7d40d6 / compose.override.yaml
| 1 | # Local development only. `docker compose` merges this automatically when it |
| 2 | # sits next to compose.yaml, and `hag` always passes `-f compose.yaml` |
| 3 | # explicitly, so none of it reaches hagrid -- where Caddy fronts the container, |
| 4 | # the SSO issuer is a public HTTPS URL with a normal CA, and there is |
| 5 | # deliberately no Docker socket. |
| 6 | # |
| 7 | # The images carry prebuilt binaries, so stage them first (--debug compiles in |
| 8 | # a fraction of the time a release build takes; --worker adds anvil-worker for |
| 9 | # the two runner containers below): |
| 10 | # |
| 11 | # ./deploy/build.sh --debug --worker |
| 12 | # docker compose up -d --build |
| 13 | # |
| 14 | # Then http://127.0.0.1:20640. `docker compose logs -f`, `docker compose down`. |
| 15 | # |
| 16 | # deploy/dev.sh remains the fuller path: it also generates deploy/dev-ca.crt |
| 17 | # (mounted below), waits for /-/healthz, and points portless at the container. |
| 18 | |
| 19 | # Two CI runners, identical but for their names. anvil executes no jobs itself |
| 20 | # (docs/remote-runners.md), so without these a queued run sits forever; two of |
| 21 | # them rather than one is what makes concurrent pipelines and the "is any |
| 22 | # runner connected" side of platform routing testable on one machine. |
| 23 | # |
| 24 | # LOCAL ONLY, and the reason is worth being explicit about: a containerized |
| 25 | # runner needs the host's Docker socket, which is the root-equivalent hold that |
| 26 | # moving CI off the forge removed. Real runners are native processes on their |
| 27 | # own host (docs/remote-runners.md § Isolation on macOS). This file already |
| 28 | # hands anvil the same socket for agent sessions, so the local trust boundary |
| 29 | # is unchanged -- the deployed compose.yaml grants neither. |
| 30 | x-runner: &runner |
| 31 | image: anvil-worker-dev:latest |
| 32 | build: |
| 33 | context: . |
| 34 | dockerfile: docker/worker/Dockerfile |
| 35 | platforms: |
| 36 | - linux/amd64 |
| 37 | restart: unless-stopped |
| 38 | depends_on: |
| 39 | - anvil |
| 40 | environment: &runner-env |
| 41 | # Container-to-container over the hagrid network, by compose service name: |
| 42 | # ANVIL_BASE_URL is what browsers use and does not resolve in here. |
| 43 | ANVIL_URL: ${ANVIL_RUNNER_URL:-http://anvil:3000} |
| 44 | # Must match `[ci] runner_token` baked in from deploy/anvil.dev.toml. |
| 45 | ANVIL_RUNNER_TOKEN: ${ANVIL_RUNNER_TOKEN:-dev-runner-token} |
| 46 | RUST_LOG: ${ANVIL_RUNNER_LOG:-anvil_worker=info} |
| 47 | volumes: |
| 48 | # The runner is a Docker client: it creates each job's sandbox as a sibling |
| 49 | # container on this host's daemon. The JOB container still gets no socket |
| 50 | # and no mounts -- the checkout is uploaded and artifacts downloaded through |
| 51 | # the API (crates/anvil-worker/src/executor.rs). |
| 52 | # |
| 53 | # `label=disable` rather than a `:z` relabel, same as anvil above: :z would |
| 54 | # rewrite the SELinux label on the host's socket, which every other |
| 55 | # container on this machine also uses. |
| 56 | - /var/run/docker.sock:/var/run/docker.sock |
| 57 | group_add: |
| 58 | - "${DOCKER_GID:-970}" |
| 59 | security_opt: |
| 60 | - label=disable |
| 61 | networks: |
| 62 | - hagrid |
| 63 | |
| 64 | services: |
| 65 | anvil: |
| 66 | # A distinct tag, so a local build carrying the DEV config can never be |
| 67 | # mistaken for -- or pushed as -- the production image. |
| 68 | image: anvil-dev:latest |
| 69 | build: |
| 70 | args: |
| 71 | # Bakes deploy/anvil.dev.toml at /etc/anvil/anvil.toml instead of |
| 72 | # production's: local base_url, the login.localhost issuer, agent |
| 73 | # sessions on, and shorter periodic scans. |
| 74 | CONFIG: deploy/anvil.dev.toml |
| 75 | # Not `anvil`: that name belongs to deploy/dev.sh's container, and compose |
| 76 | # refuses to adopt a container it did not label. |
| 77 | container_name: anvil-dev |
| 78 | |
| 79 | # !override, not a merge: `ports` is one of the keys compose CONCATENATES, |
| 80 | # so without it the production entry survives and the container tries to |
| 81 | # bind 165.232.162.167:22 on this machine. |
| 82 | ports: !override |
| 83 | # A stable, collision-resistant port for this project (`devport`), so it |
| 84 | # does not wander between runs. |
| 85 | - "127.0.0.1:${ANVIL_DEV_PORT:-20640}:3000" |
| 86 | # anvil.dev.toml advertises 20641 in SSH clone URLs; keep the two in step. |
| 87 | - "127.0.0.1:${ANVIL_DEV_SSH_PORT:-20641}:2222" |
| 88 | |
| 89 | volumes: !override |
| 90 | # Separate from production's `anvil-data`, and the same volume dev.sh |
| 91 | # uses, so the two local paths share state. `docker volume rm |
| 92 | # anvil-dev-data` starts over. |
| 93 | - anvil-dev-data:/data |
| 94 | |
| 95 | # Agent sessions (`[agent] enabled = true` in anvil.dev.toml) drive |
| 96 | # Docker directly, so they need the socket. CI does NOT -- that moved to |
| 97 | # anvil-worker, which is its own Docker client on its own machine. |
| 98 | # |
| 99 | # `label=disable` below rather than a `:z` relabel: :z would rewrite the |
| 100 | # SELinux label on the HOST's socket, which every other container on this |
| 101 | # machine also uses. |
| 102 | - /var/run/docker.sock:/var/run/docker.sock |
| 103 | |
| 104 | # The SSO back channel calls https://login.localhost directly, and that |
| 105 | # certificate comes from the CA portless generated. anvild ships its own |
| 106 | # root store (rustls), so `portless trust` does not reach it -- hence a |
| 107 | # bundle of the host's roots plus that CA, which SSL_CERT_FILE points at. |
| 108 | # deploy/dev.sh writes this file; regenerate it by hand with: |
| 109 | # cat /etc/ssl/certs/ca-bundle.crt ~/.portless/ca.pem > deploy/dev-ca.crt |
| 110 | - ./deploy/dev-ca.crt:/etc/ssl/certs/anvil-dev-ca.crt:ro,z |
| 111 | |
| 112 | # The gid owning /var/run/docker.sock on this host. `stat -c '%g' |
| 113 | # /var/run/docker.sock` if it differs on yours. |
| 114 | group_add: |
| 115 | - "${DOCKER_GID:-970}" |
| 116 | security_opt: |
| 117 | - label=disable |
| 118 | |
| 119 | # Appended to production's host.docker.internal entry, not replacing it: |
| 120 | # login.localhost resolves to the container's own loopback otherwise, |
| 121 | # rather than the host's portless proxy. |
| 122 | extra_hosts: |
| 123 | - "login.localhost:host-gateway" |
| 124 | |
| 125 | environment: |
| 126 | SSL_CERT_FILE: /etc/ssl/certs/anvil-dev-ca.crt |
| 127 | # Overrides anvil.dev.toml's baked base_url. Point it at |
| 128 | # http://127.0.0.1:20640 when testing websockets -- portless proxies |
| 129 | # them over HTTP/2, where they are currently broken -- but note that |
| 130 | # changing it also changes the OIDC redirect_uri, which the provider |
| 131 | # matches exactly. |
| 132 | ANVIL_BASE_URL: ${ANVIL_BASE_URL:-https://anvil.localhost} |
| 133 | |
| 134 | # A third is four lines: copy one of these and bump the name. The names are |
| 135 | # what run headers and `[ci]` logs show, so keep them distinct -- an unnamed |
| 136 | # runner falls back to its hostname, which in a container is a hex id. |
| 137 | runner-1: |
| 138 | <<: *runner |
| 139 | container_name: anvil-runner-1 |
| 140 | environment: |
| 141 | <<: *runner-env |
| 142 | ANVIL_RUNNER_NAME: dev-1 |
| 143 | |
| 144 | runner-2: |
| 145 | <<: *runner |
| 146 | container_name: anvil-runner-2 |
| 147 | environment: |
| 148 | <<: *runner-env |
| 149 | ANVIL_RUNNER_NAME: dev-2 |
| 150 | |
| 151 | volumes: |
| 152 | anvil-dev-data: |
| 153 | name: anvil-dev-data |
| 154 | # Same expected "not created by Docker Compose" warning as production's |
| 155 | # volume: dev.sh made this one first, and compose adopts it. |