Overview
Apache Airflow is an open-source platform for programmatically authoring, scheduling and monitoring workflows. This template deploys an Airflow 3.x stack using the CeleryExecutor: one webserver workload running the API server, scheduler, dag-processor and triggerer, Celery workers that execute tasks, Redis as the task broker and PostgreSQL as the metadata database. Workers run at a fixed replica count or, optionally, are scaled by KEDA on the queue length; DAGs are placed on a shared volume or delivered by an optional git-sync sidecar. The admin password, the JWT signing secret and the Fernet key are not template values. They come from a dictionary secret you create before installing, so they never pass through Helm. The web UI ships closed to the internet: it can trigger arbitrary code and decrypt every credential stored in an Airflow Connection, so you reach it over a port-forward until you choose to expose it.location value, which must already be a location of that GVC. Other locations the GVC has are left alone: nothing Airflow-related starts in them. There is no multi-location mode, because the shared Airflow home provisions one volume per location and the database and broker are each a single volume.What Gets Created
Prerequisites
One secret must exist before you install, and the GVC must already have the location you configure. Secrets are org-level, so no GVC flag is involved.Confirm the GVC has the location you will configure
location must name a location the GVC already has. Read the current list under spec.staticPlacement.locationLinks:location to one of those names (the default is aws-us-east-1). To add a location, see the GVC reference — every workload in a GVC runs in every location it has, so add locations to a shared GVC deliberately.Create the auth secret
jwtSecret, fernetKey and adminPassword:airflow.auth.secretName to this name.Read the secret back later
-o yaml — a bare cpln secret reveal prints only a summary table, not the values:Optional: create a git token secret for a private DAG repository
gitSync.enabled is true and the repository is private. An opaque secret with encoding plain whose payload is a personal access token; the sidecar sends it as the Git password:gitSync.auth.secretName to this name. Leave it empty for a public repository or when git-sync is off — setting it while git-sync is disabled is rejected at render.Optional: enable KEDA on the GVC
keda.enabled: true. KEDA is a GVC-level setting this template cannot turn on for you; see Keda. Export the GVC, set spec.keda.enabled: true in the exported file, apply it and read it back:cpln apply replaces a GVC’s spec rather than merging into it, so a hand-written file carrying only spec.keda removes the GVC’s locations.Installation
Install from the marketplace registry, naming the location your GVC has and the auth secret you created:UI
CLI
Terraform
Pulumi
Configuration
Location
This workload location is deactivated because maxScale is set to 0., which is what a healthy install looks like, not a fault.
PostgreSQL Metadata Database
RELEASE_NAME-airflow-config. Set postgres.config.password at first install: the shipped placeholder is published, and PostgreSQL reads the password only when the data directory is initialized, so it cannot be changed through values afterwards.
Redis Broker
Airflow
airflow.celeryWorker.replicas (or KEDA) is the only horizontal scaling knob: you scale task execution, not scheduling. The admin account is provisioned by Airflow’s SimpleAuthManager, which has no SSO or LDAP support; the webserver rewrites its password file from the secret on every container start.
Storage
/opt/airflow by the webserver and every Celery worker, so they read the same DAGs and write to the same log directory. The database and broker volumes are sized separately under postgres.volumeset.capacity and redis.volumeset.capacity.
Access
firewallConfig.inboundAllowCIDR applies to the webserver only. With the shipped [] the canonical endpoint returns 403 and the UI is reachable only over a port-forward; a change takes up to a few minutes to take effect.
internalAccess controls which workloads inside the org may reach Airflow’s own workloads:
workload-list you only list your own clients: the template merges in the webserver, workers, database and broker so they can keep reaching each other, and allows the KEDA operator onto the broker when keda.enabled is true.
DAG Delivery With git-sync
git-sync sidecar runs on the webserver and worker workloads, clones the repository under /opt/airflow/dags-sync on the shared volume, and the DAGs folder on both tiers points at the synced checkout (plus gitSync.subPath when set). gitSync.repo and gitSync.branch are required when git-sync is on.
KEDA Autoscaling
airflow.celeryWorker.replicas.
Connecting
First Run
Install with the UI closed
firewallConfig.inboundAllowCIDR: [].Tunnel to the UI and sign in
http://localhost:8080 and log in with airflow.admin.username and the adminPassword from your auth secret. There is no setup wizard; the admin account already exists.Expose it only if you need to
firewallConfig.inboundAllowCIDR, then allow a few minutes for the firewall change to take effect before testing the canonical endpoint.API Access
Airflow 3.x issues JWT access tokens signed with yourjwtSecret. Request one with the admin credentials and pass it on the v2 API:
airflow.auth.jwtExpirationTime seconds.
Operations
Deploying DAGs
With git-sync off, DAG files go in/opt/airflow/dags on the shared volume; the dag-processor picks up a new file within airflow.scheduler.dagDirListInterval seconds. Copy a local folder in through the webserver container (--stdin is required, and the archive is base64-encoded because cpln workload exec is not binary-safe):
gitSync.enabled: true, commit DAGs to the configured repository and branch instead; the sidecar syncs them every gitSync.period.
Backing Up
The chart schedules no backups and no volume set snapshots. The metadata database — every Connection, Variable, DAG run and task history — is the one thing to back up; the broker is transient and DAGs live in your repository or on the shared volume. Two options: Take a snapshot of the database volume set:pg_dump, which exists in the postgres:18 image. The dump is base64-encoded in transit because cpln workload exec corrupts binary output:
Restoring a Backup
Restore apg_dump archive into the metadata database, then restart the webserver and workers so they re-read it. Stop triggering DAG runs while the restore runs:
cpln volumeset snapshot restore) is not verified for this template.
Rotating the Admin Password
The webserver rewrites itsSimpleAuthManager password file from the secret on every container start, so changing adminPassword and restarting the webserver is enough. A secret change is not picked up by a running replica, so redeploy after changing it.
Export the secret, edit only adminPassword (leave fernetKey untouched), re-apply it and confirm the change:
jwtSecret can be rotated the same way — redeploy both the webserver and RELEASE_NAME-airflow-celery-worker, and every outstanding API token becomes invalid. Never change fernetKey.
Upgrading from 1.4.x or Earlier
Template version 1.5.0 removed the credentials that 1.4.x took as plain Helm values. Each removed key is rejected at render with a message naming its replacement, so an old values file fails loudly instead of being silently ignored. There is no in-place path from 1.4.x to 2.0.0 — follow Migrating from 1.x, carrying only the new keys:Migrating from 1.x
Template versions through 1.5.0 created their own GVC. Version 2.0.0 deploys into an existing GVC and does not declare one — and Helm deletes what a chart stops declaring.Pick the GVC and location for the new release
location, and enable KEDA on it first if you plan to use it. See Prerequisites.Install 2.0.0 as a new release against the same auth secret
airflow.auth.secretName at the same auth secret the 1.x release used: the fernetKey must be identical, or every restored Connection and Variable becomes unreadable.Move the metadata database
cpln workload exec is not binary-safe:Move the DAGs
gitSync.enabled: true there is nothing to do — the sidecar clones onto the new volume. Otherwise copy them off the old shared volume:Verify, then uninstall the old release
Scaling and Availability
- Workers scale; the webserver does not. Raise
airflow.celeryWorker.replicasfor a fixed pool, or enable KEDA to scale on queue length (see KEDA Autoscaling). The webserver, database and broker are each a single replica on a single volume. - Single location by construction. The shared Airflow home provisions one volume per location, so a second location would be a second, disconnected set of DAGs and logs. Run a separate release per location if you need Airflow in two places.
- The first Helm upgrade after an install can restart the bundled database and the webserver even when nothing changed. Expect a brief interruption of the UI; later upgrades with unchanged values are clean.
- A wide simultaneous fan-out can fail tasks without starting them. The Celery executor’s Redis transport in this worker image has an upstream race when many tasks are dispatched at the same instant; the affected tasks are marked failed with no hostname, no start time and an empty log. Stagger task submission rather than fanning out wide at once.
Troubleshooting
The install succeeded but nothing starts and cpln logs returns no lines
The install succeeded but nothing starts and cpln logs returns no lines
cpln logs is empty.Cause: the auth secret (or the git token secret) does not exist, so no container ever starts.Fix: read the deployment message, which names the missing secret:Every location reports This workload location is deactivated because maxScale is set to 0
Every location reports This workload location is deactivated because maxScale is set to 0
cpln workload get-deployments shows that message for every location.Cause: location names a location the GVC does not have. The platform stores the placement without validating it.Fix: compare location with spec.staticPlacement.locationLinks from cpln gvc get GVC_NAME -o yaml, then either add the location to the GVC or upgrade the release with a location the GVC already has. Seeing the message on the GVC’s other locations while the configured one runs is normal.Webserver log shows FATAL: keda.enabled is true in your values, but GVC has spec.keda.enabled = false
Webserver log shows FATAL: keda.enabled is true in your values, but GVC has spec.keda.enabled = false
cpln logs.Cause: keda.enabled: true without KEDA enabled on the GVC. The worker workload was accepted anyway and will never autoscale.Fix: enable KEDA on the GVC as in Prerequisites; the webserver passes the check on its next restart. Or set keda.enabled: false and use airflow.celeryWorker.replicas.Workers never scale and tasks stay queued with no error
Workers never scale and tasks stay queued with no error
keda.enabled: true, the worker workload is healthy at keda.minScale (zero workers at minScale: 0), queued tasks never run, nothing is logged.Cause: KEDA is not enabled on the GVC, and the install was onto an existing Airflow home, so the webserver only warned instead of refusing to start. Under internalAccess.type: workload-list the template already allows the KEDA operator onto the broker, so that is not the cause.Fix: read spec.keda.enabled with cpln gvc get GVC_NAME -o yaml and enable it as in Prerequisites.Tasks fail without ever starting after a large fan-out
Tasks fail without ever starting after a large fan-out
module 'redis' has no attribute 'client' from kombu/transport/redis.py) when many tasks are dispatched at the same instant.Fix: stagger task submission, and count completed task instances rather than relying on the UI to flag the failures.helm upgrade prints the gvc values key was REMOVED
helm upgrade prints the gvc values key was REMOVED
gvc key. The refusal is deliberate; the in-place upgrade would delete the GVC the 1.x chart created.Fix: do not remove the key and retry. Install 2.0.0 as a new release and move the data as described in Migrating from 1.x.The canonical endpoint returns 403
The canonical endpoint returns 403
403.Cause: firewallConfig.inboundAllowCIDR is [] (the default), or a recent change has not taken effect yet.Fix: use the port-forward from First Run, or add your CIDR ranges and allow a few minutes for the change to apply.Airflow cannot connect to the metadata database after changing postgres.config.password
Airflow cannot connect to the metadata database after changing postgres.config.password
postgres.config.password, the webserver and workers fail to authenticate against PostgreSQL.Cause: PostgreSQL reads POSTGRES_PASSWORD only when the data directory is initialized, so the database still has the old password while Airflow’s connection string carries the new one.Fix: revert the value to the password the volume was initialized with. The password can only be set at first install.Important Notes
- Create the auth secret before installing. Without it the deployment wedges with no log output;
cpln workload get-deploymentsis the one place the missing secret is named. - The Fernet key can never be rotated. Generate it once, keep it, and back it up with the auth secret.
- Never upgrade a 1.x release onto 2.0.0 in place — it deletes the GVC the 1.x chart created and everything in it. See Migrating from 1.x.
locationmust be a location of your GVC. Otherwise the install succeeds and nothing starts anywhere.- Enable KEDA on the GVC before setting
keda.enabled: true, or the workers sit atkeda.minScaleforever with no error. - Set
postgres.config.passwordat first install. The shipped placeholder is published and the value cannot be changed once the data directory exists. - The UI is closed by default. Reach it with
cpln port-forward; a firewall change takes a few minutes to apply. - The first Helm upgrade after an install can restart the database and webserver even with unchanged values; expect a brief interruption.
- Changing the admin password means editing the secret and forcing a webserver redeployment; see Rotating the Admin Password.
- Uninstalling deletes all three volume sets, including the DAGs and logs on the shared Airflow home. Back up the metadata database first; see Backing Up.