Convert Pod to Deployment with Secret Management
Scenario
A Pod named osaka-pgsql-pod is currently running in the osaka-database namespace. The manifest file is available on the dev-machine at:
/home/laborant/osaka-pgsql-pod.yaml
The Pod contains hardcoded environment variables for PostgreSQL configuration. This approach is not secure or scalable for production environments.
You need to migrate this Pod to a Deployment for better scalability and management, externalize the configuration to a Secret for security, and remove the original Pod once the migration is complete.
Tasks
Convert the Pod into a Deployment named osaka-pgsql-deployment with 3 replicas in the osaka-database namespace.
Extract the hardcoded environment variables into a Secret named osaka-pgsql-secret in the osaka-database namespace.
Update the Deployment to consume environment variables from the Secret instead of plain text.
Preserve all existing labels and ensure all replicas are running.
Delete the original Pod osaka-pgsql-pod after successful conversion.
Important: Do not modify the original Pod manifest file. The Deployment should reference the Secret, not plain text values.
Hint
Create Secret from literal values, convert Pod spec to Deployment template, use envFrom with secretRef to inject all Secret keys as environment variables.
Test Cases
Convert a Pod named osaka-pgsql-pod into a Deployment with 3 replicas, and move hardcoded environment variables into a Secret for better security.
What we're starting with:
- A Pod with hardcoded passwords (bad practice!)
- A single instance (not scalable)
What we need to create:
- A Deployment with 3 replicas (scalable!)
- A Secret to store passwords (secure!)
- Delete the old Pod
Understanding the Current Pod
Let's first look at what we have:
# View the Pod manifest file
cat /home/laborant/osaka-pgsql-pod.yaml
Output:
apiVersion: v1
kind: Pod
metadata:
name: osaka-pgsql-pod
namespace: osaka-database
labels:
app: osaka-pgsql # ← Pod Labels
tier: database
region: osaka
spec:
containers:
- name: postgres # ← Container name
image: postgres:13 # ← Container image
env:
- name: POSTGRES_USER
value: "admin" # ← Hardcoded! Bad!
- name: POSTGRES_PASSWORD
value: "SecurePass123" # ← Password in plain text! Very bad!
- name: POSTGRES_DB
value: "osaka_db"
ports:
- containerPort: 5432 # ← PostgreSQL port
Problems with this Pod:
1. Hardcoded Secrets
The password SecurePass123 is visible in plain text in the YAML file. Anyone who can see this file can see the password.
2. Not Scalable A Pod is just one instance. If it crashes, it doesn't restart automatically. We can't scale to multiple instances.
3. No Self-Healing If you delete this Pod, it's gone forever. Kubernetes won't recreate it.
What We Need to Convert
From Pod → To Deployment
A Deployment is like a Pod manager. It:
- Creates Pods for you
- Keeps the desired number of Pods running (3 replicas)
- Restarts Pods if they crash
- Allows rolling updates
Key changes needed:
- Change
kind: Podtokind: Deployment - Add
replicas: 3 - Move the Pod spec into a
templatesection - Add a
selectorto identify which Pods belong to this Deployment
Extracting Secrets
Instead of hardcoded values, we'll:
- Create a Secret to store sensitive data
- Reference the Secret in the Deployment
- Environment variables will be injected from the Secret at runtime
Step 1: Examine the Original Pod
First, let's check if the Pod is running:
kubectl get pod osaka-pgsql-pod -n osaka-database
Expected output:
NAME READY STATUS RESTARTS AGE
osaka-pgsql-pod 1/1 Running 0 5m
Let's see what environment variables it has:
kubectl exec osaka-pgsql-pod -n osaka-database -- env | grep POSTGRES
Expected output:
POSTGRES_USER=admin
POSTGRES_PASSWORD=SecurePass123
POSTGRES_DB=osaka_db
These are the three values we need to extract into a Secret.
Step 2: Create the Secret
We'll create a Secret with all three environment variables:
kubectl create secret generic osaka-pgsql-secret \
--from-literal=POSTGRES_USER=admin \
--from-literal=POSTGRES_PASSWORD=SecurePass123 \
--from-literal=POSTGRES_DB=osaka_db \
-n osaka-database
Expected output:
secret/osaka-pgsql-secret created
What this does:
generic- creates a basic Secret (as opposed to TLS or Docker registry secrets)--from-literal- creates key-value pairs directly from the command line- Each
--from-literaladds one environment variable to the Secret
Verify the Secret was created:
kubectl get secret osaka-pgsql-secret -n osaka-database
Expected output:
NAME TYPE DATA AGE
osaka-pgsql-secret Opaque 3 10s
The DATA: 3 means it contains 3 key-value pairs.
View the Secret (values are base64 encoded):
kubectl describe secret osaka-pgsql-secret -n osaka-database
Expected output:
Name: osaka-pgsql-secret
Namespace: osaka-database
Type: Opaque
Data
====
POSTGRES_DB: 8 bytes
POSTGRES_PASSWORD: 13 bytes
POSTGRES_USER: 5 bytes
Notice it shows the size but not the actual values - this is for security.
Step 3: Create the Deployment
Now we'll convert the Pod spec into a Deployment.
Understanding the Conversion
Original Pod (what we have):
apiVersion: v1 # ← Core API version for Pods
kind: Pod # ← Resource type: Pod
metadata:
name: osaka-pgsql-pod # ← Pod name
namespace: osaka-database # ← Namespace where Pod lives
labels: # ← Labels for identification
app: osaka-pgsql # ← Application name
tier: database # ← Application tier
region: osaka # ← Region/location
spec: # ← Pod specification
containers: # ← List of containers in this Pod
- name: postgres # ← Container name
image: postgres:13 # ← Container image and version
env: # ← Environment variables (HARDCODED - BAD!)
- name: POSTGRES_USER
value: "admin" # ← Plain text username
- name: POSTGRES_PASSWORD
value: "SecurePass123" # ← Plain text password - SECURITY ISSUE!
- name: POSTGRES_DB
value: "osaka_db" # ← Database name
ports: # ← Ports to expose
- containerPort: 5432 # ← PostgreSQL default port
Deployment (what we need to create):
apiVersion: apps/v1 # ← Changed from v1
kind: Deployment # ← Changed from Pod
metadata:
name: osaka-pgsql-deployment # ← New name
namespace: osaka-database
spec:
replicas: 3 # ← NEW: Creates 3 Pods
selector: # ← NEW: Identifies managed Pods
matchLabels:
app: osaka-pgsql
template: # ← NEW: Pod template starts here
metadata:
labels: # ← Same labels as original Pod
app: osaka-pgsql
tier: database
region: osaka
spec: # ← Original Pod spec goes here
containers:
- name: postgres
image: postgres:13
envFrom: # ← Changed: use Secret instead
- secretRef:
name: osaka-pgsql-secret
ports:
- containerPort: 5432
Key differences:
apiVersion: apps/v1- Deployments are in apps API groupkind: Deployment- Different resource typereplicas: 3- Tells Kubernetes to maintain 3 Podsselector.matchLabels- How Deployment finds its Pods (must match at least one label in template)templatewrapper - Contains the Pod definitionenvFromwithsecretRef- Replaces hardcodedenvvalues
Create the Deployment YAML
cat > osaka-pgsql-deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: osaka-pgsql-deployment
namespace: osaka-database
spec:
replicas: 3
selector:
matchLabels:
app: osaka-pgsql
template:
metadata:
labels:
app: osaka-pgsql
tier: database
region: osaka
spec:
containers:
- name: postgres
image: postgres:13
envFrom:
- secretRef:
name: osaka-pgsql-secret
ports:
- containerPort: 5432
EOF
Key changes explained:
apiVersion: apps/v1- Deployments are in the
appsAPI group, not corev1
- Deployments are in the
replicas: 3- Creates 3 identical Pods
selector.matchLabels- Tells the Deployment which Pods it manages
- Must match the labels in
template.metadata.labels
template- This is the Pod template
- Everything under
template.specis the same as the original Pod spec
envFromwithsecretRef- Instead of hardcoded
envvalues - Injects ALL keys from the Secret as environment variables
- Much cleaner than listing each variable individually
- Instead of hardcoded
Alternative (individual variables): If you wanted to pick specific variables, you could use:
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: osaka-pgsql-secret
key: POSTGRES_USER
But envFrom is simpler when you want all variables.
Step 4: Apply the Deployment
kubectl apply -f osaka-pgsql-deployment.yaml
Expected output:
deployment.apps/osaka-pgsql-deployment created
Watch the Deployment create Pods:
kubectl get deployment osaka-pgsql-deployment -n osaka-database -w
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
osaka-pgsql-deployment 0/3 3 0 2s
osaka-pgsql-deployment 1/3 3 1 15s
osaka-pgsql-deployment 2/3 3 2 18s
osaka-pgsql-deployment 3/3 3 3 22s
Press Ctrl+C to stop watching.
Check the Pods:
kubectl get pods -n osaka-database -l app=osaka-pgsql
Expected output:
NAME READY STATUS RESTARTS AGE
osaka-pgsql-deployment-5b8c7d9f4b-2xkqw 1/1 Running 0 30s
osaka-pgsql-deployment-5b8c7d9f4b-7hmnt 1/1 Running 0 30s
osaka-pgsql-deployment-5b8c7d9f4b-k9p2v 1/1 Running 0 30s
osaka-pgsql-pod 1/1 Running 0 10m
Notice:
- 3 new Pods from the Deployment (with random suffixes)
- 1 old Pod (the original) - we'll delete this next
Step 5: Verify Environment Variables
Let's check that the Deployment Pods are getting environment variables from the Secret:
# Get one of the Deployment Pod names
POD=$(kubectl get pods -n osaka-database -l app=osaka-pgsql --field-selector=metadata.name!=osaka-pgsql-pod -o jsonpath='{.items[0].metadata.name}')
# Check its environment variables
kubectl exec $POD -n osaka-database -- env | grep POSTGRES
Expected output:
POSTGRES_USER=admin
POSTGRES_PASSWORD=SecurePass123
POSTGRES_DB=osaka_db
Perfect! The environment variables are being injected from the Secret.
Check the Pod doesn't have hardcoded values:
kubectl get deployment osaka-pgsql-deployment -n osaka-database -o yaml | grep -A 10 "env"
Expected output:
...
envFrom:
- secretRef:
name: osaka-pgsql-secret
...
No hardcoded value: fields - all references to the Secret!
Step 6: Delete the Original Pod
Now that the Deployment is running successfully, we can delete the old Pod:
kubectl delete pod osaka-pgsql-pod -n osaka-database
Expected output:
pod "osaka-pgsql-pod" deleted
Verify only Deployment Pods remain:
kubectl get pods -n osaka-database
Expected output:
NAME READY STATUS RESTARTS AGE
osaka-pgsql-deployment-5b8c7d9f4b-2xkqw 1/1 Running 0 2m
osaka-pgsql-deployment-5b8c7d9f4b-7hmnt 1/1 Running 0 2m
osaka-pgsql-deployment-5b8c7d9f4b-k9p2v 1/1 Running 0 2m
Only the 3 Deployment Pods remain!
All done! ✅