Upgrade to Astro Private Cloud 2.0 from 0.37

This guide provides instructions to upgrade directly from Astro Private Cloud (APC) 0.37.6 to 2.0. This is a major upgrade that includes platform-level infrastructure changes (NATS JetStream migration, component replacements) as well as a comprehensive values.yaml schema migration.

Upgrade requires platform downtime

This upgrade deletes and recreates several platform components (NATS, STAN, the APC API). The platform will be unavailable for creating or updating Deployments during the upgrade. Plan a maintenance window and notify your users before starting.

If you are already on APC 1.x, use the simpler Upgrade to 2.0 from 1.x guide instead. That guide doesn’t require the platform-level migration steps described here.

Prerequisites

  • Upgrade to APC 0.37.6 if you’re on an earlier version.

  • Back up your platform database. At minimum, create a snapshot or backup of your PostgreSQL database (RDS snapshot, Azure backup, Cloud SQL backup, or pg_dump).

  • Verify that all platform and Airflow Deployment Pods are healthy:

    $kubectl get pods -n astronomer
    $kubectl get pods -n astronomer-<deployment-release-name>
  • If you use Astronomer Units (AU) for resource configuration, convert to CPU/memory settings while still on 0.37.x — the steps live on the APC 1.0 breaking changes page because that migration was required for the 1.0 release. The AU-to-CPU/memory migration script was removed in 1.0 and isn’t available in 2.0.

  • Check for duplicate workspace labels in your database. The upgrade includes a migration that adds a unique constraint to workspace labels, and it will fail if duplicates exist:

    1SET search_path TO "houston$default";
    2SELECT label, COUNT(*) FROM "Workspace" GROUP BY label HAVING COUNT(*) > 1;

    If this query returns any results, rename or remove the duplicate workspaces before proceeding.

  • Remove any deprecated Helm values from your values.yaml that are no longer recognized in 2.0. See Breaking changes and removals for the full list.

  • Python 3.10 or later installed on your local computer (required for the values migration script).

  • Install the ruamel.yaml Python package:

    $pip install ruamel.yaml
  • (Optional) Capture logs from your NATS and STAN instances before the upgrade:

    $kubectl logs -n astronomer -l component=nats --tail=1000 > nats-pre-upgrade.log
    $kubectl logs -n astronomer -l component=stan --tail=1000 > stan-pre-upgrade.log

For help with upgrade issues, see Debug upgrade.

Manual DNS and load balancer update required

When upgrading from APC 0.x.x to 2.0, APC creates a new control plane NGINX ingress Service (astronomer-cp-nginx) and a new LoadBalancer. The previous ingress and LoadBalancer are replaced.

You must:

  • Update all DNS records to the new load balancer IP address.
  • Update firewall, allowlist, and security rules to point to the new public endpoint.
  • Re-issue or update any TLS/SSL certificates that reference the previous LoadBalancer hostname, if applicable.

This change occurs because the control plane ingress Service name changes from astronomer-nginx (0.x) to astronomer-cp-nginx (2.0), which causes Kubernetes to provision a new external LoadBalancer with a new public IP/hostname. Prepare these updates before performing the upgrade.

Example (your output will vary by cloud/provider):

$kubectl -n astronomer get svc astronomer-cp-nginx
$NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
$astronomer-cp-nginx LoadBalancer 10.100.223.78 7666ac61ef6-1683718677.us-east-2.elb.amazonaws.com 80:32255/TCP,443:32647/TCP 25d

If you are on OpenShift and manage your own Routes or use a third-party ingress controller instead of the platform’s built-in NGINX ingress, this LoadBalancer change doesn’t apply to you. You can skip the DNS update step later in this guide.

Step 1: Get the migration script

The migration scripts are located in the bin/ folder of the Astronomer Helm chart repository. Clone the repository and check out the release-2.0 branch:

$git clone https://github.com/astronomer/astronomer.git
$cd astronomer
$git checkout release-2.0

The script you need for this upgrade path is bin/migrate-helm-chart-values-037x-to-2x.py.

Step 2: Migrate your values.yaml

The 0.37.x-to-2.x migration is a comprehensive transformation that includes feature flag restructuring, removal of obsolete components, key renames, value updates, new platform keys, and APC API configuration flag restructuring under astronomer.houston.config.deployments. The migration script automates the entire transformation.

Back up your override file

$cp my-values.yaml my-values.yaml.backup

Preview changes (dry run)

$./bin/migrate-helm-chart-values-037x-to-2x.py --dry-run my-values.yaml

Example output:

Found 28 migration(s) to apply:
global.rbacEnabled -> global.rbac.enabled
global.sccEnabled -> global.scc.enabled
...
global.singleNamespace -> (deleted)
global.veleroEnabled -> (deleted)
...
fluentd -> vector
global.pgbouncer.krb5ConfSecretName -> global.pgbouncer.secretName
global.pgbouncer.servicePort: "5432" -> "6543"
...
(new) -> global.plane
...

If you see No migrations needed, your file is already compatible with 2.0.

Run the migration

Review the migrated file

