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

  • Each configured qBittorrent and Syncthing API root resolves through Docker mounts to the same host path as the matching client local root.
  • The client uses one bind mount for its qBittorrent and Syncthing roots, 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, syncthing.api_root must refer to the Syncthing shared-data mount, not its configuration volume. For example, if Syncthing mounts its config at /var/syncthing and data at /var/syncthing/Sync, use api_root = "/var/syncthing/Sync" when local_root maps that data directory. This prevents a route folder being created in the config volume while the client writes nonce files into the data volume.

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.