Secrets

4 minute read

While ConfigMaps store general configuration, Secrets are designed for sensitive data like passwords, API keys, tokens, and TLS certificates. Secrets are similar to ConfigMaps but with additional safeguards.

What is a Secret?

A Secret is a Kubernetes object that holds sensitive data in key-value pairs. Compared to ConfigMaps:

  • Data is base64-encoded (not encrypted by default)
  • Secrets are stored in tmpfs on nodes (not written to disk)
  • Access can be restricted via RBAC policies
  • Kubernetes limits Secret size to 1 MB

Important: Base64 encoding is NOT encryption. Anyone with access to the Secret can decode it. For real encryption, enable encryption at rest in your cluster or use an external secrets manager.

Secret Types

Type Description Use Case
Opaque Generic key-value pairs (default) Passwords, API keys
kubernetes.io/dockerconfigjson Docker registry credentials Pulling private images
kubernetes.io/tls TLS certificate and key HTTPS/TLS termination
kubernetes.io/basic-auth Username and password Basic authentication
kubernetes.io/ssh-auth SSH private key SSH authentication
kubernetes.io/service-account-token Service account token Auto-generated by K8s

Creating Secrets

From Literal Values

kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password='S3cureP@ss!'

From Files

# Create files
echo -n 'admin' > username.txt
echo -n 'S3cureP@ss!' > password.txt

# Create Secret from files
kubectl create secret generic db-credentials \
  --from-file=username=username.txt \
  --from-file=password=password.txt

# Clean up plain text files
rm username.txt password.txt

Declarative YAML

Values must be base64-encoded:

# Encode values
echo -n 'admin' | base64           # YWRtaW4=
echo -n 'S3cureP@ss!' | base64    # UzNjdXJlUEBzcyE=
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=          # base64 of 'admin'
  password: UzNjdXJlUEBzcyE=  # base64 of 'S3cureP@ss!'

Using stringData (Plain Text — Easier)

You can use stringData to avoid manual base64 encoding:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: admin
  password: "S3cureP@ss!"

Kubernetes automatically base64-encodes stringData when storing the Secret.

Consuming Secrets

As Environment Variables (Individual Keys)

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: my-app:1.0
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password

As Environment Variables (All Keys)

spec:
  containers:
  - name: app
    image: my-app:1.0
    envFrom:
    - secretRef:
        name: db-credentials

As Mounted Volume (Files)

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: my-app:1.0
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials

Each key becomes a file:

/etc/secrets/username  → contains "admin"
/etc/secrets/password  → contains "S3cureP@ss!"

TLS Secrets

For HTTPS certificates:

# Create a TLS Secret from certificate files
kubectl create secret tls my-tls-secret \
  --cert=tls.crt \
  --key=tls.key

Or declaratively:

apiVersion: v1
kind: Secret
metadata:
  name: my-tls-secret
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>

Used with Ingress for HTTPS:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  tls:
  - hosts:
    - myapp.example.com
    secretName: my-tls-secret
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

Docker Registry Secrets

Pull images from private registries:

kubectl create secret docker-registry my-registry-secret \
  --docker-server=registry.example.com \
  --docker-username=myuser \
  --docker-password=mypassword \
  --docker-email=user@example.com

Reference it in a Pod:

spec:
  imagePullSecrets:
  - name: my-registry-secret
  containers:
  - name: app
    image: registry.example.com/my-app:1.0

Hands-On: Complete Example

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  DB_HOST: postgres
  DB_USER: appuser
  DB_PASSWORD: "MyS3cretP@ss"
  API_KEY: "sk-abc123def456"
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: busybox
        command: ["sh", "-c", "echo DB=$DB_HOST user=$DB_USER && sleep 3600"]
        envFrom:
        - secretRef:
            name: app-secrets
kubectl apply -f secret.yaml

# Verify the Secret (values are base64-encoded in output)
kubectl get secret app-secrets -o yaml

# Decode a value
kubectl get secret app-secrets -o jsonpath='{.data.DB_PASSWORD}' | base64 -d

# Check the app logs to see the environment variables
kubectl logs deployment/my-app

Inspecting Secrets

# List Secrets
kubectl get secrets

# Describe (doesn't show values)
kubectl describe secret db-credentials

# View base64-encoded values
kubectl get secret db-credentials -o yaml

# Decode a specific key
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d

Security Best Practices

1. Enable Encryption at Rest

By default, Secrets are stored unencrypted in etcd. Enable encryption:

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

2. Use RBAC to Restrict Access

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
  resourceNames: ["app-secrets"]   # Only specific Secrets

3. Use External Secrets Managers

For production, consider integrating with:

  • HashiCorp Vault — Full-featured secrets management
  • AWS Secrets Manager — AWS-native solution
  • Azure Key Vault — Azure-native solution
  • GCP Secret Manager — GCP-native solution

Tools like External Secrets Operator sync external secrets into Kubernetes Secrets automatically.

4. Don’t Commit Secrets to Git

# .gitignore
*-secret.yaml
*-secrets.yaml

Use sealed-secrets or external secrets operators for GitOps workflows.

Summary

  • Secrets store sensitive data (passwords, tokens, certificates)
  • Data is base64-encoded, not encrypted by default
  • Consume Secrets as environment variables or mounted files
  • Use RBAC to restrict who can access Secrets
  • Enable encryption at rest for production clusters
  • Consider external secrets managers (Vault, AWS SM, etc.) for enterprise use
  • Never commit Secret YAML files to Git

What’s Next?

Your applications need to persist data beyond the lifetime of a Pod. In the next chapter, we’ll learn about Volumes and Persistent Storage — how Kubernetes handles data that survives Pod restarts and rescheduling.

Next: Volumes and Persistent Storage →

Updated: