Securely Store and Use Secrets in Your Flows

For the complete documentation index, see llms.txt. For a full content snapshot, see llms-full.txt. Append .md to any kestra.io/docs/* URL for plain Markdown.

Configure sensitive values as base64-encoded environment variables so flows can reference them without exposing plain text in flow definitions or logs.

For a detailed overview of how secrets work across editions, see the Secrets concept page. For a UI-based secrets manager with access controls and audit logs, see the Enterprise Edition.


Using secrets in Kestra

Step 1: Create a .env file

Start by defining your secrets in a standard environment file:

POSTGRES_PASSWORD=actual_postgres_password
OPENAI_KEY=actual_openai_key
AWS_ACCESS_KEY=actual_aws_access_key
AWS_SECRET_KEY=actual_aws_secret_key

Step 2: Encode and prefix your secrets

Kestra expects all secret keys to be prefixed with SECRET_ and their values base64-encoded. The resulting .env_encoded file should look like this:

SECRET_POSTGRES_PASSWORD=base64_encoded_postgres_password
SECRET_OPENAI_KEY=base64_encoded_openai_key
SECRET_AWS_ACCESS_KEY=base64_encoded_aws_access_key
SECRET_AWS_SECRET_KEY=base64_encoded_aws_secret_key

To generate this file automatically, use the following Bash script:

while IFS='=' read -r key value; do
echo "SECRET_$key=$(echo -n "$value" | base64)";
done < .env > .env_encoded

This script:

  1. Base64-encodes all values.
  2. Adds the SECRET_ prefix to all variable names.
  3. Saves the result as .env_encoded.

You can verify the output by opening .env_encoded — it should look like the example above.

Alternatively, you can manually write the file using macros to encode secrets dynamically:

SECRET_POSTGRES_PASSWORD={{ "actual_postgres_password" | base64encode }}
SECRET_OPENAI_KEY={{ "actual_openai_key" | base64encode }}
SECRET_AWS_ACCESS_KEY={{ "actual_aws_access_key" | base64encode }}
SECRET_AWS_SECRET_KEY={{ "actual_aws_secret_key" | base64encode }}

Step 3: Point Docker to the encoded file

Update your docker-compose.yaml to use the .env_encoded file:

kestra:
image: kestra/kestra:latest
env_file:
- .env_encoded

This ensures your secrets are loaded when Kestra starts.


Step 4: Use secrets in a flow

Once your secrets are loaded, reference them in your flows using the secret() function — without including the SECRET_ prefix.

For example, this flow connects to PostgreSQL using SECRET_POSTGRES_PASSWORD and uploads query results to AWS S3 using SECRET_AWS_ACCESS_KEY and SECRET_AWS_SECRET_KEY.

id: postgres_to_s3
namespace: company.team
tasks:
- id: fetch
type: io.kestra.plugin.jdbc.postgresql.Query
url: jdbc:postgresql://127.0.0.1:56982/
username: pg_user
password: "{{ secret('POSTGRES_PASSWORD') }}"
sql: select id, first_name, last_name, city from users
fetchType: STORE
- id: write_to_s3
type: io.kestra.plugin.aws.s3.Upload
accessKeyId: "{{ secret('AWS_ACCESS_KEY') }}"
secretKeyId: "{{ secret('AWS_SECRET_KEY') }}"
region: "eu-central-1"
from: "{{ outputs.fetch.uri }}"
bucket: "kestra-bucket"
key: "data/users.csv"

How secrets are resolved

When you reference a secret using {{ secret('POSTGRES_PASSWORD') }}, Kestra finds the corresponding SECRET_POSTGRES_PASSWORD environment variable, base64-decodes its value, and injects it into the execution context. The decoded value does not appear in flow definitions or logs, but base64 encoding is not encryption — secure your host environment accordingly.

Was this page helpful?