Skip to content

Commit af823d5

Browse files
committed
feat(container): compose split + bring-up fixes (ADR-T-009 Phase 8)
Phase 8 of ADR-T-009. Splits the single development-only `compose.yaml` into a production-shaped baseline plus a dev-sandbox override, wires both behind a top-level `Makefile`, and folds in five latent bugs surfaced by the end-to-end `make up-dev` / `make up-prod` bring-up against `podman-compose 1.5.0` / `podman 5.8.2`. Compose split ------------- - `compose.yaml`: production-shaped baseline. Drops the `mailcatcher` sidecar and the `tty: true` allocations. Credentials and the environment-coupled mail SMTP server are referenced as bare `${VAR}` (no default, no `:?required`); validation is deferred to the wrapper and the in-container config probe (defence in depth, per ADR-T-009 §8.1). Operator selectors with a sensible cross-environment default (`TORRUST_INDEX_DATABASE`, `TORRUST_INDEX_DATABASE_DRIVER`, …) keep their `${VAR:-default}` form. Switches `depends_on` on `index` and `tracker` to long-form so an override can additively re-attach `mailcatcher` rather than silently replace the dependency list. - `compose.override.yaml` (new): dev sandbox extras — `mailcatcher` sidecar (re-attached to `index` via long-form `depends_on`), `tty: true` on `index` and `tracker`, and permissive `${VAR:-default}` credential defaults so a plain `docker compose up` works with no operator intervention. Auto-loaded by Compose v2 and by `make up-dev`; excluded by `make up-prod` via explicit `--file compose.yaml`. - `Makefile` (new): two targets, `up-dev` (plain `docker compose up`) and `up-prod` (validates required env vars via POSIX `sh -uc ': "${VAR:?required}"'`, then `docker compose --file compose.yaml up -d --wait`). `COMPOSE_FILE` is overridable for acceptance tests. The MySQL credential check is gated behind a `grep mysql:` of the compose file so a baseline without a local mysql sidecar does not require `MYSQL_ROOT_PASSWORD`. Bring-up fixes (folded in rather than deferred) ----------------------------------------------- 1. **`addgroup` missing from the curated busybox surface.** Distroless `cc-debian13` ships `/etc/passwd` and `/etc/group`, but busybox `adduser -D -u UID NAME` only writes `/etc/passwd`; `getgrnam("torrust")` then fails and the entry script's `install -g torrust …` step dies with `unknown group torrust`. Add `addgroup` to the curated symlink loop in the release runtime base (and to the preflight gate's symlink loop, kept in sync per ADR-T-009 §4.4). Entry script now does `addgroup -g "$USER_ID" torrust` followed by `adduser -D -s /bin/sh -u "$USER_ID" -G torrust torrust`, both guarded with idempotent `grep` checks of `/etc/{group,passwd}` so a container restart is a no-op under `set -e`. 2. **`jq` shipped without its shared libs.** The `jq_donor` stage installed jq via `apt-get` but the runtime stages copied only `/usr/bin/jq`; the binary then aborted with `libjq.so.1: cannot open shared object file` inside the lean `cc-debian13` runtime. Copy `libjq.so.1` and `libonig.so.5` from the donor alongside the binary into both runtime stages. Add an `ldd`-based allow-list assertion to the `jq_donor` stage (`libc.so.6 libjq.so.1 libm.so.6 libonig.so.5 ld-linux-x86-64.so.2 linux-vdso.so.1`) so a future donor-base upgrade that drags in a new transitive dep fails the build instead of silently producing a broken image. The parser strips path prefixes from `ldd`'s first column so absolute paths and bare sonames are treated identically. 3. **Empty-string env vars treated as inline config TOML.** `Info::from_env` called `env::var(…).ok()` directly, which returns `Some("")` for an exported-but-unset variable. The previous compose baseline's `TORRUST_INDEX_CONFIG_TOML=${TORRUST_INDEX_CONFIG_TOML}` propagated an empty string into the container, which the loader treated as a zero-byte TOML and rejected with `Missing mandatory option: logging.threshold`. `Info::from_env` now filters empty strings out of both `TORRUST_INDEX_CONFIG_TOML` and `TORRUST_INDEX_CONFIG_TOML_PATH`. `compose.yaml` uses Compose's bare-name pass-through form (`- TORRUST_INDEX_CONFIG_TOML`, no `=`) for those two optional inline-TOML envs so an unset host var is omitted entirely rather than forwarded as empty. 4. **Test binaries baking compile-time paths.** `packages/index-config-probe/tests/binary.rs` and `packages/index-entry-script/tests/seed_sqlite.rs` resolved their subjects via `env!("CARGO_BIN_EXE_…")` and `CARGO_MANIFEST_DIR`; both bake the build-time path into the test binary and break under cargo-nextest's archive → `--extract-to` → `--target-dir-remap` flow used by the container `test` stage. `binary.rs` is rewritten to drive the contracts through the library API (same model as `packages/index-config/tests/shipped_samples.rs`): exit-3 / 4 / 5 are pinned by exercising `load_settings` / `probe`, and exit-2 is pinned by handing clap a mirror of the binary's `Args` and asserting `Err::exit_code() == 2`. The entry-script test crate embeds `entry_script_lib_sh` at compile time via `include_str!` and materialises it to a `tempfile` per `run_sh` call; `tempfile` accordingly moves from `[dev-dependencies]` to `[dependencies]` since `run_sh` / `run_sh_with_args` are `#[doc(hidden)] pub` library items. 5. **Unqualified Docker Hub image names.** `mysql:8.0.45`, `dockage/mailcatcher:0.8.2`, and `torrust/tracker:develop` all relied on a short-name-resolution prompt that podman cannot honour without a TTY (and that operators should not be relying on regardless). Fully qualify all three as `docker.io/...` in `compose.yaml` and `compose.override.yaml`. Portable across docker and podman; immune to per-host `unqualified-search-registries` configuration. 6. **`USER_ID` missing from `_validate-prod-env`.** `compose.yaml` references `${USER_ID}` with no default (it is an operator-supplied numeric selector, neither a credential nor environment-coupled). Without wrapper-side validation, an unset `USER_ID` propagated to the entry script's `adduser -u ""` call and failed deep inside container start. Add `USER_ID` to the `_validate-prod-env` required list. Test fixtures ------------- - The three `contrib/dev-tools/container/e2e/.../e2e-env-up.sh` scripts now export `TORRUST_INDEX_CONFIG_OVERRIDE_DATABASE__CONNECT_URL` alongside the existing `..._TRACKER__TOKEN` override, so the e2e suite satisfies the §D2 mandatory-fields contract the same way `make up-prod` does. Docs ---- - `adr/009-implementation-plan.md`: mark Phase 8 Done; add §8.6 documenting the six bring-up fixes and §9.1.3 scheduling the `mailcatcher`-on-`compose.yaml` CI lint prescribed by §8.1; align the §4.4 / §9 prose and the curated-applets table with the new `addgroup` symlink and the §8.1 `USER_ID` validation. - `docs/containers.md`: new "Compose Split" section walking through the baseline / override split, the `make up-dev` / `make up-prod` invocation paths, the required env vars, and the defence-in-depth contract with the in-container config probe. Update the curated busybox applet list to include `addgroup` and refresh the auth-key bootstrap pointer to the new override file.
1 parent 839450b commit af823d5

