Files
archive-clients/docs/deployment-preflight.md
T

4.8 KiB

Deployment preflight tests

Run the deployment preflight on every new node after its qBittorrent and Syncthing stacks are running, but before starting the Archive Control daemon for normal operation or creating any routes/jobs. It is read-only: it does not contact the control daemon, alter Syncthing/qBittorrent state, or print secret contents.

The script is scripts/preflight-deployment.py. It deliberately takes paths and container names as arguments rather than assuming hostnames, compose project names, mount paths, credentials, or roles. It can therefore be used unchanged on cache and archive nodes.

Requirements

  • Run it on the Docker host being checked.
  • Use Python 3.11 or newer (the script uses the standard-library tomllib).
  • Have Docker CLI access to the daemon.
  • Have the checkout containing the script available on that host, or copy only this script into the node's compose directory.
  • Bring up qBittorrent and Syncthing first. Do not start the normal Archive Control daemon yet.

The disposable client container below uses the exact archive-client service image, mounts, environment, and network specified by that node's compose file. Consequently its qB/Syncthing API probes validate the same runtime environment the daemon will use, not the host shell's network namespace.

New-node procedure

The example uses generic names. Replace paths, compose project directories, service names, and real container names for the node being deployed.

# 1. Bring up only the local dependencies.
cd /srv/compose/syncthing-node && docker compose up -d
cd /srv/compose/qbittorrent-node && docker compose up -d

# 2. Create a temporary client from the exact production image/config, without
#    running the daemon or registering it with control.
cd /srv/compose/ArchiveControl-archive
docker compose run -d --no-deps --name archive-control-preflight \
  --entrypoint sleep archive-client infinity

# 3. Discover the real dependency container names if needed.
docker ps --format '{{.Names}}'

# 4. Run the read-only checks.
python3 /path/to/archive-clients/scripts/preflight-deployment.py \
  --client-config /srv/compose/ArchiveControl-archive/client.toml \
  --client-container archive-control-preflight \
  --syncthing-container syncthing-node-syncthing-1 \
  --qbittorrent-container qbittorrent-node-qbittorrent-1

# 5. Remove the temporary container, then start the real daemon only after a
#    successful result.
docker rm -f archive-control-preflight
docker compose up -d archive-client

If the real client configuration is mounted somewhere other than the standard /etc/archive-control/client.toml, pass that in-container location explicitly:

python3 scripts/preflight-deployment.py ... \
  --container-config /custom/path/client.toml

Required arguments

Argument Meaning
--client-config Host path to the exact client.toml that will be mounted into the daemon.
--client-container Running disposable or already-running client container to inspect and probe.
--syncthing-container Running Syncthing container for this node.
--qbittorrent-container Running qBittorrent container for this node.
--container-config Optional client.toml path inside the client container; defaults to /etc/archive-control/client.toml.

What it checks

  • The future automatic routes/<route-id> path resolves through Docker mounts to the same host path as its client-side mapping.
  • That future route path and qBittorrent content root use one client bind mount, so hard-link staging remains possible rather than silently falling back to a space-consuming copy.
  • The token, qB password, and Syncthing API-key files are non-empty regular files with no group/world permissions.
  • The client image can read its configuration and reports usable permissions, sparse-file support, and filesystem capabilities for both roots.
  • qBittorrent authentication/version compatibility and Syncthing authentication/device identity are healthy from the client container.

In particular, if syncthing.api_root is the Syncthing configuration root, bind its routes subdirectory from a dedicated directory beneath qB's data root and add a local_path_overrides mapping for that exact API path. This prevents automatic route folders being created on a small configuration filesystem while the client expects to hardlink from the qB data mount.

Failure handling

Treat a nonzero exit status as a deployment blocker. Correct the compose mounts, client.toml, secret permissions, service credentials, or service image/version that the message identifies, then rerun the same command. Do not work around a mapping failure by creating route folders manually: the route provisioner relies on those API/local path mappings being exact.