Secrets
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.