How to Deploy MySQL with StatefulSet
Deploy a production-ready MySQL database on Kubernetes using StatefulSet. Learn persistent storage, headless services, and backup strategies.
💡 Quick Answer: Deploy MySQL with StatefulSet + headless Service + PVC. Use
volumeClaimTemplatesfor persistent storage, headless Service (clusterIP: None) for stable DNS. Store credentials in Secrets. MySQL is single-writer—for HA, use Percona XtraDB Cluster or MySQL Operator.Key resources: StatefulSet (ordered pods), PVC (persistent data), ConfigMap (my.cnf), Secret (root password), headless Service (DNS records).
Gotcha: MySQL requires
fsGroupin securityContext for file permissions; always test backup/restore before production.
The Problem
You need to run MySQL on Kubernetes with persistent storage, stable network identity, and ordered deployment/scaling.
The Solution
Use a StatefulSet with PersistentVolumeClaims to deploy MySQL with stable storage and predictable pod names.
Understanding StatefulSets
StatefulSets provide:
- Stable, unique network identifiers
- Stable, persistent storage
- Ordered deployment and scaling
- Ordered, automated rolling updates
Step 1: Create a Secret for MySQL Password
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
stringData:
mysql-root-password: "YourSecurePassword123!"
mysql-password: "AppPassword456!"Apply it:
kubectl apply -f mysql-secret.yamlStep 2: Create a ConfigMap for MySQL Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
default-authentication-plugin=mysql_native_password
max_connections=200
innodb_buffer_pool_size=256M
innodb_log_file_size=64M
slow_query_log=1
slow_query_log_file=/var/log/mysql/slow.log
long_query_time=2Step 3: Create a Headless Service
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
name: mysql
clusterIP: None # Headless service
selector:
app: mysqlStep 4: Create the StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
terminationGracePeriodSeconds: 30
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: mysql-root-password
- name: MYSQL_DATABASE
value: "myapp"
- name: MYSQL_USER
value: "appuser"
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: mysql-password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: config
mountPath: /etc/mysql/conf.d
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1"
livenessProbe:
exec:
command:
- mysqladmin
- ping
- -h
- localhost
- -u
- root
- -p${MYSQL_ROOT_PASSWORD}
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command:
- mysql
- -h
- localhost
- -u
- root
- -p${MYSQL_ROOT_PASSWORD}
- -e
- "SELECT 1"
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 2
volumes:
- name: config
configMap:
name: mysql-config
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "standard" # Use your StorageClass
resources:
requests:
storage: 10GiStep 5: Create a Service for External Access
For applications to connect:
apiVersion: v1
kind: Service
metadata:
name: mysql-external
spec:
type: ClusterIP
ports:
- port: 3306
targetPort: 3306
selector:
app: mysqlConnecting to MySQL
From Within the Cluster
DNS name: mysql-0.mysql.<namespace>.svc.cluster.local
mysql -h mysql-0.mysql.default.svc.cluster.local -u root -pUsing kubectl
kubectl exec -it mysql-0 -- mysql -u root -pFrom Your Application
env:
- name: DATABASE_HOST
value: "mysql-0.mysql"
- name: DATABASE_PORT
value: "3306"
- name: DATABASE_NAME
value: "myapp"Backup Strategy
Manual Backup
kubectl exec mysql-0 -- mysqldump -u root -p${MYSQL_ROOT_PASSWORD} --all-databases > backup.sqlCronJob for Automated Backups
apiVersion: batch/v1
kind: CronJob
metadata:
name: mysql-backup
spec:
schedule: "0 2 * * *" # Daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: mysql:8.0
command:
- /bin/sh
- -c
- |
mysqldump -h mysql-0.mysql -u root -p${MYSQL_ROOT_PASSWORD} \
--all-databases | gzip > /backup/mysql-$(date +%Y%m%d).sql.gz
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: mysql-root-password
volumeMounts:
- name: backup
mountPath: /backup
restartPolicy: OnFailure
volumes:
- name: backup
persistentVolumeClaim:
claimName: mysql-backup-pvcScaling Considerations
Single Instance
For simple applications, one replica is sufficient.
MySQL Replication
For high availability, consider:
- MySQL Group Replication
- Percona XtraDB Cluster
- MySQL Operator
Monitoring MySQL
Add Prometheus exporter:
- name: exporter
image: prom/mysqld-exporter:latest
ports:
- containerPort: 9104
name: metrics
env:
- name: DATA_SOURCE_NAME
value: "exporter:exporterpassword@(localhost:3306)/"Troubleshooting
Check Pod Status
kubectl get pods -l app=mysql
kubectl describe pod mysql-0View Logs
kubectl logs mysql-0Check PVC
kubectl get pvc
kubectl describe pvc data-mysql-0Access MySQL Shell
kubectl exec -it mysql-0 -- mysql -u root -pBest Practices
- Use Secrets for passwords
- Set resource limits to prevent resource starvation
- Configure backups before going to production
- Use PodDisruptionBudget for maintenance windows
- Monitor disk usage to avoid running out of space
Key Takeaways
- StatefulSets provide stable identity and storage
- Headless services enable direct pod DNS
- PersistentVolumeClaims retain data across restarts
- Always implement backup strategies
- Consider operators for production deployments
📘 Go Further with Kubernetes Recipes
Love this recipe? There’s so much more! This is just one of 100+ hands-on recipes in our comprehensive Kubernetes Recipes book.
Inside the book, you’ll master:
- ✅ Production-ready deployment strategies
- ✅ Advanced networking and security patterns
- ✅ Observability, monitoring, and troubleshooting
- ✅ Real-world best practices from industry experts
“The practical, recipe-based approach made complex Kubernetes concepts finally click for me.”
👉 Get Your Copy Now — Start building production-grade Kubernetes skills today!
📘 Get All 100+ Recipes in One Book
Stop searching — get every production-ready pattern with detailed explanations, best practices, and copy-paste YAML.
Want More Kubernetes Recipes?
This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.