Skip to main content

Air-Gapped Deployment

AlmaForge ships signed Linux packages, signed binary tarballs, signed macOS packages, and a versioned container image for import into an internal repository. The daemon and CLI have no telemetry, no license server, and no call-home path. See the FAQ. After a release is inside the air-gapped network, installation, operation, and upgrades can run without internet access when DNS, time synchronization, SSO, certificate issuance, and storage are also available internally. Review required internal network paths before mirroring artifacts. Package metadata and standalone artifact checksums verify against the AlmaForge GPG signing key.

What to mirror​

The current artifact version is v2026.1.26. For each release you plan to install or keep for rollback, copy two package repositories, one standalone artifact set, and one container image:

ChannelUpstream sourceMirrored content
APT repositoryhttps://get.almaforge.com, suite generic, component maindists/generic/InRelease, Packages, Packages.xz, and every .deb listed in Packages
YUM repositoryhttps://get.almaforge.com, flat layoutrepodata/ and every RPM referenced by the metadata
Standalone artifactshttps://get.almaforge.com/almaforge/v2026.1.26/Linux tarball and macOS packages, plus a .sha256 and .sha256.asc next to every file (including the DEB and RPM already served from the APT and YUM repositories)
Container imagequay.io/almaforge/almaforge:2026.1.26The image copied to the internal registry and pinned by digest

Add three release-level files from the root of https://get.almaforge.com: the almaforge.gpg signing key, the VERSION text file containing 2026.1.26+g5ab8dcffcb, and install.sh.

Keep every host, agent, and container pinned to 2026.1.26.

Keep the internal HTTP tree in the upstream shape. APT, YUM, direct downloads, and install.sh all expect these paths:

text
mirror/root/
|-- VERSION
|-- install.sh
|-- almaforge.gpg
|-- dists/generic/InRelease
|-- dists/generic/main/binary-amd64/Packages
|-- dists/generic/main/binary-amd64/Packages.xz
|-- dists/generic/main/binary-arm64/Packages
|-- dists/generic/main/binary-arm64/Packages.xz
|-- repodata/
`-- almaforge/v2026.1.26/
|-- almaforge_2026.1.26_amd64.deb
|-- almaforge_2026.1.26_arm64.deb
|-- almaforge-2026.1.26.x86_64.rpm
|-- almaforge-2026.1.26.aarch64.rpm
|-- almaforge-2026.1.26-linux-amd64.tar.xz
|-- almaforge-2026.1.26-linux-arm64.tar.xz
|-- almaforge-2026.1.26-darwin-arm64.pkg
|-- almaforge-2026.1.26-darwin-amd64.pkg
`-- *.sha256 and *.sha256.asc

Mirror the artifacts​

Run the commands below from a workstation with outbound access. Stage everything under one directory tree. The examples use ./mirror/root. Copy that tree into the air-gapped network through your approved transfer path, such as removable media, a one-way diode, or internal artifact promotion. Then publish it from an internal HTTP server and an internal container registry. The examples use https://repo.internal/almaforge as the internal repository root.

If your organization re-signs repository metadata, swap the upstream almaforge.gpg references in the APT and YUM host-install snippets below for your internal signing key. See the re-signing note at the end of the YUM repository section.

GPG signing key​

Every package-manager signature and standalone checksum signature verifies against the same key. Mirror the root metadata and signing key once:

Terminal
mkdir -p ./mirror/rootcurl -fsSL -o ./mirror/root/VERSION https://get.almaforge.com/VERSIONcurl -fsSL -o ./mirror/root/install.sh https://get.almaforge.com/install.shcurl -fsSL -o ./mirror/root/almaforge.gpg \    https://get.almaforge.com/almaforge.gpggpg --show-keys --with-fingerprint ./mirror/root/almaforge.gpg

Verify the fingerprint out of band before you trust the key: CC6A 9053 284B 5F5B 4DFD 2E99 A1FE 42F3 3C83 4A70.

The key is published at https://get.almaforge.com/almaforge.gpg. The same bytes are embedded below for offline release-intake records:

AlmaForge public GPG key
text
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
=J7h+
-----END PGP PUBLIC KEY BLOCK-----

APT repository​

The APT repository has one suite (generic) and one component (main). Mirror the signed InRelease, the package indexes, and every .deb referenced by Packages:

