Skip to content

Install in an air-gapped environment

Install, update, and support Dreadnode on a disconnected cluster — prerequisites, the procedure for each install path, and support-bundle transfer.

Terminal window
sudo ./dreadnode install --license license.yaml --airgap-bundle dreadnode.airgap

On Embedded Cluster that flag is the whole difference. Everything else on this page is about the two things you need before it will run: a license entitled to download air-gap bundles, and a release that has one built.

Air gap is not a switch you flip on an existing license. Two things have to be in place, and both come from us:

Air-gap download on your license. The entitlement differs by install path — Embedded Cluster and KOTS use one, the Helm CLI uses another. Tell us which path you’re installing and we’ll enable the right one.

A release with a built bundle. Bundles are built per release, per channel. A version that shipped before your entitlement existed has no bundle until it’s rebuilt. Name the version you intend to install so we can confirm its bundle exists before you plan the transfer.

Reach out to us to arrange both. Your path’s normal requirements still apply — VM requirements for Embedded Cluster, cluster requirements otherwise. Air gap changes how images arrive, not what the platform needs to run.

On the KOTS existing-cluster and Helm CLI paths, download the support-bundle binary before you cross the boundary and carry it in alongside the release bundle. It’s how you collect diagnostics for us later, it isn’t part of the air-gap bundle, and there’s no way to fetch it once you’re disconnected. Embedded Cluster installs it on control plane nodes for you, so that path doesn’t need this.

Terminal window
curl -L https://github.com/replicatedhq/troubleshoot/releases/latest/download/support-bundle_linux_amd64.tar.gz \
| tar xzvf -
sudo mv ./support-bundle /usr/local/bin/kubectl-support_bundle
PathBundleIngressImage handling
Embedded ClusterYes — includes k0s, Traefik, and the Admin ConsoleIncludedAutomatic, into an in-cluster registry
Existing cluster with KOTSYesYou supply itKOTS re-tags and pushes to your registry
Helm CLINo — bundles are a KOTS featureYou supply itYou mirror images yourself

Embedded Cluster is the shortest path and the one to choose unless you already run a cluster you must deploy into.

Embedded Cluster’s air-gap bundle carries the infrastructure as well as the application: k0s, the Admin Console, Traefik, and every platform image. During installation it stands up a registry inside the cluster to hold the application images, and preloads the infrastructure images and Helm charts onto each node.

1. Download the bundle on a machine with internet access.

Terminal window
curl -f 'https://replicated.app/embedded/dreadnode/stable?airgap=true' \
-H 'Authorization: <license-id>' -o dreadnode.tgz

Your license ID is the licenseID: field inside your license file. For Beta releases, replace stable with beta.

2. Extract it and confirm the bundle is there.

Terminal window
tar -xvzf dreadnode.tgz
ls dreadnode.airgap

3. Transfer all three files to the target machine, then install.

Terminal window
sudo ./dreadnode install --license license.yaml --airgap-bundle dreadnode.airgap

The installer prompts for an Admin Console password, runs its host preflight checks, and prints the Admin Console URL on port 30000. Installation takes longer than an online install — it loads every image from the bundle before the application starts.

4. Configure and deploy through the Admin Console. From here the flow matches an online install: open the console, complete the config screen, and click Deploy. See Configure via the Admin Console for what each group covers, and Build a ConfigValues file if you’d rather supply configuration on the command line with --config-values.

Installing into a cluster you already run needs an image registry inside the network that KOTS can push to. KOTS rewrites every image reference in the application to read from that registry, then re-tags and pushes the images from the bundle into it. You supply two sets of registry credentials: read-write ones to push with, which KOTS does not store, and read-only ones the cluster keeps for pulling.

The application expects a single namespace in that registry, and most registries need the namespace to exist before anything can be pushed into it. Amazon ECR is the exception — it doesn’t use namespaces.

You’ll need the application bundle for your version from us, plus the KOTS Admin Console bundle (kotsadm.tar.gz) from the kots releases page. Its version has to match your kots CLI — check with kubectl kots version — and be compatible with the application bundle. Push the Admin Console images first, then install:

Terminal window
kubectl kots admin-console push-images ./kotsadm.tar.gz <registry-host> \
--registry-username <rw-username> \
--registry-password <rw-password>
kubectl kots install dreadnode \
--kotsadm-registry <registry-host> \
--registry-username <ro-username> \
--registry-password <ro-password>

Then open the Admin Console, upload your license and the .airgap application bundle, complete the config screen, and deploy. Replicated’s air gap installation in existing clusters covers the registry compatibility requirements and the full prompt-by-prompt sequence.

This path does not bring an ingress controller — see Ingress outside Embedded Cluster.

Replicated does not build air-gap bundles for Helm CLI installs; bundles are a KOTS feature. An air-gapped Helm install means you mirror the platform images into your own registry and install the chart against that mirror. We provide the image list and the pull, tag, and push commands for your target version — ask us for them alongside the entitlement, since the list changes release to release.

