Secrets & Environment Variables
Skipper separates non-sensitive configuration (environment variables) from sensitive credentials (secrets). They are stored and displayed differently.
Environment variables
For non-sensitive config like log levels, feature flags, and base URLs.
# Set one or more variables
kip app env set api LOG_LEVEL=debug API_URL=https://api.example.com
# Load from a file
kip app env set api --from-file .env.production
# List all variables (values visible)
kip app env list api
# Delete a variable
kip app env delete api LOG_LEVELValues are displayed in plain text in both the CLI and the console UI.
Secrets
For sensitive values like database passwords, API keys, and tokens.
# Interactive prompt (value hidden, not in shell history)
kip app secret set api DATABASE_URL
# Inline (warns about shell history)
kip app secret set api DATABASE_URL=postgres://user:pass@host/db
# Load from a file
kip app secret set api --from-file .secrets
# List keys only (values always masked)
kip app secret list api
# Reveal a single value
kip app secret reveal api DATABASE_URL
# Delete a secret
kip app secret delete api DATABASE_URL
# Rollback to the previous value
kip app secret rollback api DATABASE_URLAutomatic previous version
Every time you update a secret, Skipper automatically preserves the previous value (inspired by AWS Secrets Manager). The list command shows which keys have a previous version available:
$ kip app secret list api
KEY PREVIOUS VALUE
DATABASE_URL yes ••••••••
STRIPE_KEY ••••••••If you accidentally set a wrong value, rollback instantly:
kip app secret rollback api DATABASE_URLJSON secrets
If a secret value is valid JSON, kip app secret reveal displays it as formatted JSON. This is useful for structured config like database credentials:
$ kip app secret reveal api DB_CONFIG
DB_CONFIG=
{
"host": "db.example.com",
"port": 5432,
"user": "api",
"password": "secret",
"database": "production"
}Why the separation?
| Environment variables | Secrets | |
|---|---|---|
list output | Keys and values | Keys only (masked) |
set behaviour | Inline KEY=VALUE | Interactive hidden prompt |
| Console UI | Plain text | Masked with reveal button |
| Shell history | Visible | Not recorded (interactive mode) |
How it works internally
Both env vars and secrets are stored as Kubernetes Secrets (encrypted at rest by k3s). They are kept in separate Secret objects:
<app>-env: environment variables<app>-secrets: secrets
Both are injected into the pod via EnvFrom. From the application's perspective, they are all standard environment variables. There is no difference at runtime.
Updating either env vars or secrets triggers an automatic rolling restart so the application picks up the new values.
