How to Store Secrets Securely in Kestra Using an Encoded .env File

Why Secret Management Matters in Kestra

When running automation in Kestra, your flows often require:

  • SSH private keys
  • API tokens
  • Database passwords
  • Git credentials
  • Webhook secrets

Storing these directly in YAML is dangerous.

Instead, we:

✅ Store secrets in a .env file
✅ Encode them using Bash
✅ Load them into Kestra as secrets
✅ Decode only at runtime

This prevents formatting issues and reduces accidental exposure.


The Problem with Raw .env Secrets

A standard .env file might look like:

SSH_KEY=/home/user/.ssh/id_rsa
GITLAB_PASSWORD=myStrongPassword
API_TOKEN=abc123xyz

But problems occur when:

  • Secrets contain newlines (SSH keys)
  • Special characters break parsing
  • YAML interprets characters incorrectly
  • Multiline PEM blocks get corrupted

That’s why encoding is critical.


The Encoding Script

Here is the Bash script used to transform .env into an encoded version:

#!/bin/bash

while IFS='=' read -r key value; do
    if [[ "${key:-}" == SSH_* ]]; then 
        echo "SECRET_$key=$(cat $value | base64 -w 0)"; 
    else 
        echo "SECRET_$key=$(echo -n "$value" | base64 -w 0)";
    fi
done < .env > .env_encoded

What This Script Does

1️⃣ Reads Each Line of .env

while IFS='=' read -r key value

Splits:

KEY=value

into:

  • $key
  • $value

2️⃣ Detects SSH-Based Secrets

if [[ "${key:-}" == SSH_* ]];

Any key beginning with:

SSH_

is treated differently.

Why?

Because SSH values usually point to a file:

SSH_KEY=/home/user/.ssh/id_rsa

So instead of encoding the literal path…

It reads and encodes the file contents.


3️⃣ Encoding SSH Keys Properly

cat $value | base64 -w 0

This:

  • Reads the actual key file
  • Base64 encodes it
  • Removes line wrapping

This prevents PEM corruption and newline issues.


4️⃣ Encoding Normal Secrets

For non-SSH values:

echo -n "$value" | base64 -w 0

This ensures:

  • No trailing newline
  • Clean base64 output
  • Single-line environment value

5️⃣ Adds SECRET_ Prefix

Output becomes:

SECRET_SSH_KEY=LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQo...
SECRET_GITLAB_PASSWORD=bXlTdHJvbmdQYXNzd29yZA==
SECRET_API_TOKEN=YWJjMTIzeHl6

The prefix ensures consistency when referencing in Kestra:

{{ secret('SECRET_SSH_KEY') }}


Loading Into Kestra

Once .env_encoded is generated, load it into your environment.

Now in your flows:

privateKey: "{{ secret('SSH_KEY') }}"

An example of use, from last week's post, we used:

privateKey: "{{ kv('SSH_KEY') }}"

This can now be updated to:

privateKey: "{{ secret('SSH_KEY') }}"

If you have followed along, you should now be using the secret version of the key. Just remember to confirm it all before relying on it. If it's not working, did you restart your kestra instance, and is it using the env file?


Why Base64 Encoding Is the Right Approach

Environment files do not handle:

  • Multiline secrets
  • PEM formatting
  • Special characters
  • Embedded whitespace

Base64 solves all of these.

It makes every secret:

  • Single-line
  • Portable
  • Safe for Docker
  • Safe for Kubernetes
  • YAML compatible

Also, this is the way Kestra wants to import secrets in to it's database.


Security Benefits of This Method

This approach provides:

  • Separation of source .env
  • Encoded deployment file
  • Controlled secret injection
  • Reduced formatting errors
  • Clear automation boundary

However:

⚠ Base64 is NOT encryption.

It is encoding only.

For high-security environments, combine this with:

  • Encrypted secret storage - Ideally, use Kestra Enterprise's Secrets manager
  • Restricted file permissions
  • CI/CD injection
  • Vault integration

Best practice stack:

  • .env stored securely offline
  • Encoding script run during deployment
  • .env_encoded injected into container
  • Kestra secret provider enabled
  • Secrets decoded only at runtime

Hardening Recommendations

🔒 Restrict file permissions

chmod 600 .env
chmod 600 .env_encoded

🔑 Use separate SSH automation keys

🛡 Never commit .env or .env_encoded to Git

📦 Store backups securely

🧪 Rotate secrets regularly


Common Mistakes to Avoid

❌ Committing .env to Git
❌ Storing raw SSH keys in YAML
❌ Forgetting to remove trailing newlines
❌ Assuming base64 is encryption
❌ Not restricting file permissions


Final Thoughts

By combining:

  • Structured .env management
  • Bash-based encoding automation
  • Base64 compatibility
  • Secure secret injection in Kestra

You create a clean, scalable, and production-ready secret management workflow.


About the author

Tim Wilkes is a UK-based security architect with over 15 years of experience in electronics, Linux, and Unix systems administration. Since 2021, he's been designing secure systems for a telecom company while indulging his passions for programming, automation, and 3D printing. Tim shares his projects, tinkering adventures, and tech insights here - partly as a personal log, and partly in the hopes that others will find them useful.

Want to connect or follow along?

LinkedIn: [phpsytems]
Twitter / X: [@timmehwimmy]
Mastodon: [@timmehwimmy@infosec.exchange]


If you've found a post helpful, consider supporting the blog - it's a part-time passion that your support helps keep alive.

⚠️ Disclaimer

This post may contain affiliate links. If you choose to purchase through them, I may earn a small commission at no extra cost to you. I only recommend items and services I’ve personally read or used and found valuable.

As an Amazon Associate I earn from qualifying purchases.