Skip to main content
By default, the Astro Private Cloud (APC) platform Helm chart passes secrets, such as database connection strings and identity provider client secrets, to platform components as environment variables that reference Kubernetes Secrets with valueFrom.secretKeyRef. If your security policies don’t allow secrets in environment variables, turn on secrets from files, and the chart mounts each secret as a file instead.
Astro Private Cloud 2.2This feature was introduced in Astro Private Cloud 2.2. To access this feature, upgrade your Astro Private Cloud installation to 2.2 or later.
PreviewSecrets from files is in Preview. Some platform components don’t support it yet and keep their secrets in environment variables. See Secrets that stay in environment variables.

How it works

The feature covers two kinds of components:
  • Components that run a secrets loader, code in the APC image that reads the secret files when the container starts and passes each value to the application. These are the APC API, APC Worker, Navigator, DP-Link, the data plane internal API, Pilot, the Prometheus service discovery reloader, and the database bootstrapper.
  • Third-party components that read secret files with their own software: the Vector logging sidecar, the external Elasticsearch proxy, the Prometheus federation authentication proxy, PostgreSQL, and the PostgreSQL exporter.
For a component with a secrets loader, the chart:
  • Mounts each of the component’s secrets as a file, most of them in /etc/astronomer/secrets/, with mode 0440.
  • Sets the Pod’s fsGroup to global.secretsFromFiles.fsGroup, which defaults to 1000, so that the component’s non-root process can read the files. On OpenShift, the chart doesn’t set an fsGroup, because OpenShift assigns one to each namespace.
  • Removes the environment variables that referenced those secrets.
Secrets loaders are strict. A component that the feature applies to gets its secrets only from files:
  • helm upgrade fails if your values would also put one of the component’s secrets in its environment, for example in the component’s env list. The upgrade fails before it changes anything in your cluster.
  • When the container starts, the loader exits with an error if one of the component’s secrets is also in its environment, or if a file that it needs is missing or unreadable. Most loaders also exit on an empty file. See Fix startup errors for the exceptions.
The third-party components have their own checks:
  • The Vector logging sidecar, the external Elasticsearch proxy, and the federation authentication proxy don’t start if a secret file is missing or empty.
  • PostgreSQL ignores an empty password file after its data folder exists, and its entrypoint passes the password to the database server in the server’s environment.
  • The PostgreSQL exporter’s init container refuses an empty connection string. If you use the split datasource, the exporter doesn’t check for an empty password file.
Secrets from files keeps the values out of each container’s configured environment, so they don’t appear in the output of kubectl exec <pod-name> -- env. After a loader reads a file, it still passes the value to its application in the process’s own environment, because the application reads its configuration from there. Processes that the application starts inherit those values.
Components read their secret files when they start. After you change a Kubernetes Secret, restart the Pods that use it, as you would for a secret in an environment variable.

Components and settings

Each component has its own secretsFromFiles.enabled setting. If you don’t set it, the component follows global.secretsFromFiles.enabled. The database bootstrapper is the exception: its setting, global.dbBootstrapper.secretsFromFiles.enabled, defaults to false and doesn’t follow the global setting.

Secrets that stay in environment variables

The following secrets have no secretsFromFiles setting, and stay in environment variables whatever you set:
  • Grafana’s database connection
  • The registry’s notification header
  • The secrets of Laminar
  • Entries that you add to astronomer.mcpServer.secret. The Astro Private Cloud MCP server has no secrets of its own, and the chart doesn’t refuse these entries while the feature is on.
The database bootstrapper init containers in the Grafana and Laminar Pods follow global.dbBootstrapper.secretsFromFiles.enabled. Environment variables that you add to a component’s own values also stay environment variables when the component has no secretsFromFiles setting. For example, registry storage credentials in astronomer.registry.extraEnvVars and Vector credentials in vector.extraEnv stay environment variables. Secrets from files doesn’t change Airflow Deployments. To keep Airflow connections and variables out of environment variables, use a secrets backend.

Prerequisites

  • Astro Private Cloud 2.2 or later.
  • Permission to run helm upgrade on your platform release. In a split installation, the control plane and each data plane are separate Helm releases. See Apply a config change.
  • The platform images that your APC chart version pins. Images from versions earlier than 2.2 can’t read secrets from files. If you override an image for a component that the feature covers, use an image from the same APC release as the chart.

Turn on secrets from files

1

Move secrets out of environment variable lists

