Introduction
To connect to MySQL running on Kubernetes, you need a MySQL Deployment or StatefulSet, a Service to expose it, and the correct connection string using the Service's DNS name. Within the cluster, pods connect via mysql-service.namespace.svc.cluster.local:3306. For external access, use kubectl port-forward, a NodePort Service, or a LoadBalancer Service. Store credentials in a Kubernetes Secret and mount them as environment variables.
Deploying MySQL on Kubernetes
Secret for Credentials
1apiVersion: v1
2kind: Secret
3metadata:
4 name: mysql-secret
5type: Opaque
6data:
7 mysql-root-password: cGFzc3dvcmQxMjM= # base64 encoded "password123"
8 mysql-database: bXlhcHA= # base64 encoded "myapp"
# Create base64 values
echo -n "password123" | base64 # cGFzc3dvcmQxMjM=
echo -n "myapp" | base64 # bXlhcHA=
Deployment and Service
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: mysql
5spec:
6 selector:
7 matchLabels:
8 app: mysql
9 template:
10 metadata:
11 labels:
12 app: mysql
13 spec:
14 containers:
15 - name: mysql
16 image: mysql:8.0
17 ports:
18 - containerPort: 3306
19 env:
20 - name: MYSQL_ROOT_PASSWORD
21 valueFrom:
22 secretKeyRef:
23 name: mysql-secret
24 key: mysql-root-password
25 - name: MYSQL_DATABASE
26 valueFrom:
27 secretKeyRef:
28 name: mysql-secret
29 key: mysql-database
30 volumeMounts:
31 - name: mysql-storage
32 mountPath: /var/lib/mysql
33 volumes:
34 - name: mysql-storage
35 persistentVolumeClaim:
36 claimName: mysql-pvc
37---
38apiVersion: v1
39kind: Service
40metadata:
41 name: mysql-service
42spec:
43 selector:
44 app: mysql
45 ports:
46 - port: 3306
47 targetPort: 3306
48 type: ClusterIP
PersistentVolumeClaim
1apiVersion: v1
2kind: PersistentVolumeClaim
3metadata:
4 name: mysql-pvc
5spec:
6 accessModes:
7 - ReadWriteOnce
8 resources:
9 requests:
10 storage: 10Gi
Connecting from Inside the Cluster
Pods within the same Kubernetes cluster connect using the Service's DNS name:
1# Python application in the same namespace
2import mysql.connector
3
4connection = mysql.connector.connect(
5 host="mysql-service", # Service name
6 port=3306,
7 user="root",
8 password="password123",
9 database="myapp"
10)
1// Java / Spring Boot application.properties
2spring.datasource.url=jdbc:mysql://mysql-service:3306/myapp
3spring.datasource.username=root
4spring.datasource.password=password123
Full DNS Name
mysql-service.default.svc.cluster.local
Format: <service-name>.<namespace>.svc.cluster.local
If the application is in a different namespace:
1connection = mysql.connector.connect(
2 host="mysql-service.database-namespace.svc.cluster.local",
3 port=3306,
4 user="root",
5 password="password123",
6 database="myapp"
7)
Connecting from Outside the Cluster
Method 1: kubectl port-forward (Development)
1# Forward local port 3306 to the MySQL pod
2kubectl port-forward svc/mysql-service 3306:3306
3
4# Now connect locally
5mysql -h 127.0.0.1 -P 3306 -u root -p
This is for development only — the tunnel stops when you close the terminal.
Method 2: NodePort Service
1apiVersion: v1
2kind: Service
3metadata:
4 name: mysql-nodeport
5spec:
6 type: NodePort
7 selector:
8 app: mysql
9 ports:
10 - port: 3306
11 targetPort: 3306
12 nodePort: 30306 # Accessible on any node's IP at port 30306
# Connect using any node's IP
mysql -h <node-ip> -P 30306 -u root -p
Method 3: LoadBalancer Service (Cloud)
1apiVersion: v1
2kind: Service
3metadata:
4 name: mysql-lb
5spec:
6 type: LoadBalancer
7 selector:
8 app: mysql
9 ports:
10 - port: 3306
11 targetPort: 3306
1# Get the external IP
2kubectl get svc mysql-lb
3# NAME TYPE EXTERNAL-IP PORT(S)
4# mysql-lb LoadBalancer 203.0.113.50 3306:31234/TCP
5
6mysql -h 203.0.113.50 -P 3306 -u root -p
Using a StatefulSet (Production)
For production MySQL deployments, use a StatefulSet with stable network identities:
1apiVersion: apps/v1
2kind: StatefulSet
3metadata:
4 name: mysql
5spec:
6 serviceName: mysql
7 replicas: 1
8 selector:
9 matchLabels:
10 app: mysql
11 template:
12 metadata:
13 labels:
14 app: mysql
15 spec:
16 containers:
17 - name: mysql
18 image: mysql:8.0
19 ports:
20 - containerPort: 3306
21 env:
22 - name: MYSQL_ROOT_PASSWORD
23 valueFrom:
24 secretKeyRef:
25 name: mysql-secret
26 key: mysql-root-password
27 volumeMounts:
28 - name: mysql-data
29 mountPath: /var/lib/mysql
30 volumeClaimTemplates:
31 - metadata:
32 name: mysql-data
33 spec:
34 accessModes: ["ReadWriteOnce"]
35 resources:
36 requests:
37 storage: 20Gi
StatefulSet pods get predictable DNS names: mysql-0.mysql.default.svc.cluster.local.
Running a MySQL Client Pod
1# Run a temporary pod with the MySQL client
2kubectl run mysql-client --rm -it --image=mysql:8.0 -- \
3 mysql -h mysql-service -u root -ppassword123
4
5# Or use a one-liner to test connectivity
6kubectl run test-mysql --rm -it --image=mysql:8.0 -- \
7 mysql -h mysql-service -u root -ppassword123 -e "SHOW DATABASES;"
Common Pitfalls
No PersistentVolumeClaim: Without persistent storage, MySQL data is lost when the pod restarts. Always attach a PVC with ReadWriteOnce access mode for MySQL's data directory (/var/lib/mysql).
Using Deployment instead of StatefulSet: Deployments do not guarantee stable network identities or ordered startup/shutdown. For production MySQL, use a StatefulSet which provides stable pod names and persistent volume binding.
Exposing MySQL externally without security: Using LoadBalancer or NodePort exposes MySQL to the internet. Always restrict access with network policies, firewall rules, or VPN. Never expose root credentials on a public endpoint.
Hardcoding passwords in YAML: Store MySQL credentials in Kubernetes Secrets, not in plain text in Deployment manifests. Reference secrets with secretKeyRef in environment variables.
DNS resolution failures: If the application pod cannot resolve mysql-service, check that the Service exists in the same namespace or use the full DNS name. Run kubectl get svc to verify the Service is created and kubectl exec into the app pod to test DNS with nslookup mysql-service.
Summary
Deploy MySQL as a Deployment or StatefulSet with a PVC for persistent storage
Create a ClusterIP Service for internal cluster access
Connect from within the cluster using mysql-service:3306 (or full DNS name for cross-namespace)
Use kubectl port-forward for development access from outside the cluster
Use NodePort or LoadBalancer for external access (with proper security)
Store credentials in Kubernetes Secrets and reference them in pod environment variables