Skip to content

Sealed secrets

Bitnami Sealed Secrets lets you commit encrypted secrets to git. kdef gives them a first-class block — no YAML gymnastics, no wrapping kubeseal in shell scripts.

  • kubeseal installed locally
  • Sealed-secrets controller running in the cluster
Terminal window
# macOS
brew install kubeseal
# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/kubeseal-linux-amd64
chmod +x kubeseal-linux-amd64 && sudo mv kubeseal-linux-amd64 /usr/local/bin/kubeseal
Terminal window
kdef seal --secret db-credentials --key DATABASE_URL \
--value "postgres://api:hunter2@db:5432/api"

Output:

# Paste into your sealedsecret block:
sealedsecret "db-credentials" {
data = {
DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq..."
}
}

Or pipe from stdin so the plaintext never touches your shell history:

Terminal window
echo -n "hunter2" | kdef seal --secret db-credentials --key PASSWORD

If you already have a live Kubernetes Secret, seal-secret turns the whole thing into a ready-to-commit block:

Terminal window
kdef seal-secret --name db-credentials --namespace production

Output:

sealedsecret "db-credentials" {
namespace = "production"
data = {
DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO..."
READONLY_URL = "AgBz2j5PKTXL+QjUzTZaaB0sP..."
}
}

Drop that into secrets.kdef and commit it.

The secret() function works the same for plain secret blocks and sealedsecret blocks — you reference by name, not by storage mechanism:

secrets.kdef
sealedsecret "db-credentials" {
namespace = "production"
data = {
DATABASE_URL = "AgBy3i4OJSWK+PiTySYZZA9rO..."
}
}
api.kdef
deployment "api" {
namespace = "production"
container "api" {
image = image("api")
env {
DATABASE_URL = secret("db-credentials", "DATABASE_URL")
}
}
}

At deploy time the controller decrypts the SealedSecret into a regular Secret, and the pod reads from it via valueFrom.secretKeyRef.

To rotate a key, re-seal with the new value and replace the ciphertext:

Terminal window
kdef seal --secret db-credentials --key DATABASE_URL \
--value "postgres://api:new-password@db:5432/api"

Paste the new ciphertext into secrets.kdef, commit, and let your GitOps tool apply it.

  • Different clusters have different sealing keys. A SealedSecret encrypted for staging won’t decrypt in production. Re-seal per target cluster.
  • If you run sealed-secrets in a non-default namespace or with a non-default controller name, pass --namespace and --controller-name to kdef seal.
  • For local development you usually want plain secret blocks — they don’t need kubeseal and render as normal v1/Secret manifests.