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:
- 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 singlecloudomation-offline-bundle.zip. - Install on the target host from the bundle —
setup.sh --offlineloads every image from the bundle, skips the registry login, and renderspull_policy: neveron 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.netusing yourregistry-secret.txtcredentials, so thecloudomation/*service images can be pulled; - the extracted release
setup.zipas the working directory (so that./container/templates/setup.ymland./setup.shexist), or a checkout of thesetuprepository 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:
| Variable | When to set it |
|---|---|
INCLUDE_SSP=true | The license has ssp_enabled: true (adds the self-service-portal image) |
INCLUDE_CERTBOT=true | Only for --cert-mode letsencrypt, which is not usable air-gapped |
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:
- Docker engine and Compose plugin are present and working —
docker psanddocker compose versionboth succeed. The installer drives the host Docker via its socket; the host itself needs no package installs (apt-getruns only when building the setup image, never on the target host). - Internal DNS: the FQDN you will use as
SELF_URLresolves 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. - Clock within TLS skew:
date -uis correct. Without outbound NTP the clock can drift, and a wrong clock makes TLS handshakes fail. - 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:
| Mode | Use |
|---|---|
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. |
existing | Use 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):
| Setting | Description |
|---|---|
ORGANIZATION_NAME | Your organization name — must match the license. |
SELF_URL | https://<internal-fqdn> from the prerequisite check. Drives the certificate common name and the frontend API URLs. |
WORKSPACE_NAME | Workspace name. The default cloudomation is fine. |
BOOTSTRAP_USER | Email 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.
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
| Symptom | Cause and fix |
|---|---|
pull access denied / manifest unknown during docker compose up | An 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 root | The bundle is missing the installer image tarball. Re-run bundle-images.sh, which builds or copies it. |
--offline set but no ./images/*.tar.gz found | You 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.