Seven confirmed findings from an observed-vs-expected review of a real deploy
transcript against the code (four other proposed findings were refuted and no
change was made for them).
- Caddyfile reformatted so `caddy fmt` is clean, removing the warning Caddy
printed on every validate and every start. Two causes, not one: `caddy fmt`
indents with TABS, and it deletes a blank line whose following line begins
with `{` -- which is why the warning pointed at line 17, the blank before the
global options block. The check is a whole-file byte comparison, so the line
number was only the first difference and the entire file had to be
reformatted. Verified whitespace-only outside the heredoc, whose body is left
byte-identical: Caddy strips padding derived from the closing marker's
indentation, so re-indenting it would change what the page serves.
- deploy.sh no longer upgrades Caddy behind the operator's back. A bare
`docker compose pull` refreshed the floating `caddy:2-alpine` on every re-run
and `up -d` then recreated it, doing exactly what CADDY_AUTOUPDATE=0 promises
not to, with none of update.sh's health check or rollback. Pulls are now
per service: the pinned Ergo tag always, Caddy only when absent or opted in.
- deploy.sh seeds the ACME_EMAIL and NETWORK_NAME prompts from .env, so pressing
Enter through a re-run no longer renames the network to the hostname in the
summary while .env keeps the real one.
- The Caddy restart guard compares the container's identity across `up -d`. A
container compose created or recreated has already read the new Caddyfile;
only one left running still holds the old config, and `svc_state` cannot tell
those apart.
- Container logs are now rotated (json-file, 10m x 3) on every service. Alpine's
docker package ships no daemon.json, so the default is unbounded, and the
60-second health probe alone writes a log line per run.
- ergoctl notes that LUSERS counts its own probe connection, which is why a
server with nobody on it reports one invisible user.
Verified: seven local suites pass, the embedded archive round-trips, line
endings are LF, and the reformatted Caddyfile is a verified fixed point of the
formatter (semantically identical token-for-token to the previous one).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
70 lines
2.8 KiB
YAML
70 lines
2.8 KiB
YAML
# PostgreSQL overlay -- used ONLY when HISTORY=postgres.
|
|
#
|
|
# deploy.sh activates it by writing
|
|
# COMPOSE_FILE=docker-compose.yml:docker-compose.postgres.yml
|
|
# into .env, which docker compose reads by itself. So every `docker compose`
|
|
# command in this stack (up, ps, stop, logs, exec) sees the same set of services
|
|
# with no extra flags -- unlike compose profiles, where a service can be silently
|
|
# absent from one command and present in another.
|
|
#
|
|
# Networking: ergo and caddy run in the HOST namespace, so they cannot use
|
|
# compose's service DNS. PostgreSQL therefore stays on a normal bridge network
|
|
# and publishes only to the host's loopback; Ergo reaches it at 127.0.0.1:5432
|
|
# (datastore.postgresql.host in ircd.yaml). Nothing is exposed off-box.
|
|
#
|
|
# The major version is PINNED. PostgreSQL will not start on a data directory
|
|
# written by a different major version, so `update.sh` never touches this image.
|
|
# Upgrading it is a deliberate dump-and-restore -- see the README ("PostgreSQL").
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:${POSTGRES_TAG:-17-alpine}
|
|
container_name: ergo-postgres
|
|
restart: unless-stopped
|
|
# Loopback only. A Docker-published port bypasses the host INPUT firewall,
|
|
# so the bind address is the real restriction here.
|
|
ports:
|
|
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
|
|
environment:
|
|
POSTGRES_USER: "${POSTGRES_USER:-ergo}"
|
|
POSTGRES_DB: "${POSTGRES_DB:-ergo_history}"
|
|
# The password is read from a file so it never has to live in .env
|
|
# (compose interpolates .env, and a '$' in a password would break it).
|
|
POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password
|
|
# 17 and below keep the classic layout; set it explicitly so a future
|
|
# image default cannot move the data directory under us.
|
|
PGDATA: /var/lib/postgresql/data/pgdata
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
- ./secrets/postgres.pass:/run/secrets/postgres-password:ro
|
|
cap_drop: [ALL]
|
|
cap_add:
|
|
- CHOWN # initdb/entrypoint fix ownership of PGDATA
|
|
- DAC_READ_SEARCH
|
|
- FOWNER
|
|
- SETGID # the entrypoint drops from root to the postgres user
|
|
- SETUID
|
|
security_opt: [no-new-privileges:true]
|
|
logging:
|
|
driver: json-file
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U \"${POSTGRES_USER:-ergo}\" -d \"${POSTGRES_DB:-ergo_history}\" -q"]
|
|
interval: 15s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
|
|
# Ergo must not come up before the database is accepting connections: with
|
|
# persistent history enabled it fails to start if the backend is unreachable.
|
|
# depends_on is orchestration only, so it works across the host/bridge split.
|
|
ergo:
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
|
|
volumes:
|
|
postgres-data:
|