Terminal
ROOT=./mirror/root
curl -fsSL -o "$ROOT/dists/generic/InRelease" \ --create-dirs https://get.almaforge.com/dists/generic/InRelease
# InRelease is signed over every architecture's index, so apt rejects a# mirror that is missing one even if the machine does not use it.for arch in amd64 arm64; do mkdir -p "$ROOT/dists/generic/main/binary-$arch" for f in Packages Packages.xz; do curl -fsSL -o "$ROOT/dists/generic/main/binary-$arch/$f" \ "https://get.almaforge.com/dists/generic/main/binary-$arch/$f" donedone
awk '/^Filename: / {print $2}' \ "$ROOT"/dists/generic/main/binary-*/Packages | sed 's#^\./##' | sort -u | while read -r path; do mkdir -p "$ROOT/$(dirname "$path")" curl -fsSL -o "$ROOT/$path" "https://get.almaforge.com/$path" done

On every Debian or Ubuntu host, replace the upstream APT source with the internal one:

Terminal
sudo install -m 0755 -d /etc/apt/keyringscurl -fsSL https://repo.internal/almaforge/almaforge.gpg | \    gpg --dearmor | \    sudo tee /etc/apt/keyrings/almaforge.gpg >/dev/nullecho "deb [signed-by=/etc/apt/keyrings/almaforge.gpg] https://repo.internal/almaforge generic main" | \    sudo tee /etc/apt/sources.list.d/almaforge.listsudo apt-get updatesudo apt-get install -y almaforge=2026.1.26

YUM repository​

dnf reposync reads repodata/repomd.xml and pulls metadata plus RPMs in one pass. Run it from a RHEL, AlmaLinux, Rocky, or Oracle Linux host:

Terminal
sudo dnf install -y dnf-plugins-coresudo rpm --import ./mirror/root/almaforge.gpgsudo tee /etc/yum.repos.d/almaforge-upstream.repo <<'EOF'[almaforge-upstream]name=AlmaForge (upstream)baseurl=https://get.almaforge.comgpgkey=https://get.almaforge.com/almaforge.gpgenabled=1repo_gpgcheck=1gpgcheck=1EOF
sudo dnf reposync --repoid=almaforge-upstream \ --download-metadata --downloadcomps --norepopath \ -p ./mirror/root

On every RHEL, AlmaLinux, Rocky, or Oracle Linux host, replace the upstream repo:

Terminal
sudo tee /etc/yum.repos.d/almaforge.repo <<'EOF'[almaforge]name=AlmaForge (internal mirror)baseurl=https://repo.internal/almaforgegpgkey=https://repo.internal/almaforge/almaforge.gpgenabled=1repo_gpgcheck=1gpgcheck=1EOF
sudo dnf install -y --best almaforge-2026.1.26

If your organization re-signs repository metadata, regenerate the YUM metadata with createrepo_c after import and sign the new repomd.xml. Use the same pattern for APT with reprepro or aptly. Keep the original almaforge.gpg so operators can still verify upstream sidecars.

Standalone artifacts​

For minimal images, CI runners, macOS workstations, and hosts without a package manager, mirror each versioned artifact with its checksum and detached signature:

Terminal
ROOT=./mirror/rootmkdir -p "$ROOT/almaforge/v2026.1.26"for artifact in \  almaforge_2026.1.26_amd64.deb \  almaforge_2026.1.26_arm64.deb \  almaforge-2026.1.26.x86_64.rpm \  almaforge-2026.1.26.aarch64.rpm \  almaforge-2026.1.26-linux-amd64.tar.xz \  almaforge-2026.1.26-linux-arm64.tar.xz \  almaforge-2026.1.26-darwin-arm64.pkg \  almaforge-2026.1.26-darwin-amd64.pkg; do  for suffix in '' '.sha256' '.sha256.asc'; do    curl -fsSL -o "$ROOT/almaforge/v2026.1.26/$artifact$suffix" \      "https://get.almaforge.com/almaforge/v2026.1.26/$artifact$suffix"  donedone

Linux server and agent builds ship for amd64 and arm64. Mirror both if the air-gapped network runs either. The macOS packages install alma on Apple Silicon and Intel operator workstations.

Container image​