Once your images are mirrored, point the chart at your registry:

global:
domain: dreadnode.example.com
imageRegistry: registry.internal.example.com
imagePullSecrets:
- name: mirror-pull-secret
airgapped: true
replicated:
isAirgap: true

global.imageRegistry replaces the registry host on every image the chart renders and leaves the repository path untouched, so mirror each image to the same path under your own host. The chart refuses to install if you set it without global.imagePullSecrets, because every workload would otherwise fail to pull. Create that Secret before you install.

Traefik ships only as an Embedded Cluster extension. On the KOTS existing-cluster and Helm CLI paths you supply your own ingress controller — and in an air-gapped network, you mirror its images too. The chart emits standard networking.k8s.io/v1 Ingress resources and does not install a controller.

Set global.ingress.className if your controller isn’t the cluster default. Traefik is what we test against; other controllers work in principle but may need controller-specific annotations via global.ingress.annotations.

Air-gapped updates carry a new bundle in and deploy it from the Admin Console. Migrations are forward-only and rollback stays disabled — see Upgrades for what runs during an upgrade and how recovery works.

On Embedded Cluster, download the new version with the same curl command you used to install, extract it, and check for the .airgap file as before. Then either upload it from the command line:

Terminal window
./dreadnode update --airgap-bundle dreadnode.airgap

Or use Version history → Upload new version in the Admin Console and choose the .airgap file. Either way the version becomes available but is not live: click Deploy next to it, review the config and preflight screens, and confirm.

On an existing cluster with KOTS, upload the new .airgap bundle through the Admin Console the same way.

A support bundle is how anything gets out of an air-gapped install — including the instance telemetry we’d otherwise receive continuously.

Generate one from the Admin Console under Troubleshoot, or with the support-bundle binary you carried in during setup. See Support bundles for both commands. If you didn’t stage that binary, there’s no way to install it from inside the enclave, and the Admin Console is your only route.

Then transfer the archive out of the enclave the same way you carried the bundle in, and send it to us. Review the contents first if your environment requires it — the bundle collects logs, cluster state, and the telemetry Secrets.

An air-gapped install has no outbound path, and the platform doesn’t need one. Two things are worth knowing when you plan a network review.

The outbound endpoints an online install uses go unused: images come from the bundle or your mirror, and the SDK stops calling out once air-gap mode is on. On the Helm CLI path that last part depends on you setting replicated.isAirgap: true — until you do, the SDK keeps trying.

Marking the deployment air-gapped turns off the features that can only reach a public host, so configuration left over from a connected install doesn’t produce outbound attempts. That covers GitHub and Google authentication — use OpenID Connect against an identity provider inside your network instead — along with the Linear and HackerOne integrations and the model catalog.

Services you point somewhere yourself are a different case, and the platform leaves them alone: E2B sandboxes, external SMTP, and Logfire reach whatever you configure. See Optional external services. E2B runs sandboxes in E2B’s cloud and cannot work without outbound access; leave the sandbox provider on its default to keep execution inside your cluster.

Most of the platform works the same disconnected as connected. The features that need a public host are switched off when the deployment is marked air-gapped, rather than left to fail in front of a user:

FeatureWhy it needs the networkDisconnected behaviour
Analytics → NotebookIts Python kernel is a WebAssembly runtime the browser downloads from a public CDN when the notebook opensThe tab is hidden. Analytics → Query covers the same project data
Web-search result iconsSite icons come from a public favicon serviceResults show a generic icon
Agent web searchThe web_search tool answers from a search provider, and the hosted one needs a public APIThe tool reports that search is unavailable. Point it at an internal provider to restore it
GitHub and Google sign-inBoth authenticate against the provider’s own public endpointsNeither appears on the sign-in page. Use OpenID Connect or local passwords
Linear and HackerOneBoth submit findings to a fixed vendor APINeither can be selected. Webhook connections still work — see below
Model pricing and limitsCost, context window, and capability metadata come from a public model catalogServed from a copy shipped in the release. Accurate as of that release

Agent web search deserves a note. An agent’s web_search tool normally answers from the hosted search backend, which reaches a public API. Disconnected, that backend is unavailable, and the tool is barred from falling back to a public search engine — so the call returns no results and says why, rather than reaching out and failing. Tasks written against a connected deployment that assume search works will still run; their searches come back empty with the reason in the tool’s warnings.

If you run a search provider inside your network, you can give the tool a real backend. A provider you have pointed at your own host is not restricted — only the one that needs no credentials and answers from a public engine is. Set the provider’s credentials and internal URL through config.sandboxExtraEnv, along with DREADNODE_WEB_SEARCH_MODE: auto to let the tool use it.

