Air-gapped installation
How to install Octostar into a cluster with no public internet access.
Images come from a private registry; Helm charts come from a vendored
charts-offline/ directory; the installer is patched to skip internet-only
probes.
📝 First: request the delivery bundle from Octostar
An air-gapped install is driven from a pre-rendered delivery bundle that Octostar produces for you — you do not build the charts and images yourself. Before you begin, contact Octostar to obtain, for your target release, a bundle containing:
- a container image tarball — every image the deployment needs, in
docker saveformat;- a populated
charts-offline/directory — every Helm chart.tgzpinned inversions.yaml;- the full
octostar-singlenoderepository and the client helper scripts (offline-installer.sh,push-images.sh,setup-local-env.sh, the OPA Gatekeeper bundle, …).When you move to a new Octostar version, request a fresh bundle for that version — the images and
charts-offline/must match the release you install.
📝 The tooling operates on
octostar-singlenode— The offline tooling is not a standalone installer — it operates on the fulloctostar-singlenoderepo included in the bundle, rewriting itsinstall.sh,update-existing.sh, andhelmfile-*.yamlfiles to run fully offline. Always keep the full repo; never copy only a wrapper script.
What's in the bundle
| Item | Purpose |
|---|---|
Image tarball (e.g. octostar-image-list-vN-images.tar) | All container images, loaded and pushed to your private registry |
octostar-singlenode/ (incl. charts-offline/) | The install repo plus every chart .tgz |
Client helper scripts (bin/ + lib/) | offline-installer.sh, offline-update-existing.sh, offline-precheck.sh, push-images.sh, setup-local-env.sh, patch_charts_offline_helmfiles.py |
gatekeeper-k8s-generic/ | OPA Gatekeeper bundle for private-registry image rewrites |
Before you begin
- A private container registry reachable from all cluster nodes.
kubectl,helm,helmfile,yq,python3on the install host (or use--install-tools).- A kube context pointing at the target cluster
(
kubectl config get-contexts).
Recommended order: stage helpers → Gatekeeper → push-images.sh →
setup-local-env.sh → offline-precheck.sh → create namespaces →
offline-installer.sh → verify.
Steps
Run these from the octostar-singlenode repo root on the install host.
0. Stage the helper scripts
Copy the client helpers from the bundle into octostar-singlenode/bin/
(next to install.sh):
push-images.sh,setup-local-env.shoffline-installer.sh,offline-update-existing.sh,offline-precheck.shpatch_charts_offline_helmfiles.py- the
lib/directory (offline-patch.sh)
Keep the gatekeeper-k8s-generic/ tree from the bundle available on the host.
⚠️ Path assumptions — These scripts expect to live in
bin/next toinstall.sh,helmfile.yaml, and the chart layout. The Python helper must sit besideoffline-installer.sh(the installer invokes it). Running them from another path without adjusting options breaks path assumptions.
1. Install Gatekeeper (registry rewrite)
OPA Gatekeeper rewrites container image registry hosts for labelled namespaces
via an AssignImage mutation, so workloads pull from your private registry.
cd gatekeeper-k8s-generic
export PRIVATE_REGISTRY=your.registry.example.com:5000
# Preview the images the rendered chart will use
envsubst < values/values-airgap.yaml \
| helm template gatekeeper ./vendor/gatekeeper-3.22.0.tgz -f - \
| grep -E '^\s+image:' | sort -u
# Install
./scripts/helm-install.sh
Then configure the rewrite:
# 1. Set data.privateRegistry in policies/assignimage/config-registry.yaml, then:
kubectl apply -k policies/assignimage # the -k (Kustomize) is REQUIRED
# 2. Confirm the live object resolved the registry:
kubectl get assignimage rewrite-pod-containers \
-o jsonpath='{.spec.parameters.assignDomain}{"\n"}'
# Expected: your mirror host:port. If you see REPLACE_WITH_PRIVATE_REGISTRY, re-apply with -k.
# 3. Label ONLY the namespaces whose images should be rewritten:
kubectl label namespace YOUR_NS private-registry-enforced=enabled
⚠️ Common Gatekeeper pitfalls
- Always apply with
-k.kubectl apply -f policies/assignimage/assignimages.yamlalone skips Kustomize replacements and leavesassignDomainasREPLACE_WITH_PRIVATE_REGISTRY— no rewrite happens.- Use fully qualified image names in manifests (e.g.
docker.io/library/busybox:1.36, notbusybox). Bare names have no registry domain to rewrite.- This bundle is registry-rewrite only —
disableValidatingWebhook: trueanddisableAudit: true. Do not applypolicies/constraints/unless you flipdisableValidatingWebhook: falseinvalues/values-airgap.yamland reinstall.
The Gatekeeper images (openpolicyagent/gatekeeper:v3.22.0 and
openpolicyagent/gatekeeper-crds:v3.22.0) are included in the bundle's image
tarball and pushed to your registry in the next step.
2. Push images — push-images.sh
Loads the docker save tarball from the bundle, retags each image under your
private registry (stripping the upstream host segment, e.g. docker.io/... →
registry.client.com/...), and pushes.
./bin/push-images.sh --tarball PATH --registry REGISTRY \
[--username USER] [--password PASS] [--image-list PATH]
--tarball— the image tarball from the bundle.--registry— host only, no scheme or trailing slash (e.g.registry.client.comor100.65.53.20:5000).--image-list— defaults toocto_image_list.txtin the repo root; comments (#) and<none>lines are skipped.- Credentials may come from
--username/--password, theREGISTRY_PASSWORDenv var, or an interactivedocker login.
Registries that don't auto-create repositories (AWS ECR, some hardened
Harbor/Nexus): supply a create-repo command with --create-repo-cmd (or
PUSH_CREATE_REPO_CMD). It runs once per distinct repository before pushing,
with {repo}, {registry}, and {image} placeholders substituted. Example
for ECR:
./bin/push-images.sh --tarball images.tar \
--registry 123.dkr.ecr.eu-west-1.amazonaws.com \
--create-repo-cmd 'aws ecr describe-repositories --repository-names {repo} --region eu-west-1 >/dev/null 2>&1 || aws ecr create-repository --repository-name {repo} --region eu-west-1'
A nonzero exit from the create command is non-fatal (usually "already exists").
3. Generate local-env.yaml
./bin/setup-local-env.sh [--directory DIR | -d DIR | --installer-root DIR]
Interactively builds local-env.yaml from local-env.template.yaml (must
exist in the repo root). Prompts for namespace, domains, and which optional
components to enable. Requires yq.
For PostgreSQL (CNPG) in air-gap, set postgresql.imageName,
postgresql.operatorImageRepository, and postgresql.operatorImageTag to
match your mirrored images (the script's output notes this).
4. Pre-check compatibility — offline-precheck.sh
Strongly recommended before every install/upgrade. Non-destructive: works on a temp copy, touches no cluster.
./bin/offline-precheck.sh
# exit 0 → all install-critical patch anchors present
# exit 1 → an install-critical anchor is missing (bundle/repo drift)
It synthesizes a maximal local-env.yaml (PostgreSQL + monitoring enabled) so
every feature-gated patch is exercised regardless of your real config. If it
reports missing(fatal), the bundle is inconsistent — contact Octostar for a
corrected bundle rather than forcing the install.
5. Pre-create namespaces
offline-installer.sh does not create namespaces. Create cert-manager
and your app namespace (from local-env.yaml) first:
kubectl create namespace cert-manager
kubectl create namespace octostar-main # or your configured namespace
6. Install — offline-installer.sh
The air-gap wrapper around bin/install.sh. It patches a temporary copy of
install.sh to skip internet probes and rewrites helmfiles to use the local
charts from charts-offline/.
./bin/offline-installer.sh --context <kube-context> [ ...same flags as install.sh... ]
Prerequisites:
local-env.yamlexists (step 3).kubectl,helm,helmfile,yq,python3onPATH.- Namespaces
cert-managerand your app namespace already exist (step 5). - Cluster image pulls work via private registry + pull secrets and/or
Gatekeeper
AssignImage(step 1). - Vendored charts present under
~/octostar-singlenode/charts-offline(from the bundle), orOCTOSTAR_CHARTS_OFFLINE.
Key environment variables:
| Variable | Default | Effect |
|---|---|---|
OCTOSTAR_CHARTS_OFFLINE | $HOME/octostar-singlenode/charts-offline | Directory of vendored chart .tgz files |
OCTOSTAR_OFFLINE_HELMFILE_MAIN_NO_WAIT | 1 | Main sync runs without --wait so one slow release doesn't block the install; set 0 for strict wait |
7. Verify
kubectl get pods -n octostar-main
kubectl get svc -n octostar-main
Open https://home.<domain> once pods are ready.
Local charts (charts-offline)
The offline installer always uses chart packages from disk — never remote Helm/OCI repos.
-
Directory. Defaults to
$HOME/octostar-singlenode/charts-offline, populated by the bundle with the.tgzfiles matchingversions.yaml(e.g.kube-prometheus-stack-81.6.1.tgz,mysql-9.13.0.tgz,cert-manager-v1.19.2.tgz— note thevprefix on cert-manager). Override the location withOCTOSTAR_CHARTS_OFFLINE. -
Helmfile rewriting. On each run,
offline-installer.shcallspatch_charts_offline_helmfiles.py, which idempotently rewrites the remote-driven helmfiles in the repo root to:- set
repositories: [](so helmfile adds no chart repos), and - replace
chart:/version:pairs (Bitnami, OCI octostar charts, Jetstack, Prometheus Community, Grafana, OpenSearch, CNPG, Altinity, …) with local paths of the formchart: '{{ requiredEnv "OCTOSTAR_CHARTS_OFFLINE" }}/<archive>-{{ .Values... }}.tgz'.
Files touched include
helmfile.yaml,helmfile-monitoring.yaml,helmfile-nginx.yaml,helmfile-octostar.yaml,helmfile-postgresql.yaml,helmfile-preps.yaml,helmfile-data.yaml,helmfile-clickhouse.yaml,helmfile-metallb.yaml,helmfile-traefik.yaml,helmfile-photon.yaml,helmfile-seaweedfs.yaml,helmfile-temporal.yaml,helmfile-langfuse.yaml, andhelmfile-backup.yaml. - set
-
Not rewritten. Releases already using in-repo paths (
./helm/...,./charts/...) are left unchanged. -
First run mutates tracked YAML. These edits persist in your working tree. Commit or snapshot if you need to reproduce the same tree elsewhere.
-
Grafana plugins. Monitoring values may set
grafana.plugins: []so the stack doesn't callgrafana.comforGF_INSTALL_PLUGINS. The bundle's Grafana image bakes ingrafana-clickhouse-datasourceand pointsGF_PATHS_PLUGINSat it.
Order of operations (summary)
- Request the bundle from Octostar for your target release.
- Stage helpers (0) → Gatekeeper (1) →
push-images.sh(2) →setup-local-env.sh(3) →offline-precheck.sh(4) → create namespaces (5) →offline-installer.sh(6) → verify (7).
For upgrading an existing air-gap install, see Upgrading.