While the feature applies to a component, helm upgrade fails if one of the following lists in your values.yaml file contains one of the component’s secrets:
  • astronomer.houston.env, astronomer.navigator.env, and astronomer.dpLink.env
  • astronomer.commander.env and astronomer.pilot.env
  • prometheus.filesdReloader.extraEnv
  • global.customLogging.extraEnv
  • postgresql.extraEnv
The chart also refuses an entry that sets a variable it sets for the component’s secrets loader, such as HOUSTON_SECRETS_FROM_FILES or a <NAME>_FILE path. In astronomer.dpLink.env, astronomer.pilot.env, and the extraEnv lists, it refuses any entry that takes its value from a Kubernetes Secret.To give the APC API, Navigator, or DP-Link a secret of your own, add it to the component’s secret list instead of its env list. The chart delivers each entry as a file named after its envName, and the secrets loader requires that file. For example:
If you store your external Elasticsearch credentials inline in global.customLogging.secret, store them under the elastic key of a Kubernetes Secret instead, and name the Secret in global.customLogging.secretName. See Save your Elastic Cloud deployment credentials.For entries in the other lists, remove the entry. The chart delivers those components’ secrets itself.
2

(Optional) Check the PostgreSQL exporter's connection string

Skip this step unless global.prometheusPostgresExporter.enabled is true in your values.The exporter can’t read a whole connection string from a file. With the feature on, an init container named split-data-source-name reads the connection string from prometheus-postgres-exporter.config.datasource.connectionSecret and writes the database user, password, and address to separate files that the exporter reads. Only the init container mounts the Kubernetes Secret that holds the connection string.The init container fails, and the exporter doesn’t start, if the connection string:
  • Is missing or empty.
  • Isn’t a postgres:// or postgresql:// URL. For example, a host=... user=... connection string.
  • Contains a comma. Percent-encode it as %2C.
  • Has an invalid percent-escape or a control character, or a user or password that starts or ends with whitespace.
  • Has no user or no host.
The init container’s error describes the problem and never includes the connection string. To read it, run:
If you already use the split datasource, with connectionSecret set to null and a passwordSecret, it keeps working, and the exporter reads its password from a file.To keep the exporter on environment variables instead, turn the feature off for it:
3

Turn on the feature

Add the following values to your values.yaml file:
global.secretsFromFiles.enabled turns on the feature for every component that doesn’t set its own setting to false. The database bootstrapper needs its own setting, as Components and settings explains.In a split installation, add these values to the values file of each release, the control plane and each data plane, where you want the feature.Change global.secretsFromFiles.fsGroup only if a component’s image runs with a group other than 1000.
4

(Optional) Keep a component on environment variables

To keep one component on environment variables while the feature is on for the rest of the platform, set the component’s own setting to false. For example:
To turn on the feature for some components only, leave global.secretsFromFiles.enabled set to false, and set those components’ settings to true.Each secretsFromFiles.enabled setting must be true, false, or unset. A quoted value, such as "false", fails the render.
5

Apply the change

Apply your updated values.yaml file with helm upgrade. See Apply a config change.If the chart refuses your values, helm upgrade fails before it changes anything in your cluster, and the error names the setting to change. See Fix Helm upgrade errors.The Pods of each component that the change affects restart.
6

Verify the change

  1. List the environment variables that still take their values from Kubernetes Secrets:
    The command lists the Pod, container, and variable name for each one, and never a value. With the feature on for every covered component, the output lists only the secrets that stay in environment variables, such as GF_DATABASE_URL in the Grafana Pod and REGISTRY_NOTIFICATIONS_ENDPOINTS_0_HEADERS in the registry Pod. In a split installation, run the command on each cluster.
  2. Confirm that a component loaded its secrets. For example, for the APC API:
    The output looks similar to the following:
    The number depends on your configuration. The secrets loader logs this line when the container starts, so the line can be missing from the log of a container that has run for a long time.

Turn off secrets from files

Set global.secretsFromFiles.enabled to false. If you turned on global.dbBootstrapper.secretsFromFiles.enabled, set it to false too. Then apply the change. The components go back to environment variables that reference the same Kubernetes Secrets. A component whose own setting is true keeps reading its secrets from files.

Troubleshoot

Fix Helm upgrade errors

When the chart refuses your values, the error starts with a token for the problem, then names the setting to change:

Fix startup errors

When a secrets loader finds a problem, the container exits, and the Pod restarts with the CrashLoopBackOff status. The container’s log lists each problem on its own line, with the variable name and a token, and ends with a line that describes how to fix the problems. For example:
To read the log of a container that restarted, run:
The exact wording differs between components, but each loader uses the same tokens: If the cause is a permission error, check that the Pod’s securityContext.fsGroup is set, and that no admission policy in your cluster removes or replaces it.