An agent can still reach a URL a task hands it. The fetch and web_extract tools connect to whatever the task or model asks for, which is what makes them useful against services inside your enclave, and they are not gated by this setting. Restricting where an agent may connect is your network’s job, not a product toggle.

Model pricing deserves a note. Disconnected, the platform reads cost and context data from a catalog bundled into the image rather than fetching a current one, so a model released after your version won’t be in it. Deployments you configure with explicit pricing are unaffected — that pricing is what the platform bills against. If you add a model the bundled catalog doesn’t recognize, set its costs on the deployment.

Findings can still leave the deployment through a webhook connection, which posts to whatever URL you give it. Point it at a ticketing system inside your network to keep the submission workflow intact.

Embedded Cluster and KOTS detect air-gapped installs and set this for you. On the Helm CLI path nothing can detect it, so set global.airgapped: true yourself — the same division of labour as replicated.isAirgap.

Left unset on a disconnected deployment, neither feature breaks loudly. The notebook sits on Initializing Python… and never finishes, and result icons silently fail to load. If you are seeing either, this value is the first thing to check.

Fonts are served by the platform itself and need no configuration. Earlier versions loaded them from a public font service on every page load, which failed in a disconnected network and left the interface in a fallback typeface.

This documentation ships inside the release. The dreadnode-docs workload serves the pages for the version you installed at /docs on the platform’s own hostname, and every help link in the application points there, so a reader on an isolated network gets the same docs as a connected one — including the API and CLI reference and full-text search, which run entirely in the browser. Nothing in the bundled copy fetches from a public host.

Two things the public site has are left out, because they are indexes of absolute URLs and the hostname is not known when the release is built: the sitemap, and the llms.txt exports on the homepage.

The bundled copy is on by default and needs no configuration. Turning it off in favour of another documentation site, and the routing it needs under your own ingress, are covered in Configure → Documentation.

Capabilities are how agents get their tooling, and each one can declare system packages, Python packages, and setup scripts that the sandbox runs when it starts. Disconnected, none of that is fetched: the runtime image already contains everything the capabilities shipped with the release declare, so each step finds its work already done.

A deployment marked air-gapped goes further and installs without network access at all, rather than relying on those steps to be quiet. Python packages resolve from what is already installed and the local cache. System packages are not requested — if one is missing, the deployment reports it as an unmet prerequisite instead of contacting a package archive it cannot reach. A request that cannot succeed is not made.

Setup scripts still run, because a capability whose tools are present has to keep working. Their contents are the capability author’s responsibility, so a script that reaches for something it does not already have will fail. That failure is bounded and reported rather than left to hang, and it is recorded distinctly from an ordinary install error, so a network review can tell the difference between a capability that could not install and one that tried to reach a public host.

A capability whose prerequisites are missing loads with those tools marked unavailable. The agent is told, so it asks you rather than calling tools that cannot work.

Two tools are the exception, because we do not redistribute them.

Caido backs the web-security capability’s proxy tooling. The capability talks to a Caido instance over the network rather than driving a local binary, so run Caido somewhere inside your network and point the sandbox at it by setting CAIDO_URL. Until you do, the capability loads but its proxy tools cannot connect, and the agent is told the tool is unavailable rather than discovering it mid-task.

Burp Suite is optional for the same capability. Supply the Community or Professional jar at /opt/burp/burpsuite.jar in a runtime image derived from ours if you want it. A Java runtime is already included, so nothing else is needed.

The browser automation tool downloads its browser on first use and cannot do that disconnected. The rest of the capability is unaffected.

Pointing sandboxes at services inside your network

Section titled “Pointing sandboxes at services inside your network”

config.sandboxExtraEnv sets environment variables on every sandbox. Use it for the endpoints above, and for internal package mirrors if you run them:

dreadnode-api:
config:
sandboxExtraEnv:
CAIDO_URL: http://caido.internal.svc.cluster.local:8080
UV_INDEX_URL: https://nexus.internal/repository/pypi/simple
PIP_INDEX_URL: https://nexus.internal/repository/pypi/simple
HF_ENDPOINT: https://huggingface.internal

Mirrors are optional. Everything the built-in capabilities need is already in the image, and these are only useful if you have added capabilities of your own that declare dependencies we do not ship. The variables are the standard ones each tool already understands, so anything a resolver or client reads from its environment works here.

Variables the platform sets for the sandbox itself, such as its server address and credentials, cannot be overridden this way. Setting one stops the API at startup with a message naming it, rather than accepting a value that would be ignored.

Installation is not a finished deployment. Continue with the post-install steps, which are the same on every path:

  1. First admin account — sign in and claim platform ownership
  2. First model deployment — every model picker stays empty until you add one
  3. Connect clients — point the TUI, CLI, and SDK at your deployment

Your Hub starts empty, and an air-gapped instance can’t pull from ours. See Loading Hub content for the export-and-carry flow that populates it.