# Docker and Compose For every field, default, constraint and complete example, see the [TOML configuration reference](configuration.md)。 The image contains the validator, `rsync`, CA certificates and `/usr/bin/time`. The root filesystem is read-only; input and configuration are read-only; output and state mounts are writable. The application receives a subcommand and one configuration option: `validate|daemon --config FILE`. Build the image: ```bash docker build -f docker/Dockerfile -t panda-rpki:v0.1.0 . ``` ## Single validate run Create a TOML whose paths are valid **inside** the container: ```toml config-version = 1 mode = "validate" out = "/output" rrdp-state-dir = "/state/repository" ccr-out = "/output/result.ccr" [[anchors]] id = "anchor" tal = "/input/anchor.tal" # ta = "/input/anchor.cer" # uncomment for fixed TA mode ``` Run the provided Compose template: ```bash mkdir -p state output export CONFIG_FILE="$PWD/validate.toml" export INPUT_DIR="$PWD/input" STATE_DIR="$PWD/state" OUTPUT_DIR="$PWD/output" export PUID="$(id -u)" PGID="$(id -g)" docker compose -f docker/compose.yaml run --build --rm validator ``` The template mounts the TOML read-only at `/config/validate.toml` and invokes `panda-rpki validate --config /config/validate.toml`. Host variables only select mounts, image and container UID/GID; they are not read as validator settings. ## Local HTTPS test repository Use [validate-localhost.toml](../docker/examples/validate-localhost.toml) and [compose.localhost.yaml](../docker/compose.localhost.yaml) to run the generated HTTPS repository used by the regression tests. The TOML explicitly sets `http-root-cert = ["/fixture/certs/rrdp-ca.pem"]`; this is the generated test server CA, separate from the RPKI trust anchor. The validator uses host networking because the generated RRDP URI is `https://localhost:8443`: ```bash work="$(mktemp -d)" python3 tests/support/generate_repository.py \ --rrdp-host localhost --rrdp-port 8443 --output "$work/fixture" ln -s cases/baseline-v1/http "$work/fixture/active" mkdir -p "$work/output" "$work/state" python3 tests/support/serve_repository.py \ "$work/fixture/active" 8443 \ "$work/fixture/certs/rrdp-server.pem" \ "$work/fixture/certs/rrdp-server.key" >"$work/server.log" 2>&1 & server_pid="$!" trap 'kill "$server_pid" 2>/dev/null || true; rm -rf "$work"' EXIT for _ in $(seq 1 50); do grep -qx ready "$work/server.log" && break; sleep 0.1; done grep -qx ready "$work/server.log" export CONFIG_FILE="$PWD/docker/examples/validate-localhost.toml" export FIXTURE_DIR="$work/fixture" OUTPUT_DIR="$work/output" STATE_DIR="$work/state" export PUID="$(id -u)" PGID="$(id -g)" docker compose -f docker/compose.localhost.yaml run --build --rm validator ``` ## All five RIRs Copy the vendored [OpenBSD TALs and constraints](../tals/README.md) into an operator-managed input directory. Review the retained upstream notices, including the ARIN disclaimer. TA certificates are optional; the examples refresh them. Both all5 templates enable constraints by default: ```bash mkdir -p input state/all5 output/all5 cp tals/*.tal tals/*.constraints input/ cp docker/examples/validate-all5.toml all5.toml export CONFIG_FILE="$PWD/all5.toml" export INPUT_DIR="$PWD/input" STATE_DIR="$PWD/state/all5" OUTPUT_DIR="$PWD/output/all5" docker compose -f docker/compose.all5.yaml run --build --rm validator ``` The application still receives only `validate --config`; workers, timeouts, logging and sync policy belong in `all5.toml`. ## Daemon Daemon TOML paths are also container paths. `state-root` owns run directories, protocol state and the `latest` pointer: ```toml config-version = 1 mode = "daemon" state-root = "/data" interval-secs = 600 retain-runs = 10 run-timeout-secs = 1800 shutdown-grace-secs = 30 [[anchors]] id = "anchor" tal = "/input/anchor.tal" # ta = "/input/anchor.cer" # uncomment for fixed TA mode ``` Run the daemon template: ```bash mkdir -p daemon-state export CONFIG_FILE="$PWD/daemon.toml" export INPUT_DIR="$PWD/input" DAEMON_DIR="$PWD/daemon-state" export PUID="$(id -u)" PGID="$(id -g)" docker compose -f docker/compose.daemon.yaml up --build ``` For an all-RIR daemon use `docker/compose.all5-daemon.yaml` with an all-RIR TOML. Stop the whole Compose service for an upgrade so the state-root lock and validator child are released; do not delete `daemon-state`. ## Configuration and persistence Run `docker compose ... run --rm validator validate --config ... --check-config` only when the image command is overridden deliberately; the normal templates already pass the config. Check/print never download TA files or open RocksDB. Successful validate publication, when `publish-dir` is configured, is an immutable `publish/runs/` plus `publish/latest`; daemon publication uses its existing `state-root/runs/run_` directory. Cache files are written beside the protocol state as `.ta-cache/`. If a host path is not writable by the selected UID/GID, pre-create it and fix ownership. Preserve existing state and run history when changing images. Verify `summary.json`, `result-complete.json`, `vrps.csv`, `vaps.csv`, timing files and the `latest` link after a deployment. ## Complete configuration examples Copy a template before editing it; set `CONFIG_FILE` to the copy's absolute path. These templates use automatic **TA certificate** refresh, text/info logs and `require-complete = true`. They never download or update TAL files. | Compose file | Complete TOML | | --- | --- | | `compose.yaml` | [validate.toml](../docker/examples/validate.toml) | | `compose.localhost.yaml` | [validate-localhost.toml](../docker/examples/validate-localhost.toml) | | `compose.all5.yaml` | [validate-all5.toml](../docker/examples/validate-all5.toml) | | `compose.daemon.yaml` | [daemon.toml](../docker/examples/daemon.toml) | | `compose.all5-daemon.yaml` | [daemon-all5.toml](../docker/examples/daemon-all5.toml) | For example, from the repository root: ```bash mkdir -p input cp tals/*.tal tals/*.constraints input/ cp docker/examples/daemon-all5.toml daemon.toml export CONFIG_FILE="$PWD/daemon.toml" export INPUT_DIR="$PWD/input" DAEMON_DIR="$PWD/daemon-state" mkdir -p "$DAEMON_DIR" export PUID="$(id -u)" PGID="$(id -g)" docker compose -p panda-all5 -f docker/compose.all5-daemon.yaml up -d --build ``` For single validation, `/state` is the writable mount parent. Set `rrdp-state-dir = "/state/repository"` so both `/state/repository` and its sibling `/state/repository.ta-cache` are persistent and writable. Existing fixed-mode deployments with `rrdp-state-dir = "/state"` must plan a stopped state-layout migration or mount `/state.ta-cache` separately before enabling automatic mode. Do not silently point an existing database at an empty path. Daemon needs no such migration: `/data/state` and `/data/state.ta-cache` already share the writable `/data` mount. ## Graceful stop, edit and reload Use the same project name, Compose file and environment file on every command. The following assumes the `panda-all5` project created above: ```bash dc() { docker compose -p panda-all5 -f docker/compose.all5-daemon.yaml "$@"; } dc stop --timeout 40 validator dc ps -a cp -a "$CONFIG_FILE" "$CONFIG_FILE.bak.$(date -u +%Y%m%dT%H%M%SZ)" ``` Edit the existing top-level field to `log-format = "text"`. To enable automatic TA refresh, remove each desired anchor's `ta` field; keep its `id` and `tal`. `ta-refresh-policy = "allow-valid-cache"` allows a revalidated cache fallback and marks that run partial; `"require-fresh"` rejects the fallback. Missing or invalid cache cannot satisfy the first automatic run if all downloads fail. Check the changed file and recreate the service: ```bash dc run --rm --no-deps validator daemon --config /config/daemon.toml --check-config dc run --rm --no-deps validator daemon --config /config/daemon.toml --print-config # Continue only after Configuration valid. and the expected effective settings. dc up -d --no-build --force-recreate validator dc logs --since 2m validator ``` Recreation is intentional: editors and `sed -i` can replace the host file's inode, while an existing single-file bind mount may retain the old file. `start` or `restart` alone is not the documented configuration reload procedure. Recreation retains bind-mounted state and run history. SIGTERM prevents further cycles and allows the active child up to `shutdown-grace-secs` (30 here) to finish. If it does not finish, the controller sends TERM, then KILL after another 2 seconds. Docker's 40-second timeout leaves room for controller cleanup. It is not an unlimited wait for the current cycle. To avoid interrupting a long cycle, stop during the daemon's sleeping interval. `exited` confirms the container stopped; exit 3 means the controller recorded partial runs under `require-complete`, and does not mean stop failed. Inspect lifecycle logs to establish whether the last child finished or was interrupted. The controller logs are in `docker compose logs`; child diagnostics are in `data/runs/run_XXXXXX/validator.log` on the host. Existing logs keep their original format. JSON summary/metadata files remain JSON even with text diagnostics. `latest` exists only after a complete success; partial runs do not create or replace it, including the first run after upgrading an old state root. ## Change or disable constraints Edit the copied `input/.constraints` files, or set an anchor's `constraints` to a different file under `/input`. Set top-level `ta-constraints-enabled = false` to disable all policies, including same-stem auto-discovery. The all5 examples use `true` and explicit paths so missing files fail configuration checks. Use the stop/check/print/force-recreate steps above after editing. Validate-only runs read the configuration and policies afresh on each invocation. Do not copy vendored files over customized input files during an image upgrade; merge upstream updates deliberately and retain backups. No online TAL or policy update is performed by the validator.