quay.io/almaforge/almaforge:2026.1.26 is a manifest list: one tag covering both the amd64 and the arm64 image. Copy it whole, in one step, so the internal tag covers both as well:

Terminal
docker buildx imagetools create \    --tag registry.internal/almaforge/almaforge:2026.1.26 \    quay.io/almaforge/almaforge:2026.1.26docker buildx imagetools inspect \    registry.internal/almaforge/almaforge:2026.1.26

Do not mirror the image with pull, tag, and push. docker pull resolves a manifest list down to the single image matching the machine it runs on, so pushing that back publishes an internal tag with one architecture in it. Nodes of the other architecture then fail to pull.

With Podman, skopeo copy --all does the same job, and the --all is what carries every architecture rather than only the local one:

Terminal
skopeo copy --all \    docker://quay.io/almaforge/almaforge:2026.1.26 \    docker://registry.internal/almaforge/almaforge:2026.1.26

imagetools inspect prints the digest of the copied manifest list alongside the per-architecture digests beneath it. Record the sha256:... of the list and pin production deployments to registry.internal/almaforge/almaforge@sha256:<digest> rather than the tag. Tags can be re-pushed. Digests cannot. See the Kubernetes deployment guide for the overlay edit that swaps quay.io/almaforge/almaforge for the internal registry. The image bundles its runtime on the UBI 9 micro base layer, so the base layer does not need a separate mirror.

Verify before publishing​

Catch corrupted transfers and missing signatures before you promote the internal repository. Import the AlmaForge signing key into a scratch keyring, verify the fingerprint, then verify repository metadata and artifact checksums:

Terminal
TRUST="$PWD/almaforge-trust.gpg"gpg --no-default-keyring --keyring "$TRUST" \    --import ./mirror/root/almaforge.gpg
gpg --no-default-keyring --keyring "$TRUST" \ --verify ./mirror/root/dists/generic/InRelease
gpg --no-default-keyring --keyring "$TRUST" \ --verify ./mirror/root/repodata/repomd.xml.asc \ ./mirror/root/repodata/repomd.xml
( cd ./mirror/root/almaforge/v2026.1.26 for artifact in \ almaforge_2026.1.26_amd64.deb \ almaforge_2026.1.26_arm64.deb \ almaforge-2026.1.26.x86_64.rpm \ almaforge-2026.1.26.aarch64.rpm \ almaforge-2026.1.26-linux-amd64.tar.xz \ almaforge-2026.1.26-linux-arm64.tar.xz \ almaforge-2026.1.26-darwin-arm64.pkg \ almaforge-2026.1.26-darwin-amd64.pkg; do gpg --no-default-keyring --keyring "$TRUST" \ --verify "$artifact.sha256.asc" "$artifact.sha256" sha256sum -c "$artifact.sha256" done)

The manifest list digest upstream should match the one the internal registry reports after the copy. If a registry-side re-serialization changes it, pin Kubernetes deployments to the internal digest.

Save the verification output with the release intake record before the mirror crosses into the air gap.

Publish inside the air gap​

Copy mirror/root/ into the air-gapped network and serve it as https://repo.internal/almaforge/. The internal HTTP server must preserve paths exactly. APT, YUM, direct tarball downloads, macOS packages, and install.sh all depend on the same root layout.

Before hosts use that mirror, distribute these trust roots inside the network:

  • The internal repository TLS CA, if repo.internal uses a private CA.
  • The AlmaForge package GPG key, or your internal repository signing key if you re-sign package-manager metadata.
  • The internal container registry TLS CA and pull credentials, if the registry is private.
  • The cluster web TLS CA for clients and agents when you use an internal CA.

Also mirror OS prerequisites such as curl, gpg, gpgv, xz, ca-certificates, dnf-plugins-core, and package-manager dependencies.

Install from the mirror​

After those trust roots are installed, the mirror behaves like the public download host. Substitute https://repo.internal/almaforge for https://get.almaforge.com and registry.internal/almaforge/almaforge for quay.io/almaforge/almaforge in the existing guides:

For production hosts, prefer APT, YUM, or the signed tarball. Package managers verify repository metadata and package signatures. The tarball path verifies its signed checksum sidecar.

install.sh honors DOWNLOAD_URL and VERSION and runs unchanged against the internal mirror. Admit the exact mirrored copy through release intake before serving it internally.

