Kubernetes Secrets store sensitive data like passwords, API keys, and TLS certificates. While convenient, native Secrets require careful handling to maintain security. This comprehensive guide covers Secrets management best practices for production environments in 2026.
Quick Reference: Kubernetes Secrets Commands
| Command | Description |
|---|---|
kubectl create secret generic name --from-literal=key=value | Create from literal |
kubectl create secret generic name --from-file=file | Create from file |
kubectl create secret tls name --cert=tls.crt --key=tls.key | Create TLS secret |
kubectl create secret docker-registry name --docker-server=... | Create registry secret |
kubectl get secrets | List secrets |
kubectl describe secret name | Describe secret (hides values) |
kubectl get secret name -o jsonpath='{.data.key}' | base64 -d | Decode secret value |
Understanding Kubernetes Secrets
What Are Secrets?
Secrets are Kubernetes objects that store sensitive data in key-value pairs. They’re similar to ConfigMaps but designed for confidential information.
How Secrets Are Stored
- API Server - Receives secret data (base64 encoded)
- etcd - Stores secrets (optionally encrypted at rest)
- Kubelet - Mounts secrets into pods
- Pod - Consumes secrets as env vars or volumes
Secret Types
| Type | Use Case |
|---|---|
Opaque | General purpose (default) |
kubernetes.io/tls | TLS certificates |
kubernetes.io/dockerconfigjson | Docker registry credentials |
kubernetes.io/service-account-token | Service account tokens |
kubernetes.io/basic-auth | Basic authentication |
kubernetes.io/ssh-auth | SSH credentials |
Creating Secrets
From Literal Values
# Create secret with key-value pairs
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='S3cr3tP@ssw0rd!'
# With namespace
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password='S3cr3tP@ssw0rd!' \
-n production
From Files
# Create from files
kubectl create secret generic tls-certs \
--from-file=tls.crt \
--from-file=tls.key
# With custom key names
kubectl create secret generic ssh-key \
--from-file=id_rsa=./ssh/id_rsa \
--from-file=id_rsa.pub=./ssh/id_rsa.pub
TLS Secrets
kubectl create secret tls my-tls-secret \
--cert=tls.crt \
--key=tls.key
Docker Registry Secrets
kubectl create secret docker-registry my-registry \
--docker-server=https://registry.example.com \
--docker-username=user \
--docker-password=password \
--docker-email=user@example.com
From YAML Manifest
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
# Values must be base64 encoded
username: YWRtaW4= # echo -n 'admin' | base64
password: UzNjcjN0UEBzc3cwcmQh # echo -n 'S3cr3tP@ssw0rd!' | base64
Using stringData (Plain Text)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
# Plain text - Kubernetes encodes automatically
username: admin
password: S3cr3tP@ssw0rd!
Consuming Secrets
As Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
All Keys as Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- secretRef:
name: db-credentials
As Volume Mount
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: secrets-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets-volume
secret:
secretName: db-credentials
defaultMode: 0400 # Read-only for owner
Mount Specific Keys
volumes:
- name: secrets-volume
secret:
secretName: db-credentials
items:
- key: password
path: db-password.txt
For Docker Registry Authentication
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
imagePullSecrets:
- name: my-registry
containers:
- name: app
image: registry.example.com/myapp:latest
Encryption at Rest
By default, Secrets are stored unencrypted in etcd. Enable encryption at rest for security.
Configure Encryption
Create encryption config:
# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <32-byte-base64-encoded-key>
- identity: {} # Fallback for reading unencrypted secrets
Generate encryption key:
head -c 32 /dev/urandom | base64
Configure API Server
Add to kube-apiserver:
spec:
containers:
- command:
- kube-apiserver
- --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
Encrypt Existing Secrets
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
External Secrets Managers
For production, use external secrets managers for enhanced security.
External Secrets Operator (ESO)
Install ESO:
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace
AWS Secrets Manager Integration
# SecretStore
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
---
# ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets
kind: SecretStore
target:
name: db-credentials
data:
- secretKey: username
remoteRef:
key: production/database
property: username
- secretKey: password
remoteRef:
key: production/database
property: password
HashiCorp Vault Integration
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "my-role"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 15m
secretStoreRef:
name: vault
kind: SecretStore
target:
name: db-credentials
data:
- secretKey: password
remoteRef:
key: databases/production
property: password
RBAC for Secrets
Restrict access to secrets with Role-Based Access Control.
Read-Only Access
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
resourceNames: ["app-secrets"] # Limit to specific secrets
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-secrets
namespace: production
subjects:
- kind: ServiceAccount
name: app-sa
namespace: production
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
Deny Secrets Access
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: no-secrets
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "services", "deployments"]
verbs: ["*"]
# No rules for secrets = no access
Sealed Secrets for GitOps
Encrypt secrets for safe storage in Git.
Install Sealed Secrets
# Install controller
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
# Install kubeseal CLI
brew install kubeseal
Create Sealed Secret
# Create regular secret manifest
kubectl create secret generic db-credentials \
--from-literal=password=mysecret \
--dry-run=client -o yaml > secret.yaml
# Seal the secret
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
sealed-secret.yaml (safe to commit to Git):
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
password: AgBy3i...encrypted...data...
# Apply sealed secret
kubectl apply -f sealed-secret.yaml
Security Best Practices
1. Enable Encryption at Rest
Always encrypt secrets in etcd.
2. Use External Secrets Managers
For production, integrate with Vault, AWS Secrets Manager, or Azure Key Vault.
3. Implement Least Privilege RBAC
# Only allow specific secrets
resourceNames: ["allowed-secret-1", "allowed-secret-2"]
4. Avoid Environment Variables for Sensitive Data
Volume mounts are more secure:
# Better: volume mount
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
# Avoid: environment variable (visible in pod spec)
env:
- name: PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
5. Rotate Secrets Regularly
# Update secret
kubectl create secret generic db-credentials \
--from-literal=password=newpassword \
--dry-run=client -o yaml | kubectl apply -f -
# Restart pods to pick up new secret
kubectl rollout restart deployment/app
6. Audit Secret Access
Enable Kubernetes audit logging:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
7. Don’t Log Secrets
# Bad
logger.info(f"Connecting with password: {password}")
# Good
logger.info("Connecting to database...")
8. Use Secret Scanning in CI/CD
Integrate tools like:
- GitLeaks
- TruffleHog
- GitHub Secret Scanning
Complete Example: Secure Application
Secrets
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
stringData:
db-password: "SecureP@ssword123"
api-key: "sk-api-key-12345"
---
apiVersion: v1
kind: Secret
metadata:
name: tls-certs
namespace: production
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
Deployment with Secrets
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
serviceAccountName: api-sa
containers:
- name: api
image: api:v1.0.0
ports:
- containerPort: 8080
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
- name: tls
mountPath: /etc/tls
readOnly: true
volumes:
- name: secrets
secret:
secretName: app-secrets
defaultMode: 0400
- name: tls
secret:
secretName: tls-certs
defaultMode: 0400
Troubleshooting
Secret Not Found
# Check secret exists
kubectl get secret db-credentials -n production
# Check namespace
kubectl get secrets -n production
Permission Denied
# Check RBAC
kubectl auth can-i get secrets -n production --as system:serviceaccount:production:app-sa
Decode Secret Value
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
Secret Not Updating in Pod
Secrets mounted as volumes update automatically (~1 min). Secrets as env vars require pod restart:
kubectl rollout restart deployment/app
Conclusion
Kubernetes Secrets require careful management for security in 2026. Key takeaways:
- Enable encryption at rest for etcd
- Use external secrets managers for production
- Implement RBAC with least privilege
- Prefer volume mounts over environment variables
- Use Sealed Secrets for GitOps
- Rotate secrets regularly
- Audit secret access
Master Secrets management to build secure Kubernetes applications.
Related Resources
- Kubernetes ConfigMap 2026
- Kubernetes Security News 2026
- Application Security Monitoring 2026
- Kubernetes Consulting Services
Need help with Kubernetes security? Book a free 30-minute consultation with our security experts.