Skip to main content
An air-gapped data plane is the same Helm chart as any self-hosted install, with two differences: it never talks to the Popsink control plane, and every image it runs — including the connector workers it launches at runtime — comes from a registry inside your network. This page is the checklist for those two differences, then the procedure for creating the first admin account, which the control plane would otherwise do for you. Read Install on Kubernetes first for the general shape of the install; every key below is documented in full in the Helm chart reference.

1. Deployment mode

Do not install an air-gapped data plane in SELF_HOSTED mode. It renders and starts, but the background tasks retry the control plane forever, and editing or deleting a user fails with Unable to update user on control plane.

2. Images

Mirror every image into your registry at the same repository path and with the tag the chart pins, then point the chart at it. global.imageRegistry overrides the registry of every chart-rendered image in one key. It does not cover k8sRegistryHost.
k8sRegistryHost is the one that gets missed. Connector workers, backfill loaders and maintenance jobs are not part of the chart: the data plane creates them as pods at runtime, with the image <k8sRegistryHost>/onprem/connectors/<image> (and onprem/backfill/…, onprem/jobs/…). If it still points at Popsink’s registry, the install looks healthy and every connector you create sits in ImagePullBackOff.Mirror at least the images of the connectors you plan to use, and ask Popsink for the list matching your release.
Keep the broker tag (kodansu.image.tag) exactly as pinned by the chart: the object-store layout it writes is tied to that tag, and kotatsu must match it.

Pull secrets

Pick one:
  • imagePullSecret.create: true with registry and token set to your registry. The secret is attached to every pod, including the connector workers’ service account.
  • A secret you manage yourself, listed in global.imagePullSecrets. In that case also set kora.image.pullSecrets: [] and postgresql.image.pullSecrets: []; their defaults reference the secret imagePullSecret.create would have made, and kubelet warns about it.
If your registry needs no credentials, use the second option with an empty global.imagePullSecrets.

The chart itself

Transfer the packaged chart (helm pull oci://ghcr.io/popsink/charts/data-plane --version <version>) and install from the .tgz. Its sub-charts — Bitnami common and postgresql, kora — are already vendored inside it. Do not run helm dependency build inside the isolated network: it fetches from registry-1.docker.io and ghcr.io.

3. Broker storage

The broker needs an object store reachable from the cluster. Inside an isolated network that is a self-hosted S3 server — MinIO or RustFS, an on-premises IBM COS appliance, or another S3 endpoint. There is no IRSA, so credentials are static keys.
kodansu.storage.aws.region is required even when the store has no notion of region. kotatsu.s3.* inherits all of this from the broker, so there is nothing to repeat if you enable kotatsu. The bucket must exist before the install, with versioning and object lock off — see what every backend must provide.

4. Database and schema registry

  • PostgreSQL: the bundled sub-chart (postgresql.enabled: true, keep primary.persistence.enabled: true) or your own (postgresql.enabled: false + externalDatabase.*).
  • Kora needs a role and a database that already exist — the chart does not create them, and the install aborts with the SQL to run. Kora can also share the data plane’s own database. See the chart reference.
  • No schema registry (kora.enabled: false, schemaRegistry.url empty): supported in STANDALONE. Payloads are then JSON, and the connector wizard only offers the connector types that do not need Avro (connectorCatalog.requiresSchemaRegistry).

5. Secrets

No wizard generates these for you in an air-gapped install. Generate them locally and store them in your secret manager before installing.
connectorConfigEncryptionKey.key must never change: every stored connector configuration is encrypted with it. Back it up.

6. Network

  • Ingress: ingress.enabled, ingress.hostname, ingress.ingressClassName, and ingress.tls with ingress.existingSecret holding a certificate from your internal CA. Set ingressUrl to the URL users open, even though STANDALONE does not require it.
  • Name resolution: if sources or targets are not resolvable by the cluster’s DNS, add them under hostAliases.
  • Network policies: if you enable networkPolicy or workerNetworkPolicy, add extraEgress rules towards your sources and targets, the object store, and the registry.
  • Leave closed: allowSelfRegistration: false and allowDesignLogin: false (both defaults).

Example values.yaml

values.yaml (air-gapped — abridged)
Then install from the transferred chart:

Create the first admin account

With no control plane, there is no SSO and no central user directory: accounts are local to the data plane. The application UI adds existing users to teams and environments but does not create accounts, so the first one is created in the admin panel.
1

Enable the admin panel

Set enableAdminPanel: true and adminCredentials (username and password), then helm upgrade. The panel is read/write over every table, so only enable it when the ingress is internal.
2

Log in to the panel

Open https://<ingressUrl host>/api/admin and log in with adminCredentials. Failed attempts are throttled: after 10 failures in 15 minutes the account is refused with 429 (loginThrottle.*).
3

Create the user

Go to users → User → + New and fill in:Click Save.
4

Log in to the application

Open https://<ingressUrl host>/ and log in with that email and password.
5

Create the first environment

A fresh install has no environment, and the application cannot be used without one. See Create the first environment below.
6

Create the other accounts

A superuser can log in to /api/admin with their own credentials and create further accounts the same way, leaving Is Superuser unchecked for regular users. Then add them to the environment from the application, or set their Active Environment ID to the environment’s UUID (listed under Env in the panel) when creating them.
7

Turn the panel off

Once the accounts exist, set enableAdminPanel: false and helm upgrade. Turn it back on only when you need to manage accounts.
If Save returns an internal error, check deploymentId first: it must be a valid UUID.

Create the first environment

Everything in the application happens inside an environment. A user who belongs to none is sent to the Environment creation page after logging in and sees nothing else — not the connectors, not the member management. Creating an account in the admin panel does not need an environment; using the application does. The admin panel lists environments but cannot create them. An environment is created through the API (POST /envs/), either from the application’s form or directly.
Log in as the superuser. With no environment, the application opens the Environment creation page, which shows the form only when the user is a superuser and deploymentMode is STANDALONE.
  • Environment name: your choice.
  • Connect a Broker: the chart’s own broker — Bootstrap servers <release>-tansu.<namespace>:9092, Security Protocol PLAINTEXT, no SASL, no certificates. This is the address the data plane itself uses (DEFAULT_KAFKA_BOOTSTRAP_SERVER). Check the service name with kubectl -n <namespace> get svc | grep tansu.
The creator becomes a member of the environment.

Troubleshooting

Further reading

Standalone mode

What a data plane without a control plane gives up.

Helm chart reference

Every value the chart exposes, component by component.

Upgrades

In STANDALONE, upgrades are fully manual.

Troubleshooting

Install and first-boot problems common to every deployment.