deploy / deploy (push) Successful in 33s
Three things in one setup run, two of them mine. The server image was 1.11.1 while the host's CLI is 1.11.2, and a 1.11.2 client looks up the domain entry at a UUID 1.11.1 does not have. So `system domain set-displayname` and `set-image` both failed with "Item not found", which says nothing about versions. The CLI had been warning about the mismatch on every single call. Image bumped to 1.11.2; the README says to keep them together and which one to move. The onboarding token asked for `--rw`, and the flag is spelled `--readwrite`. The script swallowed stderr and reported a bare warning, so a step that leaves every invite failing closed said nothing about why. It now passes the right flag, and if it still fails it says what the CLI said and prints the command to retry by hand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
150 lines
5.5 KiB
YAML
150 lines
5.5 KiB
YAML
# Tomter Vel's own platform: portal + its own Kanidm and NATS as podman
|
|
# containers. The content (pages, forms, desks) is not here: portal fetches
|
|
# it from tomtervel/questions on prosjekt.klingenbergbygg.no and hot-reloads
|
|
# it over NATS. This repo owns identity, the bus, and which portal version
|
|
# runs. TLS depends on where it runs: on a standalone host the bundled Caddy
|
|
# (`--profile edge`) terminates it; on kasse the host Caddy already owns
|
|
# 80/443 and reverse-proxies vel.klingenbergbygg.no -> portal (127.0.0.1:3050).
|
|
#
|
|
# bootstrap.sh first time on a fresh host: renders configs from
|
|
# .env, makes Kanidm's internal cert, starts it all,
|
|
# recovers the Kanidm admin, creates the portal client
|
|
# and desk groups.
|
|
# podman compose up -d --build every time after that.
|
|
|
|
name: tomtervel
|
|
|
|
services:
|
|
caddy:
|
|
# Bundled TLS for a standalone host only: `podman compose --profile edge up`.
|
|
# On kasse the host Caddy already owns 80/443 (it reverse-proxies
|
|
# vel.klingenbergbygg.no -> 127.0.0.1:3050, the portal port below), so this
|
|
# is profiled off there to avoid the port clash. Default `up` skips it.
|
|
profiles: ["edge"]
|
|
image: caddy:2
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
- "443:443/udp"
|
|
environment:
|
|
PORTAL_HOST: ${PORTAL_HOST}
|
|
ID_HOST: ${ID_HOST}
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy_data:/data
|
|
- caddy_config:/config
|
|
depends_on:
|
|
- portal
|
|
- kanidm
|
|
|
|
nats:
|
|
image: nats:2.14.6-alpine
|
|
restart: unless-stopped
|
|
command: ["-c", "/etc/nats/nats.conf"]
|
|
volumes:
|
|
- ./nats/nats.conf:/etc/nats/nats.conf:ro
|
|
- nats_data:/data
|
|
# Published on the host so the reload job (`nats pub portal.content.reload ""`)
|
|
# can reach it; password-protected (see nats.conf). Host port 4223, not 4222:
|
|
# on kasse the shared platform NATS already owns 127.0.0.1:4222 and the vel
|
|
# runs its own NATS in isolation, so the reload workflow targets 4223 there.
|
|
# (Standalone, nothing else owns 4222, but 4223 is harmless.)
|
|
ports:
|
|
- "127.0.0.1:4223:4222"
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8222/healthz"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
|
|
kanidm:
|
|
image: kanidm/server:1.11.2
|
|
restart: unless-stopped
|
|
environment:
|
|
KANIDM_CONFIG_PATH: /data/server.toml
|
|
volumes:
|
|
- kanidm_data:/data
|
|
- ./kanidm/server.toml:/data/server.toml:ro
|
|
# Internal self-signed TLS: Caddy terminates the public
|
|
# certificate and proxies here without verification.
|
|
- ./certs/kanidm-chain.pem:/data/chain.pem:ro
|
|
- ./certs/kanidm-key.pem:/data/key.pem:ro
|
|
# Standalone (--profile edge): only the bundled Caddy talks to Kanidm.
|
|
# On kasse the host Caddy fronts it, so publish loopback-only for a conf.d
|
|
# entry: id.<vel> { reverse_proxy https://localhost:8443 {
|
|
# transport http { tls_insecure_skip_verify } } }
|
|
# (Kanidm's internal self-signed cert; 8443 is free on kasse — its shared
|
|
# Kanidm is on 8310.)
|
|
ports:
|
|
- "127.0.0.1:8443:8443"
|
|
|
|
portal:
|
|
build:
|
|
context: ./portal
|
|
args:
|
|
PORTAL_RELEASE: ${PORTAL_RELEASE}
|
|
restart: unless-stopped
|
|
env_file: portal.env
|
|
environment:
|
|
LEPTOS_SITE_ADDR: 0.0.0.0:3000
|
|
LEPTOS_SITE_ROOT: site
|
|
LEPTOS_HASH_FILES: "true"
|
|
ports:
|
|
# Host-published for kasse's host Caddy (vel.klingenbergbygg.no -> here).
|
|
# With --profile edge the bundled Caddy proxies portal:3000 internally
|
|
# instead, but publishing loopback-only is harmless there.
|
|
- "127.0.0.1:3050:3000"
|
|
depends_on:
|
|
nats:
|
|
condition: service_healthy
|
|
kanidm:
|
|
condition: service_started
|
|
|
|
# The mailer. Portal decides what to send - a `mail:` on a state in the
|
|
# content, or an invite - and publishes it on this NATS; gdo is what
|
|
# actually hands it to a mail server. It is in the vel's own stack rather
|
|
# than shared, so the vel's mail leaves on the vel's own terms, but it
|
|
# has no mail server of its own: it relays through the host's, which on
|
|
# kasse is Klingenberg Bygg's postfix on port 25. That is the one thing
|
|
# here the vel borrows.
|
|
gdo:
|
|
build:
|
|
context: ./gdo
|
|
args:
|
|
GDO_RELEASE: ${GDO_RELEASE}
|
|
restart: unless-stopped
|
|
environment:
|
|
NATS_URL: nats://portal:${NATS_PASSWORD}@nats:4222
|
|
# The host, from inside the container. Podman resolves this to the
|
|
# gateway; the host's postfix listens on 0.0.0.0:25.
|
|
SMTP_HOST: host.containers.internal
|
|
SMTP_PORT: "25"
|
|
# A strict server refuses a bare container hostname in EHLO.
|
|
SMTP_HELO: ${PORTAL_HOST}
|
|
extra_hosts:
|
|
- "host.containers.internal:host-gateway"
|
|
depends_on:
|
|
nats:
|
|
condition: service_healthy
|
|
|
|
# The Gitea Actions runner is a host service on kasse (pacman gitea-runner),
|
|
# registered against prosjekt.klingenbergbygg.no. It reaches this NATS via the
|
|
# published 127.0.0.1:4222 port above, so no in-compose runner — and no
|
|
# docker.sock/podman.sock mount — is needed here.
|
|
|
|
volumes:
|
|
caddy_data:
|
|
caddy_config:
|
|
nats_data:
|
|
kanidm_data:
|
|
|
|
# Pin the project network subnet inside 172.16/12 so Kanidm's X-Forwarded-For
|
|
# trust (kanidm/server.toml.tpl) stays valid under Podman — its default pool
|
|
# hands out 10.89.x addresses that Caddy's forwarded client IP can't match.
|
|
networks:
|
|
default:
|
|
ipam:
|
|
config:
|
|
- subnet: 172.18.0.0/16
|