Terminal
curl -fsSL https://repo.internal/almaforge/install.sh | \    DOWNLOAD_URL=https://repo.internal/almaforge VERSION=2026.1.26 sh

Required internal network paths​

AlmaForge has no required outbound dependency on AlmaForge-operated services. The deployment still needs internal paths for the systems you choose:

SourceDestinationWhy it matters
Operators, clients, agents, and automationCluster entry point on TCP/443Browser login, CLI login, resource access, and agent join traffic.
Load balancerEach almad host or pod diagnostics endpoint/readyz health checks remove unhealthy replicas from rotation.
almad hosts or podsState backendAuth state, resource state, cluster membership, and heartbeats.
almad hosts or podsAudit and recording backendsAudit events and session recordings must leave the host or pod before failures.
almad hosts or podsInternal IdP discovery, JWKS, and token endpointsOIDC login needs issuer metadata, signing keys, and server-side authorization-code exchange.
User workstationsInternal IdP and cluster entry pointBrowser SSO completes only when both are reachable.
Hosts, pods, and workstations during install or upgradeInternal package mirror and container registryPackage install, tarball download, and image pull.
All hosts and Kubernetes nodesInternal DNS and NTPTLS validation, audit ordering, backend resolution, and certificate lifetimes.
Hosts, pods, clients, and agentsInternal CA distribution, CRL, or OCSP endpoints if your PKI requires themTLS validation can fail closed when revocation or trust distribution is unreachable.

Several optional features and common backends need an explicit egress decision: keep the endpoint inside the network, route it through an allowlist, or leave the feature off.

  • SSO provider. Cloud IdPs (Google, Okta, Entra, Auth0) need egress to discovery, JWKS, and token endpoints. Self-hosted IdPs (Keycloak, Dex) stay inside the network.
  • Storage backends. etcd, PostgreSQL, and S3-compatible object stores like MinIO keep traffic in-cluster. AWS S3, DynamoDB, GCS, Firestore, and Azure Blob need egress to their cloud endpoints.
  • TLS certificate issuance. The built-in ACME path needs reachability from public Let's Encrypt servers, so it does not work in a sealed network. Issue from an internal CA and distribute the cert and key (see TLS certificates).
  • Optional integrations. Slack, PagerDuty, Jira, and Telegram speak to vendor APIs and are off by default. Enable them only when the egress allowlist already covers the integration's hostname. See the Slack, PagerDuty, Jira, and Telegram guides for their network paths.

Release evidence​

Keep a release intake record with the mirrored artifacts. Security and operations teams should be able to answer these questions without reopening access to the public download host:

FieldEvidence to keep
ReleasePinned 2026.1.26, gitVersion 2026.1.26+g5ab8dcffcb from the VERSION file, mirror date, and operator who approved import.
Trust rootalmaforge.gpg fingerprint and how it was verified out of band.
Repository metadataGPG verification output for APT InRelease and YUM repomd.xml.asc.
ArtifactsFile names, SHA256 values, and GPG verification output for DEB, RPM, Linux tarball, and macOS packages.
Container imageSource image tag, source digest, internal registry tag, destination digest, and registry project.
TransferTransfer medium, diode job, artifact promotion run, or other chain-of-custody record.
Extra attestationsVersion-pinned SBOM, build provenance, and third-party license bundle if your review requires them.

For organizations whose security review requires additional attestations, contact us for the version-pinned bundle corresponding to the release on the mirror.

Upgrade, rollback, and retention​

For upgrades, repeat the mirror procedure for the target release and then follow the rolling upgrade. Only version-specific paths change: almaforge/v<new-version>/... and the new container digest. The APT suite, component, and YUM layout are stable across releases, so host-side sources.list and .repo files keep working.

Keep at least the previous release on the mirror so a failed canary can roll back via apt-get install almaforge=<previous> or the RPM and container equivalents. Do not remove a package while APT or YUM metadata still references it. Either keep the referenced artifact or regenerate and re-sign the metadata before publishing a pruned mirror.

Before an upgrade, snapshot the state backend and write down rollback criteria from the Upgrades guide. Binary rollback does not undo backend schema or state changes that a newer version has already written.

Re-run the Verify before publishing sequence against the internal URLs on the same schedule as other internal repositories to catch silent corruption.

References​