Offline install
Use the offline bundle when you cannot, or do not want to, pull from our registry. That covers a cluster with no AWS account behind it, and a network with no route to the registry at all.
The bundle is one tarball. It holds the Helm chart, the Align images as docker save files, a
manifest of the digests the images were built from, and a SHA256SUMS file over everything.
Get the bundle
Ask sales@align.tech for the release you want. We build it and send you two things:
align-offline-<release>.tar- Its sha256, on a different channel than the tarball (a call, or a separate email)
The images are linux/amd64 only.
Optional, on request: the k3s airgap images for a k3s version you name.
Verify it
The checksum we send protects you from a bundle that was altered on the way. Hash the tarball
yourself first, with your own tool. The scripts inside the bundle are part of the bundle, so a
tampered tarball could carry a verify.sh that always says yes.
sha256sum align-offline-<release>.tar # macOS: shasum -a 256 align-offline-<release>.tar
# compare with the hash we sent you on the other channel, and stop if it differs
tar -xf align-offline-<release>.tar
bash align-offline-<release>/verify.sh align-offline-<release>.tar --expected-sha256 <hash>
verify.sh refuses a tarball with no --expected-sha256. --allow-unverified-origin overrides
that, and then it proves only that the bundle is self-consistent.
verify.sh needs no network. It needs cosign (v2.4 or later) and jq on the machine. It fails unless:
- the tarball matches the hash we sent you
- every line of
SHA256SUMSis well formed, and every file matches it - no file is present that
SHA256SUMSdoes not list, and nothing is a symlink - each image file holds exactly the image tag the manifest names
- each image has a cosign signature in
signatures/thatcosign verify-blob --offlineaccepts, with the Fulcio chain, Rekor key and CT log key all taken fromtrust/trusted_root.json. The certificate must name ourrelease.yamlworkflow at this release's tag, with the GitHub Actions issuer. The signed payload must name the digest the manifest lists for that image.
How the signature check works with no network. Release signatures are keyless. Each one
comes with a short-lived Fulcio certificate and an entry in the Rekor transparency log. At
build time we save both next to the signature, and save Sigstore's trusted root (the Fulcio
roots and the Rekor and CT log keys) in trust/. verify.sh then checks the signature, the
certificate chain, the log entry's signed timestamp and the certificate's embedded SCT locally.
It does not look anything up. It prints cosign's own error for each signature if none passes.
What it does not prove. The trusted root travels inside the bundle, so it is covered by
SHA256SUMS and by the hash you got on a second channel, not by anything Sigstore signs for you.
If you want it independent, run cosign initialize once on a connected machine (with an empty HOME) and compare
~/.sigstore/root/targets/trusted_root.json with trust/trusted_root.json. SHA256SUMS alone shows
the files are unchanged since we built the bundle. The hash you got on a second channel shows the
bundle is the one we sent. The signatures show the image digests listed in the manifest came out of our release workflow. verify.sh does not recompute a registry digest from the docker save tarballs, so tie the tarballs to the signed digests by the outer hash, not by the signatures. To pin the trust root yourself, pass --trusted-root <file> to verify.sh.
A bundle built with --skip-signature-check carries no signatures. verify.sh refuses it unless
you pass --allow-no-signatures.
What was actually run. On 27 Oct 2026 we built a bundle for release v0.57.16 (image
migrations) and ran verify.sh with cosign v2.4.1 inside a network-less Linux namespace
(unshare -rn, empty HOME). It printed Verified OK. A one-character change to the signed
payload, and a manifest naming a different release tag, both failed in cosign. Only the
migrations image was run end to end, not the other images. cosign versions other than v2.4.1
are untested. The release workflow pins the cosign version it signs with to v2.4.1, the same one
we tested, so releases cut after that pin are signed by it. A release cut before the pin used
whatever cosign was current then.
Reject an older bundle. An older bundle with valid signatures also verifies, because each
one is genuinely ours. Pass --expected-release <tag> (for example --expected-release v0.57.16)
with the release you were told to expect. verify.sh then fails when the manifest names any
other release.
Load the images
On every machine whose container runtime will run Align pods:
bash align-offline-<release>/load.sh align-offline-<release>.tar --expected-sha256 <hash>
load.sh runs verify.sh first and loads nothing if it fails. Then it:
| Runtime | Command | What happens |
|---|---|---|
| Docker as the node runtime | default | docker load for each image |
| containerd (kubeadm, EKS and most others) | add --runtime containerd | ctr -n k8s.io images import for each image. docker load would not reach the kubelet here |
| A registry you already run | add --target-registry registry.internal:5000 | Loads, retags, and pushes each image there |
| k3s | add --runtime k3s (as root) | Copies each file into /var/lib/rancher/k3s/agent/images, which k3s imports |
It ends by printing the value to use for global.imageRegistry. On a cluster with several
nodes, k3s and plain Docker both need the images on each node that can schedule Align pods,
unless you used --target-registry.
Install the chart from the file
The chart is in the bundle, so helm install needs no registry login. The bundle also holds
values-offline.yaml, generated for that release. It sets the registry, the release tag, the tag
of each connector you bundled, and the migrations pull policy. Those last two matter: the chart
defaults connectors to latest and migrations to Always, and either one makes the cluster
reach for the registry.
helm install align align-offline-<release>/chart/align-<chart version>.tgz \
--namespace align --create-namespace \
--values values.yaml \
--values align-offline-<release>/values-offline.yaml
Pass your own file first, so values-offline.yaml wins on the image keys. If you loaded with
--target-registry, add --set global.imageRegistry=<that registry> last.
You do not need global.imagePullSecrets, because nothing is pulled. If a pod sits in
ImagePullBackOff, the image is not on that node, or its name differs from the one the chart
builds. Compare the two:
kubectl -n align describe pod <pod> | grep -i image
docker images | grep align/ # on that node
Images the bundle does not carry
The bundle holds Align's images only. These are third-party images from their own public registries, and each one needs you to load it yourself or turn its feature off:
| Image | Used when | Default |
|---|---|---|
pgvector/pgvector:pg16 | postgresql.enabled (bundled Postgres) | Off |
redis:7-alpine | redis.enabled (bundled Redis) | Off |
postgres:15-alpine | The telemetry CronJobs | On |
otel/opentelemetry-collector-contrib:0.149.0 | observability.enabled | Off |
bitnami/pgbouncer | gateway.pgbouncer.enabled | Off |
The names for pgvector, Redis, the telemetry CronJobs and the OTel collector are written into
the chart templates, and the tags above were read from this release's chart. Read the tags from
your chart before you save anything: helm template it and grep for image:. Only the
pgbouncer image has a value you can repoint. To load one yourself,
docker save it on a connected machine. To skip the telemetry CronJobs, set
telemetry.aggregation.enabled=false and telemetry.dailyRollup.enabled=false. If you use the GPU server, its vLLM image and the NVIDIA plugin image
come from elsewhere too.
What an offline install still needs
The bundle removes the registry pull for Align's images. The Brain still calls the LLM and embedding endpoints you configure, and connectors still call the tools they read. The images in the table above still come from those third-party registries. See LLM configuration for the provider options.