Migrating WordPress from Bitnami Helm Charts to Official Images

This is a continuation of my previous post about migrating from the Bitnami Postgres image. In that post I modified the helm values for a Nextcloud instance to point to a freshly deployed Postgres database.
This post will explain how I moved away from the Bitnami Helm chart for WordPress and went with official images and straight manifests defining all of the infrastructure. Like with my Postgres database for nextcloud, the reason for migrating away from the Bitnami images was Bitnami's recent shift to a paid model.
Here's a step by step guide on how I did the migration and defined my Kubernetes infrastructure.
Prerequisites
- Ability to execute into pods with
kubectl
- WordPress and MariaDB instances from the Bitnami Helm chart running in the
wordpress
namespace (you can modify commands to match your namespace if it's different) - Once again a backup of all WordPress data before starting would be nice to also have
- Secrets containing the credentials for MariaDB and Wordpress. These can be created with:
kubectl create secret generic wordpress-mariadb-secrets \
--from-literal=MARIADB_ROOT_PASSWORD='MyRootPassword' \
--from-literal=MARIADB_REPLICATION_PASSWORD='MyReplicationPassword' \
--from-literal=MARIADB_PASSWORD='MyUserPassword' \
-n wordpress-new
kubectl create secret generic wordpress-secrets \
--from-literal=WORDPRESS_DB_PASSWORD='MyUserPassword' \
-n wordpress-new
My original WordPress was deployed into the wordpress
namespace. Due to pod naming conventions I decided to create a new namespace called wordpress-new
, and after everything was migrated and the old resources removed, I moved the new resources from wordpress-new
to wordpress
Step 1: Deploy MariaDB
First I created a simple StatefulSet in the wordpress-new
namespace using this manifest:
---
## MariaDB StatefulSet for WordPress
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: wordpress-mariadb
spec:
selector:
matchLabels:
app: wordpress-mariadb
serviceName: "wordpress-mariadb-svc"
replicas: 1
minReadySeconds: 10
template:
metadata:
labels:
app: wordpress-mariadb
spec:
containers:
- name: mariadb
image: mariadb:11.8 # Match whatever version you want
envFrom:
- secretRef:
name: wordpress-mariadb-secrets
ports:
- containerPort: 3306
name: tcp-mariadb
volumeMounts:
- name: wordpress-mariadb-data
mountPath: /var/lib/mysql
volumes:
- name: wordpress-mariadb-data
persistentVolumeClaim:
claimName: wordpress-mariadb-data
---
# MariaDB service
apiVersion: v1
kind: Service
metadata:
name: wordpress-mariadb-svc
labels:
app: wordpress-mariadb
spec:
ports:
- port: 3306
name: tcp-mariadb
selector:
app: wordpress-mariadb
Storage is handled with a previously created PVC in the same namespace named wordpress-mariadb-data
.
The ReclaimPolicy for this PVC will need to be set to Retain
if you intend to move it to a new namespace
Once that's up and running we're ready for step 2:
Step 2: Prepare MariaDB for Migration
For this migration I'm going to be shelling into the pods and performing commands directly. It's not the best approach, but in this case I wanted ease and simplicity.
Shell into the Bitnami MariaDB pod inside the wordpress
namespace
# Find out the name of the pod. It'll likely be wordpress-mariadb-0
kubectl get pods -n wordpress
# Shell into the pod
kubectl -n wordpress exec -it <wordpress-mariadb-pod> -- bash
Dump the MariaDB database inside the pod
mysqldump -u root -p bitnami_wordpress > /tmp/backup.sql
Enter the password for the database and it should dump without any problems.
Copy the dumped database to local machine
Exit out of the pod and copy the database
# don't forget to exit the pod
exit
# copy the database
kubectl cp wordpress/<wordpress-mariadb-pod>:/tmp/backup.sql ./backup.sql
Copy the dumped database to the new mariadb pod in the wordpress-new
namespace
# Get the name of the new mariadb pod
kubectl get pods -n wordpress-new
# copy the database
kubectl cp ./backup.sql wordpress-new/<new-wordpress-mariadb-pod>:/tmp/backup.sql
Shell into the new MariaDB pod and login to MariaDB
# Shell into the new MariaDB pod
kubectl -n wordpress-new exec -it <new-wordpress-mariadb-pod> bash
# login to mariadb
mariadb -u root -p
It will prompt you for the root password you set with MARIADB_ROOT_PASSWORD
Create the new Database
# Create the WordPress DB, USER, and grant privileges to new user
CREATE DATABASE wordpress;
CREATE USER 'wordpress'@'%' IDENTIFIED BY 'MySuperSecurePassword';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'%';
FLUSH PRIVILEGES;
Import the dumped database and exit out of the pod
# import the database
mariadb -u root -p wordpress < /tmp/backup.sql
# get out of the pod
exit
The database is now good to go and we can get WordPress up and running
Step 3: Deploy WordPress in the wordpress-new
namespace
Here is a simple manifest I used for the WordPress deployment. It includes the deployment, service, and an ingress using my internal ingressclass.
---
## WordPress Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
replicas: 1
selector:
matchLabels:
app: wordpress
template:
metadata:
labels:
app: wordpress
spec:
containers:
- name: wordpress
image: wordpress:6.8.2-apache # Use whichever image version you want
imagePullPolicy: IfNotPresent
env:
# DB ENV
- name: WORDPRESS_DB_HOST
value: wordpress-mariadb-svc:3306
- name: WORDPRESS_DB_NAME
value: wordpress
- name: WORDPRESS_DB_USER
value: wordpress
# Pull all other env from external secret (DB Password)
envFrom:
- secretRef:
name: wordpress-secrets
ports:
- containerPort: 80
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 6
httpGet:
path: /wp-login.php
port: http
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
volumeMounts:
- mountPath: /var/www/html/wp-content
name: wordpress-data
volumes:
- name: wordpress-data
persistentVolumeClaim:
claimName: wordpress-wordpress-data
---
# WordPress service
apiVersion: v1
kind: Service
metadata:
name: wordpress-wordpress-svc
spec:
selector:
app: wordpress
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
---
# WordPress Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wordpress
spec:
ingressClassName: internal
tls:
- hosts:
- wordpress.pawked.com
rules:
- host: wordpress.pawked.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wordpress-wordpress-svc
port:
number: 80
Again, storage is handled with a previously created PVC in the same namespace named wordpress-wordpress-data
.
The ReclaimPolicy for this PVC will need to be set to Retain
if you intend to move it to a new namespace
Step 4: Import Uploaded Content from old instance
Copy the uploads from the old pod
We'll need to get the name of the old WordPress pod first
# Get the name of the old WordPress pod
kubectl get pods -n wordpress
# copy the uploads folder to the local machine
kubectl cp wordpress/<old-wordpress-pod>:/bitnami/wordpress/wp-content/uploads ./uploads
Copy the uploads into the new pod
We need to get the new pod name as well
# Get the name of the new WordPress pod
kubectl get pods -n wordpress-new
# copy the uploads folder to the new WordPress pod
kubectl cp ./uploads/. wordpress-new/<new-wordpress-pod>:/var/www/html/wp-content/uploads/
Set the permissions on the uploads content
kubectl exec -n wordpress-new <new wordpress-pod> -- chown -R www-data:www-data /var/www/html/wp-content/uploads
If you edited any themes or plugins in your original instance it might be better to copy the entire /bitnami/wordpress/wp-content/
(minus the wp-config.php) using the same commands.
Conclusion
And now you should be able to navigate to your WordPress instance and be ready to go. If you want you can change the namespace from wordpress-new
to something else. I went with wordpress
because I'm creative.
This migration was slightly more difficult than the Postgres one I did last week because of needing to shell in to the pods and creating the database, as well as moving the uploaded content over, but it was still fairly simple and worth doing to get away from the Bitnami images.