Turning a .env File into docker-compose, Kubernetes, and GitHub Actions Secrets
A .env file is the lowest-common-denominator way to hold configuration — flat KEY=value lines, no structure. The moment you deploy anywhere beyond docker run --env-file .env, though, every platform wants that same data in its own shape. Here’s the same source file converted three ways, and what’s actually different about each target.
# .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
API_KEY="sk_live_abc123"
DEBUG=false
PORT=3000
docker-compose environment
services:
app:
environment:
- DATABASE_URL=postgres://user:pass@localhost:5432/mydb
- API_KEY=sk_live_abc123
- DEBUG=false
- PORT=3000
The simplest of the three conversions — docker-compose’s environment: list accepts the same KEY=value shape a .env file already uses. The honest question here is whether you need this at all: if you’re fine keeping a separate .env file next to your docker-compose.yml, env_file: .env does the same job with one line, and keeps secrets out of the compose file itself. Inlining values like this is for when you need one self-contained file — a shareable template, or a CI step that doesn’t have the original .env available.
Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
DATABASE_URL: cG9zdGdyZXM6Ly91c2VyOnBhc3NAbG9jYWxob3N0OjU0MzIvbXlkYg==
API_KEY: c2tfbGl2ZV9hYmMxMjM=
DEBUG: ZmFsc2U=
PORT: MzAwMA==
The one detail that trips people up: base64 here is an encoding requirement of the Secret resource type, not encryption. Kubernetes’ API requires data: values to be base64 — it’s how the field is typed, nothing more. Anyone with read access to the Secret (or the raw YAML) can decode it in one line (echo '...' | base64 -d). Treat a generated manifest exactly like the original .env file for handling purposes: don’t commit it to git, and use a real secrets tool (Sealed Secrets, SOPS, your cloud provider’s secret manager) for anything that does get committed or needs actual encryption at rest.
GitHub Actions secrets
gh secret set DATABASE_URL --body 'postgres://user:pass@localhost:5432/mydb'
gh secret set API_KEY --body 'sk_live_abc123'
gh secret set DEBUG --body 'false'
gh secret set PORT --body '3000'
This one is fundamentally different from the other two: GitHub Actions secrets are encrypted with a per-repository public key on GitHub’s servers — there’s no YAML or config file format to “convert” into, because a static value can’t be pre-encrypted for a key it hasn’t fetched yet. The real mechanism is the GitHub CLI’s gh secret set, which handles the encryption handshake for you. What this converter actually generates is the exact sequence of gh secret set commands for every key in your .env file, so bulk-importing a dozen secrets is a paste-and-run instead of a dozen manual gh secret set calls. It also prints the matching ${{ secrets.KEY }} block to drop into a workflow’s env: section.
One script, one caveat: it contains your real secret values as plain-text shell arguments. Run it once, then delete it — don’t let it linger in a downloads folder or, worse, a repo.
The pattern across all three
None of these are copy-paste-safe artifacts to keep around after use — a docker-compose file with inlined secrets, a Kubernetes Secret manifest, and a gh secret set script all carry the same real values your original .env file did, just reshaped for wherever they’re going next. Convert what you need, use it, and keep the same handling discipline you’d apply to the .env file itself.
All three tools run entirely in your browser — nothing you paste is uploaded anywhere.