Open the migrated file and verify the changes look correct. Pay special attention to:

  • New default values — review the New keys added with defaults table and override any defaults that don’t match your environment.
  • Fluentd-to-Vector rename — if you had custom Fluentd configuration, only the resource values carry over. See Breaking changes for migration guidance.
  • PgBouncer port — confirm the new port (6543) works for your setup, or override it back to 5432.
  • Unified mode — Ensure global.plane.mode is set to "unified". This is equivalent to how 0.37.x operates and is required for the initial upgrade.

Step 3: Validate the Helm upgrade (dry run)

Run a Helm upgrade dry run to verify that the upgrade will succeed:

$helm upgrade -f migrated-values.yaml -n astronomer astronomer astronomer/astronomer --version 2.0.x --dry-run

Replace 2.0.x with the specific patch version you are upgrading to.

Don’t proceed with the remaining steps until the dry run completes successfully. If it fails, resolve the reported errors first.

Step 4: Delete existing STAN and NATS StatefulSets

Before upgrading, you must migrate from STAN to JetStream. Run the following commands to remove legacy STAN components:

$kubectl delete sts <release-name>-stan
$kubectl delete sts <release-name>-nats --cascade=orphan

Step 5: Patch astronomer-bootstrap secret with database name

Ensure the astronomer-bootstrap secret includes a database name suffix in connection (for example, /postgres). This prevents Postgres connection errors during or after the upgrade.

Check your current connection string

$kubectl get secret -n astronomer astronomer-bootstrap \
> --template='{{.data.connection | base64decode }}'

A connection string with a database name looks like:

postgresql://user:pass@host:5432/postgres
^^^^^^^^
database name present

A connection string without a database name looks like:

postgresql://user:pass@host:5432
^
no database name

If your connection string already includes a database name, skip to Step 6.

Determine the correct database name

The default database name depends on your cloud provider:

  • AWS RDS: postgres (standard default; main may appear only in specific legacy or custom configurations)
  • Azure Database for PostgreSQL: postgres
  • GCP Cloud SQL: postgres

If you’re unsure, connect to your PostgreSQL instance and run \l to list available databases.

Patch the secret

$NAMESPACE=astronomer
$DB_NAME=postgres
$
$CURRENT=$(kubectl -n "$NAMESPACE" get secret astronomer-bootstrap -o jsonpath='{.data.connection}' | base64 -d)
$NEW="${CURRENT%/}/$DB_NAME"
$kubectl -n "$NAMESPACE" patch secret astronomer-bootstrap --type=merge -p "{\"data\":{\"connection\":\"$(printf '%s' "$NEW" | base64 -w0)\"}}"

Step 6: Delete the APC Deployment

Delete the APC Deployment to avoid a known Helm patch conflict related to environment variable ordering changes between versions.

$kubectl delete deployment/<release-name>-houston -n astronomer --cascade=orphan

The --cascade=orphan flag keeps the APC Pods running during the delete operation. The Helm upgrade in the next step recreates the Deployment with the correct configuration.

If you skip this step and encounter a Helm patch error during the upgrade, see Debug upgrade for the workaround.

Step 7: Upgrade to APC 2.0

Ensure that your migrated values.yaml includes the unified mode configuration:

1global:
2 plane:
3 mode: "unified"

After upgrading to 2.0 in unified mode, you can optionally add separate data planes to run Airflow Deployments in other clusters.

Complete all pre-upgrade steps (database backup, STAN/NATS deletion, bootstrap secret patch, APC Deployment deletion, values migration) before running the upgrade command.

$helm upgrade -f migrated-values.yaml -n astronomer astronomer astronomer/astronomer --version 2.0.x

Replace 2.0.x with the specific patch version you are upgrading to.

Airgapped environments

For airgapped environments that can’t access the internet, download the Helm chart .tgz file directly from the Astronomer Helm repository:

https://helm.astronomer.io/astronomer-<version>.tgz

Replace <version> with the specific version you are upgrading to. Upload this file to your internal artifact repository, then reference it in your helm upgrade command.

Step 8: Restart NATS and APC API components

After the platform upgrade completes and all Pods are running, restart NATS and APC API components to ensure the new JetStream components and APC API services are connected and synchronized:

$kubectl rollout restart sts/<release-name>-nats
$kubectl rollout restart deploy/<release-name>-houston
$kubectl rollout restart deploy/<release-name>-houston-worker

After the rollout completes, verify the Pods have been recreated by checking their age:

$kubectl get pods -n astronomer -l "component in (nats,houston,houston-worker)" -o wide

All Pods should show a recent AGE (a few minutes). If any Pods show an older age, delete them manually to force recreation:

$kubectl delete pod -n astronomer -l component=nats
$kubectl delete pod -n astronomer -l component=houston
$kubectl delete pod -n astronomer -l component=houston-worker

Step 9: Update DNS records

APC 2.0 creates a new control plane NGINX ingress Service (astronomer-cp-nginx) with a new LoadBalancer. Get the new load balancer address:

$kubectl -n astronomer get svc astronomer-cp-nginx

Update your DNS records to point to the new load balancer IP or hostname. This includes all subdomains for your base domain (for example, app.<baseDomain>, houston.<baseDomain>, registry.<baseDomain>).

