-
Notifications
You must be signed in to change notification settings - Fork 1
Secrets Management
Manage environment secrets across local and VPS environments. strut provides tools to generate, source from secret managers, sync, encrypt at rest, diff, rotate, and validate .env files.
Since: v0.25.0 (init-secrets), v0.26.0 (secrets push/pull/diff/validate), v0.27.0 (hydrate, lock/unlock, rotate, template, export, status)
| Command | Purpose |
|---|---|
init-secrets |
Generate a populated .env from a .env.template (random values) |
secrets hydrate |
Build .env from template, resolving secret references (vault, exec, file) |
secrets push |
Upload local .env to VPS (secure SCP, mode 600) |
secrets pull |
Download .env from VPS to local |
secrets diff |
Compare local vs remote keys (values redacted) |
secrets validate |
Check required_vars, detect placeholders, weak secrets, unresolved refs |
secrets status |
Show the secrets pipeline state for a stack |
secrets rotate |
Re-hydrate/re-generate, validate, push, and optionally restart |
secrets template |
Reverse-engineer a .env.template from an existing .env
|
secrets export |
Export .env to docker-secret, k8s-secret, or env-json format |
secrets lock |
Encrypt .env at rest using age or GPG (safe to commit) |
secrets unlock |
Decrypt an encrypted .env back to plaintext |
Start from a stack's .env.template and auto-generate cryptographic secrets for password, token, and key variables:
strut my-app init-secrets --env prod
strut my-app init-secrets --env prod --dry-run # preview without writing
strut my-app init-secrets --env staging --force # overwrite existing values- Reads
stacks/<stack>/.env.template - Detects placeholder values (
changeme,xxxx, empty, etc.) - Auto-generates secrets based on key name patterns:
-
*SECRET*,*JWT*— 32-byte hex -
*PASSWORD*— 16-byte hex -
*SALT*— 16-byte hex -
*TOKEN*,*KEY*(generic) — 24-byte hex -
*ENCRYPTION*— 32-byte hex
-
- Respects generation hints in comments:
# Generate with: openssl rand -hex 64 MY_SECRET=changeme - Leaves API keys, domains, and URLs as-is (user must fill manually)
-
Never overwrites existing non-placeholder values (unless
--force) - Safe to re-run — only fills in missing/placeholder values
- Variables needing manual input (API keys, external URLs) are left untouched
These patterns are recognized as placeholders:
- Empty values
-
changeme,CHANGEME,Change-Me -
your-*,YOUR_* -
xxxx,XXXX -
replace-*,REPLACE_* -
todo,TODO,fixme,FIXME -
example,placeholder
secrets hydrate resolves secret references in your .env.template from external sources:
strut my-app secrets hydrate --env prod
strut my-app secrets hydrate --env prod --dry-runIn your .env.template, use these prefixes:
| Prefix | Source | Example |
|---|---|---|
vault://<item> |
Vaultwarden/Bitwarden (via bw CLI) |
DB_PASS=vault://my-db-creds |
exec://<command> |
Stdout of a shell command | TOKEN=exec://op read "My Token" |
file://<path> |
Contents of a file | CERT=file:///run/secrets/cert |
| (plain value) | Literal — copied as-is | PORT=3000 |
Security note: exec:// runs commands with your local privileges. Only hydrate templates you trust.
The hydrate system is extensible. Providers are auto-detected based on the reference prefix. You can add custom providers following the extensibility guide in the project docs.
Upload local .env to the VPS:
strut my-app secrets push --env prod
strut my-app secrets push --env prod --force # overwrite existing remote
strut my-app secrets push --env prod --dry-runBefore pushing, strut automatically validates required_vars (if the file exists). If any required variables are missing, the push aborts.
Download the remote .env to local:
strut my-app secrets pull --env prod
strut my-app secrets pull --env prod --force # overwrite local fileCompare local and remote env files without exposing values:
strut my-app secrets diff --env prodOutput shows:
- Changed — keys present in both but with different values
- Only in local — keys you have locally but not on VPS
- Only on remote — keys on VPS you don't have locally
Values are never displayed — only key names.
Check that all variables in required_vars are present, non-empty, and well-formed:
strut my-app secrets validate --env prodSince v0.27.0, validate also detects:
- Placeholder values that were never filled in
- Weak secrets (short, low-entropy)
- Unresolved secret references (e.g. leftover
vault://orexec://strings)
Show the full secrets pipeline state for a stack:
strut my-app secrets status --env prodDisplays:
- Local env file info (path, size, variable count)
- Template references and whether they're hydrated
- Remote sync state (in-sync, stale, or missing)
-
required_varscoverage - Deploy key presence
Rotate secrets in one step — re-hydrate or re-generate, validate, push to VPS, and optionally restart containers:
strut my-app secrets rotate --env prod
strut my-app secrets rotate --env prod --restart
strut my-app secrets rotate --env prod --dry-runReverse-engineer a .env.template from an existing .env file. Useful when you have a working env and want to create a template for the team:
strut my-app secrets template --env prod
strut my-app secrets template --env prod --output stacks/my-app/.env.templateExport your .env to other formats for use in different deployment systems:
strut my-app secrets export --env prod --format docker-secret
strut my-app secrets export --env prod --format k8s-secret
strut my-app secrets export --env prod --format env-jsonEncrypt .env files so they're safe to commit to version control:
strut my-app secrets lock --env prod # .prod.env -> .prod.env.age
strut my-app secrets unlock --env prod # .prod.env.age -> .prod.env-
Backends:
age(preferred),gpg(fallback) — auto-detected -
Recipients: Create
.strut-recipientswith age or SSH public keys (one per line). If absent, encrypts to self using~/.ssh/id_ed25519.pub -
Identity:
--identity <file>orSTRUT_AGE_IDENTITYenv var. Defaults to~/.age/key.txt,~/.ssh/id_ed25519, or~/.ssh/id_rsa
# Decrypt to edit
strut my-app secrets unlock --env prod
nano .prod.env
# Re-encrypt before committing
strut my-app secrets lock --env prod
git add .prod.env.age
git commit -m "update prod secrets"# 1. Create .env from template with generated secrets
strut my-app init-secrets --env prod
# OR hydrate from a secret manager
strut my-app secrets hydrate --env prod
# 2. Fill in remaining values manually (API keys, domains)
nano .prod.env
# 3. Validate everything is set
strut my-app secrets validate --env prod
# 4. Push to VPS
strut my-app secrets push --env prod
# 5. Encrypt for safe storage in git
strut my-app secrets lock --env prod
# 6. Deploy
strut my-app release --env prodstrut looks for env files in this order:
-
stacks/<stack>/.<env>.env(stack-level) -
<project_root>/.<env>.env(project-level)
Remote path: <deploy_dir>/.<env>.env
| Tool | Purpose |
|---|---|
init-secrets |
Generate initial .env from template (random values) |
secrets hydrate |
Resolve secret refs from vault/exec/file providers |
secrets push/pull |
Sync .env between local and VPS |
secrets diff |
Compare local vs remote |
secrets lock/unlock |
Encrypt/decrypt .env at rest |
secrets rotate |
Full rotation cycle (re-gen + validate + push + restart) |
secrets template |
Generate .env.template from existing .env
|
secrets export |
Convert .env to docker-secret, k8s-secret, env-json |
ssh:keygen |
Generate deploy keypairs for CI/CD |
ci:init |
Bootstrap CI/CD secrets from topology |
keys env:* |
Rotate/validate individual env vars |
drift detect |
Detect config file drift (broader than just secrets) |
strut · v0.28.0 · Report an Issue
Getting Started
Core Concepts
Operations
- Deployment
- Ship and Rebuild
- GitHub Actions
- Remote Host Setup
- Provisioning
- Blue-Green Deploy
- Deploy Rollback
- Database Backups
- Secrets Management
- Stack Groups
- Lifecycle Hooks
- Notifications
- Key Rotation
- Drift Detection
- Domain and SSL
- Certificate Management
- Gateway Management
- Monitoring
- Volume Management
Advanced
- Security Posture
- VPS Audit and Migration
- Stack Validation
- Data Anonymization
- Debugging
- Local Development
Extending
Contributing