Skip to main content
Version: 12 - TBD

Offline / Air-gapped Installation

Some environments do not permit the install host to reach the Docker/OVH container registry — for example a segregated or air-gapped network where the security policy forbids outbound registry egress. The setup.sh --offline mode installs Cloudomation Engine on such a host: every container image is loaded from a bundle prepared beforehand, so the deploy pulls no images at install time.

This page covers the offline install path end to end. For the standard (registry-connected) install and the full setup.sh flag reference, see the Engine Installer page.

How it works

A normal install pulls the service and base images from the Cloudomation OVH registry (and a few images from Docker Hub) while bringing the stack up. An offline install replaces that with two steps:

  1. Build the bundle once, on an internet-connected machine — a helper script (bundle-images.sh) pulls every image the deployment needs and packs them, together with the installer, into a single cloudomation-offline-bundle.zip.
  2. Install on the target host from the bundlesetup.sh --offline loads every image from the bundle, skips the registry login, and renders pull_policy: never on every Compose service so the deploy attempts no registry pull.

Cloudomation can also provide the pre-built bundle for you, in which case you skip step 1 and start at Install on the target host.

Step 1 — Build the bundle

Run bundle-images.sh on an internet-connected machine that has:

  • a working Docker engine;
  • zip / unzip;
  • registry access — docker login 0m0k4g04.c1.de1.container-registry.ovh.net using your registry-secret.txt credentials, so the cloudomation/* service images can be pulled;
  • the extracted release setup.zip as the working directory (so that ./container/templates/setup.yml and ./setup.sh exist), or a checkout of the setup repository at the target release branch.
# from the extracted setup.zip (or a setup-repo checkout at the release branch)
docker login 0m0k4g04.c1.de1.container-registry.ovh.net
./bundle-images.sh

The script reads the image references straight from setup.yml, so the bundle always matches the release you extracted. It pulls each image, docker saves and gzips it, adds the installer image and setup.sh, and produces a single file:

cloudomation-offline-bundle.zip
└── offline-bundle/
├── setup.sh
├── setup-<tag>.tar.gz # the installer image
├── images/*.tar.gz # every service and base image
└── RUNBOOK-offline-install.md

The default image set (self-signed certificate, self-service portal disabled) is: cloudomation-workspace, cloudomation-sandbox, cloudomation-frontend, cloudomation-git, auth-service, postgres, hashicorp/vault, nginx:alpine, alpine:latest, and the installer image. Two environment toggles adjust it:

VariableWhen to set it
INCLUDE_SSP=trueThe license has ssp_enabled: true (adds the self-service-portal image)
INCLUDE_CERTBOT=trueOnly for --cert-mode letsencrypt, which is not usable air-gapped
note

The signed license.yml is intentionally not part of the bundle. Cloudomation provides it separately, and it is placed on the target host at install time (see the next step). A bundled license would be an unsigned placeholder that fails the installer's signature check.

Transfer cloudomation-offline-bundle.zip to the target host by whatever channel the environment allows.

Step 2 — Confirm the target-host prerequisites

Because the offline path renders pull_policy: never, the deploy fails loudly if anything the stack needs is missing. Confirm the following on the target host before installing:

  1. Docker engine and Compose plugin are present and working — docker ps and docker compose version both succeed. The installer drives the host Docker via its socket; the host itself needs no package installs (apt-get runs only when building the setup image, never on the target host).
  2. Internal DNS: the FQDN you will use as SELF_URL resolves on the host to the host itself — getent hosts <fqdn> returns the correct address. The browser used to reach the workspace must resolve it too.
  3. Clock within TLS skew: date -u is correct. Without outbound NTP the clock can drift, and a wrong clock makes TLS handshakes fail.
  4. Disk: a few GB free for the loaded images plus workspace data (df -h).

Install on the target host

unzip cloudomation-offline-bundle.zip
cd offline-bundle
cp /path/to/license.yml ./license.yml # the signed license Cloudomation gives you
./setup.sh --offline --cert-mode selfsigned

--offline loads the installer image from setup-*.tar.gz and every images/*.tar.gz before starting, skips the registry login (no registry-secret.txt required), and sets pull_policy: never on every service.

setup.sh requires ./license.yml in the offline-bundle/ directory and exits immediately if it is missing — copy the signed license in before running the installer.

Certificate mode

Choose a certificate mode that needs no outbound access:

ModeUse
selfsigned (default)Generates a self-signed certificate matching SELF_URL. The browser shows a trust warning, which is acceptable for a first / proof-of-concept install.
existingUse a certificate issued by the customer's internal CA: --cert-mode existing --cert <crt> <key>. Switching to this later needs no re-bundling.

The letsencrypt mode requires outbound access to the ACME service and an inbound HTTP-01 challenge, so it is not usable in an air-gapped install.

Interactive prompts

setup.sh prompts for the core settings (pre-seed any of them as an environment variable of the same name to reduce prompting):

SettingDescription
ORGANIZATION_NAMEYour organization name — must match the license.
SELF_URLhttps://<internal-fqdn> from the prerequisite check. Drives the certificate common name and the frontend API URLs.
WORKSPACE_NAMEWorkspace name. The default cloudomation is fine.
BOOTSTRAP_USEREmail address of the initial all-permissions user.

Verify

docker compose ps # all services Up / healthy
curl -k https://<internal-fqdn>/alive # -> alive/ok

Then open https://<internal-fqdn> in a browser (accept the self-signed warning when using --cert-mode selfsigned) and log in as the bootstrap user.

note

An offline install stays healthy without any outbound access. License validation is a local signature check performed at install time; the auth service writes its usage report to a local file rather than reporting it outbound; and Vault runs inside the stack (hashicorp/vault, auto-unsealed) with no dependency on an external Vault. The installation does not degrade later for lack of registry or license egress.

Troubleshooting

SymptomCause and fix
pull access denied / manifest unknown during docker compose upAn image is missing from the bundle, or a tag moved. Run docker images to see what loaded; every service tag the installer renders into <install-dir>/docker-compose needs a matching loaded image. Re-build the bundle from the correct release checkout.
--offline set but no setup-*.tar.gz found in the bundle rootThe bundle is missing the installer image tarball. Re-run bundle-images.sh, which builds or copies it.
--offline set but no ./images/*.tar.gz foundYou are not in the offline-bundle/ directory, or the bundle shipped without images. Check ls images/.
Certbot / ACME errors--cert-mode letsencrypt is selected. Use selfsigned or existing.

For a failed fresh install with nothing to roll back to, run docker compose down and re-run setup.sh --offline once the cause is fixed.