13 files changed

Lines changed: 396 additions & 49 deletions

File tree

Containerfile

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,31 @@ RUN mkdir -p /app/share/torrust/default/database/; \
3535
FROM rust:slim-trixie AS jq_donor
3636
RUN apt-get update && \
3737
apt-get install -y --no-install-recommends jq && \
38-
rm -rf /var/lib/apt/lists/* && \
39-
mkdir -p /jq && \
38+
rm -rf /var/lib/apt/lists/*
39+
# Pin jq's runtime shared-library set so a future donor-base
40+
# upgrade that drags in a new transitive dep fails the build
41+
# instead of producing a silently-broken runtime image. The
42+
# allow-list mirrors what the runtime stages COPY across
43+
# (libjq, libonig) plus libraries already present in the
44+
# `cc-debian13` runtime base (libc, libm, ld-linux, vdso).
45+
RUN set -eu; \
46+
expected='libc.so.6 libjq.so.1 libm.so.6 libonig.so.5 ld-linux-x86-64.so.2 linux-vdso.so.1'; \
47+
# ldd column 1 is sometimes a bare soname ("libc.so.6")
48+
# and sometimes an absolute path ("/lib64/ld-linux-…").
49+
# Reduce to basenames so the allow-list check is uniform.
50+
actual="$(ldd /usr/bin/jq | awk '{print $1}' | sed 's|.*/||' | sort -u | tr '\n' ' ')"; \
51+
for lib in $expected; do \
52+
case " $actual " in *" $lib "*) ;; *) echo "ERROR: jq lost expected library: $lib" >&2; exit 1 ;; esac; \
53+
done; \
54+
for lib in $actual; do \
55+
case " $expected " in *" $lib "*) ;; *) echo "ERROR: jq pulls unexpected library: $lib (allow-list: $expected)" >&2; exit 1 ;; esac; \
56+
done
57+
# Stage jq + its two non-glibc shared libraries at
58+
# deterministic paths under `/jq/`. The `*-linux-gnu` glob
59+
# resolves to whichever multi-arch tuple the donor was
60+
# built for; re-staging under stable names lets the runtime
61+
# COPY directives stay tuple-agnostic.
62+
RUN mkdir -p /jq && \
4063
cp /usr/bin/jq /jq/jq && \
4164
cp -L /usr/lib/*-linux-gnu/libjq.so.1 /jq/libjq.so.1 && \
4265
cp -L /usr/lib/*-linux-gnu/libonig.so.5 /jq/libonig.so.5
@@ -227,7 +250,7 @@ ENV PATH=/usr/local/bin:/bin:/usr/bin:/sbin
227250
# §4.4 of the implementation plan — update both in the same
228251
# change.
229252
RUN ["/bin/busybox", "sh", "-c", \
230-
"for a in sh adduser install mkdir dirname chown chmod tr mktemp cat printf rm echo grep; do \
253+
"for a in sh adduser addgroup install mkdir dirname chown chmod tr mktemp cat printf rm echo grep; do \
231254
/bin/busybox ln -s busybox /bin/$a; \
232255
done && rm -f /tmp/.preflight-sentinel"]
233256

Makefile

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Top-level developer / operator entry points for the
2+
# Compose split introduced by ADR-T-009 §8.3.
3+
#
4+
# Targets:
5+
#
6+
# make up-dev Plain `docker compose up`. Auto-loads
7+
# `compose.override.yaml`, applying the dev
8+
# sandbox extras (mailcatcher, permissive
9+
# credential defaults, tty allocation). No
10+
# validation — operators get whatever
11+
# defaults the override provides.
12+
#
13+
# make up-prod Production-shaped invocation. Validates
14+
# required credentials are set in the
15+
# environment before any container starts,
16+
# then runs `docker compose --file
17+
# $(COMPOSE_FILE) up -d --wait` with the
18+
# override excluded. Make propagates the
19+
# recipe's exit code, so a failed
20+
# healthcheck (`--wait`) or a missing
21+
# credential (`:?required`) surfaces as a
22+
# non-zero `make` exit.
23+
#
24+
# Validation logic uses POSIX `sh` with `set -u` and
25+
# explicit `: "$${VAR:?message}"` per required variable so
26+
# the file is dependency-free and shellcheck-clean.
27+
#
28+
# COMPOSE_FILE is overridable so acceptance tests (and
29+
# operators with a non-default layout) can point at an
30+
# alternate baseline without filesystem manipulation:
31+
#
32+
# make up-prod COMPOSE_FILE=path/to/other.yaml
33+
#
34+
# Compose's own `COMPOSE_FILE` env var is ignored when
35+
# `--file` is set on the command line, so the make-variable
36+
# is the only reliable way to redirect the recipe.
37+
38+
.PHONY: up-dev up-prod _validate-prod-env
39+
40+
COMPOSE_FILE ?= compose.yaml
41+
42+
up-dev:
43+
docker compose up
44+
45+
# NOTE: The MySQL check is name-coupled to the compose
46+
# service (`mysql:`). This is acceptable because it is
47+
# defence-in-depth only — the config probe (exit 3 on
48+
# empty connect_url) and the MySQL entrypoint (rejects
49+
# empty root password) are the authoritative gates.
50+
_validate-prod-env:
51+
@sh -uc '\
52+
: "$${USER_ID:?required (numeric host UID owning ./storage)}" && \
53+
: "$${TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN:?required}" && \
54+
: "$${TORRUST_INDEX_CONFIG_OVERRIDE_DATABASE__CONNECT_URL:?required}" && \
55+
: "$${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN:?required}" && \
56+
if grep -q "^[[:space:]]*mysql:" $(COMPOSE_FILE); then \
57+
: "$${MYSQL_ROOT_PASSWORD:?required}"; \
58+
fi'
59+
60+
up-prod: _validate-prod-env
61+
docker compose --file $(COMPOSE_FILE) up -d --wait

adr/009-implementation-plan.md

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ re-litigated here — when in doubt, defer to the ADR.
2121
| 5 | Schema (D2) | Done |
2222
| 6 | Config probe | Done |
2323
| 7 | Entry-script contract | Done |
24-
| 8 | Compose split | Not started |
24+
| 8 | Compose split | Done |
2525
| 9 | Documentation & audit (D8, D9) | Not started |
2626

2727
## Phase Dependency Graph
@@ -698,7 +698,7 @@ ENV PATH=/usr/local/bin:/bin:/usr/bin:/sbin
698698
# preflight_gate carries for the BuildKit dependency edge;
699699
# it has no runtime purpose and should not ship.
700700
RUN ["/bin/busybox", "sh", "-c", \
701-
"for a in sh adduser install mkdir dirname chown chmod tr mktemp cat printf rm echo; do \
701+
"for a in sh adduser addgroup install mkdir dirname chown chmod tr mktemp cat printf rm echo; do \
702702
/bin/busybox ln -s busybox /bin/$a; \
703703
done && rm -f /tmp/.preflight-sentinel"]
704704

@@ -951,6 +951,7 @@ applet symlinks — busybox `sh` provides them internally.
951951
|-----------|-----------------------------------------------------|
952952
| `sh` | Entry script interpreter (`#!/bin/sh`) |
953953
| `adduser` | §4.1 — create `torrust` user at first boot |
954+
| `addgroup`| §4.1 — create `torrust` group before `adduser` so a downstream `install -g torrust` resolves the group |
954955
| `install` | `inst()` helper — seed config/database templates |
955956
| `mkdir` | Create volume subdirectories, auth-key dirs |
956957
| `dirname` | §7.1 — resolve parent of auth-key and database paths |
@@ -2307,6 +2308,7 @@ dependency-free and shellcheck-clean.
23072308

23082309
_validate-prod-env:
23092310
@sh -uc '\
2311+
: "$${USER_ID:?required (numeric host UID owning ./storage)}" && \
23102312
: "$${TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN:?required}" && \
23112313
: "$${TORRUST_INDEX_CONFIG_OVERRIDE_DATABASE__CONNECT_URL:?required}" && \
23122314
: "$${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN:?required}" && \
@@ -2318,6 +2320,15 @@ dependency-free and shellcheck-clean.
23182320
# defence-in-depth only — the config probe (exit 3 on
23192321
# empty connect_url) and the MySQL entrypoint (rejects
23202322
# empty root password) are the authoritative gates.
2323+
#
2324+
# NOTE: `USER_ID` is required because `compose.yaml`
2325+
# references `${USER_ID}` with no default (it is an
2326+
# operator-supplied numeric selector, not a credential
2327+
# and not an environment-coupled hostname). An empty
2328+
# value would propagate to the entry script's
2329+
# `adduser -u ""` call and fail loudly inside the
2330+
# container; failing earlier in the wrapper matches the
2331+
# spirit of the §8.1 defence-in-depth contract.
23212332

23222333
up-prod: _validate-prod-env
23232334
docker compose --file $(COMPOSE_FILE) up -d --wait
@@ -2355,6 +2366,105 @@ compositions pass `--file compose.yaml` explicitly.
23552366
Describe the two files with a clear "for development" / "for
23562367
deployment template" distinction.
23572368

2369+
### 8.6 Bring-up addendum (issues surfaced & fixed inline)
2370+
2371+
Phase 8 was validated end-to-end against `podman-compose
2372+
1.5.0` / `podman 5.8.2` (as the closest available stand-in
2373+
for `docker compose v2`'s additive override merge). The
2374+
bring-up exposed five latent bugs in earlier phases that
2375+
only surface when the full stack actually starts. All five
2376+
were fixed in the same PR rather than deferred, since each
2377+
would block any operator following the documented workflow.
2378+
2379+
1. **Test binaries baking compile-time paths** (Phases 6, 7).
2380+
`packages/index-config-probe/tests/binary.rs` and
2381+
`packages/index-entry-script/tests/seed_sqlite.rs`
2382+
resolved the binary / shell-library under test via
2383+
`env!("CARGO_BIN_EXE_…")` and `CARGO_MANIFEST_DIR`.
2384+
Both bake the build-time path into the test executable
2385+
and break under cargo-nextest's archive →
2386+
`--extract-to``--target-dir-remap` flow used in the
2387+
container `test` stage. **Fix:** drive the contracts
2388+
through the library API (probe) and embed the shell
2389+
library at compile time via `include_str!` materialised
2390+
to a `tempfile` at run time (entry-script). Same model
2391+
as `packages/index-config/tests/shipped_samples.rs`.
2392+
2. **Missing `addgroup` in the curated busybox applets**
2393+
(Phase 4 / 7). Distroless `cc-debian13` ships
2394+
`/etc/passwd` and `/etc/group`, but busybox
2395+
`adduser -D -u UID NAME` writes only `/etc/passwd`
2396+
`getgrnam("torrust")` then fails, breaking the entry
2397+
script's `install -g torrust …` step with `unknown
2398+
group torrust`. **Fix:** add `addgroup` to the curated
2399+
symlink loop in the release runtime base, change the
2400+
entry script to `addgroup -g "$USER_ID" torrust`
2401+
followed by `adduser -D -s /bin/sh -u "$USER_ID" -G
2402+
torrust torrust`, and guard both with idempotent
2403+
`grep`-of-`/etc/{passwd,group}` checks so a container
2404+
restart is a no-op rather than a fatal exit under
2405+
`set -e`.
2406+
3. **`jq` shipped without its shared libs** (Phase 2). The
2407+
`jq_donor` stage installed jq via `apt-get` but the
2408+
runtime stages copied only `/usr/bin/jq`; the binary
2409+
then aborted with `libjq.so.1: cannot open shared
2410+
object file` inside the lean `cc-debian13` runtime base.
2411+
**Fix:** copy `libjq.so.1` and `libonig.so.5` from the
2412+
donor alongside the binary, and add an `ldd`-based
2413+
allow-list assertion to the `jq_donor` stage so a future
2414+
donor-base upgrade that drags in a new transitive dep
2415+
fails the build instead of silently producing a broken
2416+
image. The allow-list is `libc.so.6 libjq.so.1
2417+
libm.so.6 libonig.so.5 ld-linux-x86-64.so.2
2418+
linux-vdso.so.1`; the parser strips path prefixes from
2419+
`ldd`'s column 1 so absolute paths
2420+
(`/lib64/ld-linux-…`) and bare sonames are treated
2421+
identically.
2422+
4. **Empty-string env vars treated as configuration TOML**
2423+
(Phases 3, 8). `Info::from_env` called `env::var(…).
2424+
ok()` directly, which returns `Some("")` for an
2425+
exported-but-unset variable. The compose baseline's
2426+
`TORRUST_INDEX_CONFIG_TOML=${TORRUST_INDEX_CONFIG_TOML}`
2427+
then propagated an empty string into the container,
2428+
which the loader treated as a zero-byte TOML and
2429+
rejected with "Missing mandatory option:
2430+
logging.threshold". **Fix:** `Info::from_env` now
2431+
filters empty strings out of both `TORRUST_INDEX_CONFIG_TOML`
2432+
and `TORRUST_INDEX_CONFIG_TOML_PATH`, and `compose.yaml`
2433+
uses Compose's bare-name pass-through form
2434+
(`- TORRUST_INDEX_CONFIG_TOML`, no `=`) for those two
2435+
optional inline-TOML envs so an unset host var is
2436+
omitted entirely rather than forwarded as empty.
2437+
5. **Unqualified Docker Hub image names** (Phase 8).
2438+
`mysql:8.0.45`, `dockage/mailcatcher:0.8.2`, and
2439+
`torrust/tracker:develop` all relied on a
2440+
short-name-resolution prompt that podman cannot honour
2441+
without a TTY (and that operators should not be relying
2442+
on regardless). **Fix:** fully qualify all three as
2443+
`docker.io/...` in `compose.yaml` /
2444+
`compose.override.yaml`. This is portable across both
2445+
docker and podman and immune to per-host
2446+
`unqualified-search-registries` configuration.
2447+
6. **`USER_ID` missing from `_validate-prod-env`** (Phase 8).
2448+
`compose.yaml` references `${USER_ID}` with no default
2449+
(it is an operator-supplied numeric selector, not a
2450+
credential and not an environment-coupled hostname, so
2451+
the §8.1 bare-`${VAR}` rule does not strictly apply —
2452+
but neither does the `${VAR:-default}` selector
2453+
convention, since there is no safe cross-environment
2454+
default). Without validation, an unset `USER_ID`
2455+
propagated to the entry script's `adduser -u ""` call
2456+
and failed deep inside container start. **Fix:** add
2457+
`USER_ID` to the `_validate-prod-env` required list so
2458+
the wrapper fails fast with a clear message before any
2459+
container starts. The entry-script-side failure remains
2460+
the authoritative gate; this is defence-in-depth in the
2461+
same spirit as the credential checks.
2462+
2463+
These inline fixes preserve the Phase 8 deliverable's scope
2464+
(the compose split itself) while ensuring the documented
2465+
`make up-dev` / `podman-compose up` flows actually reach a
2466+
healthy `200 OK` on `:3001`.
2467+
23582468
---
23592469

23602470
## Phase 9 — Documentation & Audit (D8, D9 docs part)
@@ -2394,6 +2504,19 @@ consumers), the PEM+PATH mutual-exclusion enforcement
23942504
`TORRUST_INDEX_DATABASE_DRIVER` (now a TOML-selection
23952505
knob only, no longer a runtime database dispatcher).
23962506

2507+
### 9.1.3 Add `mailcatcher` CI lint on `compose.yaml`
2508+
2509+
Phase 8 §8.1 prescribed a CI lint that re-runs the
2510+
`grep -nriE 'mailcatcher|MAILER|SMTP|smtp_'` audit against
2511+
`compose.yaml` to catch accidental re-introduction of the
2512+
dev sidecar into the production-shaped baseline. Land it
2513+
as a workflow step (or a `make lint-compose` target wired
2514+
into the existing CI pipeline) so the audit trail is
2515+
enforced rather than reviewer-discretionary. The check
2516+
must inspect `compose.yaml` only — the override file is
2517+
*expected* to mention `mailcatcher` and matching it there
2518+
would be a false positive.
2519+
23972520
### 9.2 Internal code audit for vendored `su-exec`
23982521

23992522
Add `contrib/dev-tools/su-exec/AUDIT.md` with:

compose.override.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Dev sandbox extras for `compose.yaml`. Auto-loaded by
2+
# Compose v2 when present (i.e. by `docker compose up` and
3+
# `make up-dev`). Excluded by `make up-prod` via explicit
4+
# `--file compose.yaml`. See ADR-T-009 §8.2.
5+
#
6+
# What this file adds:
7+
#
8+
# - The `mailcatcher` sidecar (re-attached to `index` via
9+
# long-form `depends_on`, which Compose v2 merges
10+
# additively — short-form would silently replace the
11+
# base's tracker/mysql dependencies).
12+
# - `tty: true` on `index` and `tracker` so interactive
13+
# `docker attach` sessions get a sensible terminal.
14+
# - Permissive `${VAR:-default}` credential defaults so a
15+
# plain `docker compose up` works without operator
16+
# intervention. Production paths (`make up-prod`) bypass
17+
# this file and rely on the validated bare-`${VAR}` form
18+
# in the base.
19+
20+
services:
21+
22+
index:
23+
tty: true
24+
environment:
25+
# DEV-ONLY defaults — change for any public or production deployment.
26+
- TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN=${TORRUST_INDEX_CONFIG_OVERRIDE_TRACKER__TOKEN:-MyAccessToken}
27+
- TORRUST_INDEX_CONFIG_OVERRIDE_DATABASE__CONNECT_URL=${TORRUST_INDEX_CONFIG_OVERRIDE_DATABASE__CONNECT_URL:-sqlite:///var/lib/torrust/index/database/index.sqlite3.db?mode=rwc}
28+
# Long-form merge — extends the base's `tracker` / `mysql`
29+
# dependencies rather than replacing them. See ADR-T-009 §8.2.
30+
depends_on:
31+
mailcatcher:
32+
condition: service_started
33+
34+
tracker:
35+
tty: true
36+
environment:
37+
# DEV-ONLY default — change for any public or production deployment.
38+
- TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN=${TORRUST_TRACKER_CONFIG_OVERRIDE_HTTP_API__ACCESS_TOKENS__ADMIN:-MyAccessToken}
39+
40+
mysql:
41+
environment:
42+
# DEV-ONLY credentials — change for any public or production deployment.
43+
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root_secret_password}
44+
- MYSQL_USER=${MYSQL_USER:-db_user}
45+
- MYSQL_PASSWORD=${MYSQL_PASSWORD:-db_user_secret_password}
46+
47+
mailcatcher:
48+
image: docker.io/dockage/mailcatcher:0.8.2
49+
networks:
50+
- server_side
51+
ports:
52+
- 127.0.0.1:1080:1080
53+
- 127.0.0.1:1025:1025

0 commit comments

Comments
 (0)