If you are on OpenShift and manage your own Routes or use a third-party ingress controller, you can skip this step.

You must complete this step before you can access the Astro UI or API.

Step 10: Upgrade all Airflow Deployments

After you have validated that all platform Pods are healthy, upgrade your Airflow Deployments to ensure compatibility with 2.0.

Existing Airflow Deployments typically continue to function after the platform upgrade without immediate action. However, Astronomer recommends upgrading Deployments to ensure full compatibility with the new platform version.

To upgrade Deployments, use one of the following approaches:

  • Astro UI: Upgrade each Deployment manually from the Astro UI.
  • APC API: Use the APC API upsertDeployment mutation for programmatic or bulk upgrades.
  • Astro CLI: Use the Astro CLI’s astro deploy command.

Step 11: Validate the upgrade

1

Confirm that NATS Pods are running with JetStream enabled

Check if the JetStream job is created:

$kubectl -n astronomer get jobs | grep jetstream

Check if NATS Pods are running:

$kubectl -n astronomer get pods -l component=nats
2

Verify APC Worker Pods are healthy and processing events

Check if APC Worker Pods are running:

$kubectl -n astronomer get pods -l component=houston-worker
3

Verify there are no remaining references to STAN

This command should return no results:

$kubectl -n astronomer get statefulsets | grep stan
4

Verify Vector is running (replaces Fluentd)

$kubectl -n astronomer get pods -l component=vector
5

Verify you can access the Astro UI

Navigate to app.<baseDomain> in your browser and confirm the UI loads.

Values migration reference

bin/migrate-helm-chart-values-037x-to-2x.py on the Astronomer Helm chart release-2.0 branch includes every shared rule from Values migration reference in bin/helm_chart_values_migration_shared.py, and the 0.37-only steps in the tables below. The script runs, in order: (1) apply_global_feature_flag_rules_to_all; (2) the 0.37 MIGRATIONS list in migrate-helm-chart-values-037x-to-2x.py (every DeleteKey, RenameKey, SetValue, and AddKeyIfMissing in that file); (3) the same MIGRATIONS against nested global maps; (4) apply_houston_config_flag_migrations; (5) apply_houston_deployment_migrations; (6) apply_nginx_csp_policy_migrations. The 1.x script uses the same APC API and nginx steps but a different order and adds strictSchemaCheck at the end; the 0.37 script adds strictSchemaCheck during step (2). Use --dry-run to see the exact list for your file.

The following tables are the complete 0.37-only portion (the full MIGRATIONS list from that script).

Deleted keys (0.37 script)

KeyNotes
global.singleNamespaceObsolete feature
global.veleroEnabledObsolete feature
global.enableHoustonInternalAuthorizationReplaced by install-mode behavior
global.nodeExporterSccEnabledNo longer used
global.stanSTAN removed (JetStream)
tags.stanSTAN tag removed
stan (top-level)STAN resources removed
kibana (top-level)See Breaking changes
prometheus-blackbox-exporter (top-level)See Breaking changes

Renamed keys (0.37 script)

Old pathNew name / pathNotes
fluentd (top-level key)vector (top-level)Subtree kept; custom Fluentd config isn’t auto-translated to Vector.
global.pgbouncer.krb5ConfSecretNameglobal.pgbouncer.secretNameSame Secret object

Value updates (only when the current value matches the “old” column)

PathOldNewNotes
global.pgbouncer.servicePort5432 (string)6543 (string)Set back to 5432 in overrides if required
global.nats.jetStream.enabledfalsetrueSet to false in overrides to keep JetStream off

New keys added with defaults

The migration script adds these keys with default values if they aren’t already present. Review each key and override the default if it doesn’t match your environment.

Key PathDefault ValueDescriptionAction Needed?
global.authHeaderSecretName~ (null)Name of Kubernetes secret for cross-plane authentication.Set this if running in multi-plane mode. Not needed for unified mode.
global.plane.mode"unified"Platform operating mode: control, data, or unified.Change to control or data if running a multi-plane deployment.
global.plane.domainPrefix""Cluster identifier prefix for multi-plane DNS.Set to your cluster ID if running in multi-plane mode.
global.podLabels{}Labels applied to every pod.Add labels if needed for monitoring, cost allocation, or policy enforcement.
global.logging.provider~ (null)Logging provider identifier.Set if using a specific logging backend.
nats.init.resources.requests.cpu"75m"NATS init container CPU request.Override if your cluster needs different resource settings.
nats.init.resources.requests.memory"30Mi"NATS init container memory request.Override if your cluster needs different resource settings.
nats.init.resources.limits.cpu"250m"NATS init container CPU limit.Override if your cluster needs different resource settings.
nats.init.resources.limits.memory"100Mi"NATS init container memory limit.Override if your cluster needs different resource settings.

Roll back the upgrade

If you need to revert the values migration:

  1. Restore your backup:

    $cp my-values.yaml.backup my-values.yaml
  2. To downgrade the chart after a Helm upgrade:

    $helm rollback astronomer <previous-revision> --namespace astronomer