Skip to main content

Air-gapped and private registries

The normal deployment pulls public images from Docker Hub. For a private registry or a fully air-gapped host, keep TOW_VERSION as the release selector and override every TOW image repository together.

Point the stack at your registry​

Copy the shipped example to the ignored override file and replace the registry namespace:

cp compose.override.example.yaml compose.override.yaml
compose.override.yaml
services:
schema-migrate:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}
backend:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}
search-worker:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}
email-worker:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}
inbound-email-worker:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}
migration-worker:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}
frontend:
image: registry.example.com/tow/frontend:${TOW_VERSION:?Set TOW_VERSION in .env}
docs:
image: registry.example.com/tow/docs:${TOW_VERSION:?Set TOW_VERSION in .env}
authentik-bootstrap:
image: registry.example.com/tow/backend:${TOW_VERSION:?Set TOW_VERSION in .env}

Compose loads compose.override.yaml automatically next to compose.yaml, and it is operator-owned, so updates never touch it. If you also carry other override entries (published ports, volume mappings), keep them in the same file.

Mirror the images​

Mirror the TOW images for your pinned release:

  • anistow/tow-backend:<TOW_VERSION>
  • anistow/tow-frontend:<TOW_VERSION>
  • anistow/tow-docs:<TOW_VERSION> (only with the docs profile)

And the pinned third-party images from compose.yaml:

  • pgvector/pgvector:pg16
  • getmeili/meilisearch:v1.12
  • nginx:1.27-alpine
  • With the authentik profile: ghcr.io/goauthentik/server:<AUTHENTIK_TAG>, postgres:16-alpine, redis:7-alpine

For a fully air-gapped host, pre-load them with docker save / docker load or a registry mirror inside the enclave.

Operations scripts without a kit clone​

Air-gapped hosts that cannot clone the deploy kit can extract the version-matched operations scripts from the backend image they already have:

ops_image="$(docker compose images -q backend)"
ops_container="$(docker create "$ops_image")"
docker cp "${ops_container}:/usr/local/share/tow-ops/." scripts/
docker rm "$ops_container" >/dev/null
chmod 750 scripts/*.sh

Everything else, including installation questions, upgrades, and backups, works exactly as on a connected host once the images are reachable.