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.envfile
✅ 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=abc123xyzBut 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_encodedWhat 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=YWJjMTIzeHl6The 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
Recommended Production Setup
Best practice stack:
.envstored securely offline- Encoding script run during deployment
.env_encodedinjected 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
.envmanagement - 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.