128 lines
5.7 KiB
YAML
128 lines
5.7 KiB
YAML
# Forgejo (self-hosted Git + CI/CD) as a Docker Swarm stack — the only workload
|
|
# this Swarm cluster runs (see deploy/swarm/README.md). Deliberately separate
|
|
# from the Terraform-managed docker-compose.yml that runs the Thermograph app
|
|
# itself: this stack's only job is Forgejo and its container registry.
|
|
#
|
|
# The Actions RUNNER is deliberately NOT a service in this stack. Per
|
|
# thermograph-docs/runbooks/implementation-handoff.md (Track B step 5), the canonical
|
|
# design registers the runner on the desktop node as a plain systemd service
|
|
# (deploy/forgejo/register-lan-runner.sh) — the same place the pre-Forgejo
|
|
# GitHub self-hosted runner already lived, not a Swarm-scheduled container.
|
|
#
|
|
# No Traefik here. Forgejo is pinned to beta (node.labels.role == forge)
|
|
# because that's what was chosen, but beta is ALSO today's live thermograph.org
|
|
# host — Caddy already owns its ports 80/443 (see /etc/caddy/Caddyfile on that
|
|
# box). A second reverse proxy binding those same ports would either fail to
|
|
# start or fight Caddy. Instead: forgejo's web port publishes to
|
|
# 127.0.0.1:3080 only (host-local, mode: host), and beta's existing Caddy gets
|
|
# a new site block reverse-proxying git.thermograph.org -> 127.0.0.1:3080,
|
|
# same pattern as its thermograph.org block. TLS is Caddy's existing
|
|
# automatic-HTTPS (HTTP-01), not a second ACME flow.
|
|
#
|
|
# Deploy from the manager node (prod), after all three nodes (prod, beta,
|
|
# desktop) have joined the swarm and beta is labeled role=forge:
|
|
#
|
|
# docker stack deploy -c deploy/forgejo/docker-stack.yml forgejo
|
|
#
|
|
# Requires this Swarm secret to exist first (see deploy/forgejo/README.md):
|
|
# forgejo_db_password
|
|
#
|
|
# Registry exposure (hazard #15 in thermograph-docs/runbooks/hop1-forgejo-registry-cutover.md):
|
|
# resolved as the runbook's first listed option — serve Git/UI publicly but
|
|
# firewall the /v2/ registry API paths so only WireGuard-mesh clients can
|
|
# reach them. That's implemented in the Caddy site block (see
|
|
# deploy/forgejo/README.md), not here — nothing in this stack file is
|
|
# registry-specific, the restriction lives entirely in Caddy's config on beta.
|
|
|
|
services:
|
|
db:
|
|
image: postgres:16-alpine
|
|
environment:
|
|
POSTGRES_USER: forgejo
|
|
POSTGRES_DB: forgejo
|
|
POSTGRES_PASSWORD_FILE: /run/secrets/forgejo_db_password
|
|
secrets: [forgejo_db_password]
|
|
volumes:
|
|
- forgejo_db:/var/lib/postgresql/data
|
|
networks: [forgejo_net]
|
|
deploy:
|
|
placement:
|
|
constraints: [node.labels.role == forge]
|
|
restart_policy:
|
|
condition: on-failure
|
|
|
|
forgejo:
|
|
image: codeberg.org/forgejo/forgejo:9-rootless
|
|
depends_on: [db]
|
|
environment:
|
|
FORGEJO__database__DB_TYPE: postgres
|
|
FORGEJO__database__HOST: db:5432
|
|
FORGEJO__database__NAME: forgejo
|
|
FORGEJO__database__USER: forgejo
|
|
FORGEJO__server__DOMAIN: "${FORGEJO_DOMAIN:-git.thermograph.org}"
|
|
FORGEJO__server__ROOT_URL: "https://${FORGEJO_DOMAIN:-git.thermograph.org}/"
|
|
FORGEJO__server__SSH_PORT: "2222"
|
|
# Without this, Forgejo starts no SSH server at all despite SSH_PORT
|
|
# being set — git@ clones get "connection refused", not a slow failure.
|
|
FORGEJO__server__START_SSH_SERVER: "true"
|
|
# Config is fully supplied via env, so lock the install wizard rather
|
|
# than leave it open on the public internet waiting for someone to
|
|
# complete it first (DB config alone does NOT imply this).
|
|
FORGEJO__security__INSTALL_LOCK: "true"
|
|
# Actions on: repo/org/user-level runners register against this instance.
|
|
FORGEJO__actions__ENABLED: "true"
|
|
# Google SSO account handling. Forgejo only consults ACCOUNT_LINKING during
|
|
# the auto-registration path, so auto-registration must be ON for a Google
|
|
# login to auto-link (by verified email) to the existing account instead of
|
|
# dropping to the manual link/register page. To keep that from letting *any*
|
|
# Google user register, the auth source pins a required email claim (added
|
|
# via `forgejo admin auth update-oauth --required-claim-name email
|
|
# --required-claim-value <you>@gmail.com`), so only that one address can log
|
|
# in at all — it collides with the existing account's email and links to it.
|
|
FORGEJO__oauth2_client__ACCOUNT_LINKING: "auto"
|
|
FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION: "true"
|
|
# Gitea/Forgejo's app.ini env-mapping honors a __FILE suffix to read a
|
|
# value from a file instead of the literal env var — same convention as
|
|
# the official Postgres image's POSTGRES_PASSWORD_FILE above.
|
|
FORGEJO__database__PASSWD__FILE: /run/secrets/forgejo_db_password
|
|
secrets:
|
|
- source: forgejo_db_password
|
|
target: forgejo_db_password
|
|
volumes:
|
|
- forgejo_data:/var/lib/gitea
|
|
networks: [forgejo_net]
|
|
ports:
|
|
# SSH for git@ clones — published on whichever node the task lands on
|
|
# (pinned to beta by the placement constraint below, so effectively
|
|
# always beta's public IP, port 2222).
|
|
- target: 2222
|
|
published: 2222
|
|
protocol: tcp
|
|
mode: host
|
|
# Web UI/API. Swarm's port schema has no host_ip scoping, so this binds
|
|
# 0.0.0.0:3080 on beta — NOT actually localhost-only by itself. A
|
|
# DOCKER-USER iptables rule (see deploy/forgejo/README.md) is what
|
|
# actually restricts it, since Docker's own iptables rules bypass ufw
|
|
# for published ports. beta's Caddy reverse-proxies to 127.0.0.1:3080.
|
|
- target: 3000
|
|
published: 3080
|
|
protocol: tcp
|
|
mode: host
|
|
deploy:
|
|
placement:
|
|
constraints: [node.labels.role == forge]
|
|
restart_policy:
|
|
condition: on-failure
|
|
|
|
networks:
|
|
forgejo_net:
|
|
driver: overlay
|
|
attachable: false
|
|
|
|
volumes:
|
|
forgejo_db:
|
|
forgejo_data:
|
|
|
|
secrets:
|
|
forgejo_db_password:
|
|
external: true
|