K8s by Example: Secrets

Secrets store sensitive data: passwords, tokens, keys. Base64-encoded by default (not encrypted!). In production, enable encryption at rest and consider external secret managers like Vault or AWS Secrets Manager.

secret.yaml

Secrets use the core v1 API. Values in data must be base64 encoded. Use echo -n “password” | base64 to encode.

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  DB_PASSWORD: cGFzc3dvcmQ=
  API_KEY: c2VjcmV0LWtleQ==
secret-stringdata.yaml

Use stringData to avoid manual base64 encoding since Kubernetes encodes it automatically. You can mix data and stringData in the same Secret.

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  DB_PASSWORD: "my-actual-password"
  API_KEY: "my-api-key"
  config.yaml: |
    database:
      host: postgres
      port: 5432
secret-types.yaml

Secret types provide structure and validation. Opaque is generic. Built-in types include TLS, docker-registry, basic-auth, and ssh-auth with required keys.

apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
  name: tls-secret
data:
  tls.crt: <base64-cert>
  tls.key: <base64-key>
---
apiVersion: v1
kind: Secret
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: secret123
pod-secret-env.yaml

Inject secrets as environment variables. Same pattern as ConfigMaps. Use optional: true to allow Pod startup if secret doesn’t exist.

spec:
  containers:
    - name: app
      envFrom:
        - secretRef:
            name: app-secrets
            optional: true
      env:
        - name: PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: DB_PASSWORD
pod-secret-volume.yaml

Mount secrets as files. Files are stored in tmpfs (RAM, not disk). Set proper permissions with defaultMode. Use items to mount specific keys.

spec:
  containers:
    - name: app
      volumeMounts:
        - name: certs
          mountPath: /etc/ssl/certs
          readOnly: true
  volumes:
    - name: certs
      secret:
        secretName: tls-certs
        defaultMode: 0400
        items:
          - key: tls.crt
            path: server.crt
terminal

Docker registry secrets authenticate image pulls from private registries. Create with kubectl or declaratively.

$ kubectl create secret docker-registry regcred \
    --docker-server=ghcr.io \
    --docker-username=user \
    --docker-password=token
secret/regcred created

Reference in Pod spec with imagePullSecrets. Can also be attached to a ServiceAccount for automatic use.

spec:
  imagePullSecrets:
    - name: regcred
  containers:
    - name: app
      image: ghcr.io/org/private-app:v1
encryption-config.yaml

Encryption at rest protects Secrets in etcd. Without it, Secrets are only base64-encoded (easily decoded). Configure EncryptionConfiguration on the API server.

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-32-byte-key>
      - identity: {}
terminal

Best practices: use RBAC to restrict Secret access, avoid committing to Git (use sealed-secrets), rotate credentials regularly. Secrets are namespaced.

$ kubectl get secret app-secrets -o yaml
apiVersion: v1
kind: Secret
data:
  DB_PASSWORD: cGFzc3dvcmQ=
...

$ kubectl get secret app-secrets \
    -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
password

$ kubectl get secrets --all-namespaces
NAMESPACE   NAME          TYPE     DATA   AGE
default     app-secrets   Opaque   2      5m
kube-system coredns       Opaque   0      30d

Index | GitHub | Use arrow keys to navigate |