Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
VGSOURCES
Get listed

Container deploymentPATCH 2026.10.15

How to Run WordPress in Docker Containers and Kubernetes Clusters

By VGSources Team 13 min read

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run WordPress on one machine with Docker Compose: place WordPress and MariaDB or MySQL in separate services, mount named volumes at /var/www/html and /var/lib/mysql, supply database settings through environment variables or secret files, and start the stack with docker compose up -d. The containers can be replaced; the volumes hold your site.

Move to Kubernetes only when you need cluster scheduling, persistent-volume provisioning, ingress, or horizontal web capacity. A cluster does not automatically make WordPress highly available. You must design storage for uploads, database backup and failover, media delivery, secrets, TLS, and recovery.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the deployment that matches your problem

Docker Compose is the practical default for local development, testing, a small site, or a single server. It gives you a repeatable two-service application without requiring a control plane. Kubernetes is an orchestration platform for multiple nodes: it can reschedule Pods, expose Services, attach PersistentVolumes, and coordinate ingress and certificates, but each of those capabilities needs an explicit configuration.

Concern Docker Compose Kubernetes
Operational scope One Docker host and its local storage A cluster of nodes with a scheduler and control plane
State storage Named volumes such as wp_data and db_data PersistentVolumeClaims backed by a storage class, managed disk, or another provisioner
Web exposure Host port mapping, reverse proxy, or load balancer outside Compose Service plus Ingress or a cloud load balancer
Rescheduling Containers restart on the same host unless you operate another host yourself Pods can be restarted or moved; storage must be attachable where the Pod runs
Secrets Compose secrets and secret files, kept out of ordinary configuration Kubernetes Secrets or an external secret manager
Scaling Manual scaling on the host; shared storage and routing are your responsibility Deployments, Services, and autoscaling can add web Pods, but database and media architecture still require separate work
Recovery Back up the host volumes and verify a restore Back up PVC data, database state, cluster configuration, and any external media store

Use the smallest platform that meets the requirement. A second WordPress container is not a substitute for shared uploads, a consistent database, or a tested failover path.

What must persist when a container disappears

A container’s writable layer is disposable. WordPress needs two different kinds of state:

  • Database state: posts, pages, users, settings, comments, plugin data, and metadata in MariaDB or MySQL.
  • Filesystem state: the WordPress installation, themes, plugins, and especially uploaded media under wp-content/uploads.

Mount database storage at /var/lib/mysql and WordPress storage at /var/www/html (or at a deliberately selected subdirectory). If a Pod or container is deleted without those mounts, the application may start as a new installation even though the image is unchanged.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prerequisites for a Compose deployment

  • Docker Engine with the Compose v2 plugin, so the command is docker compose.
  • A directory reserved for this stack and enough disk space for the database, core files, plugins, themes, and uploads.
  • A tested image tag policy. Use a specific WordPress and database tag in production rather than silently pulling a moving latest tag; the example uses latest only to keep the demonstration independent of a particular release.
  • A plan for HTTPS and DNS. Compose publishes a port; it does not by itself issue certificates or terminate TLS.

Build a two-service WordPress stack with Docker Compose

1. Create the project and secret files

Create a directory such as wordpress-stack, then create a secrets subdirectory. Put a long, unique database password in secrets/db_password.txt and a different root password in secrets/db_root_password.txt. Restrict those files to the account that runs Docker and keep them out of version control.

The Compose model separates services, networks, volumes, configs, and secrets. The database and WordPress services below share only an internal application network; the database has no published host port.

2. Define the services and named volumes

services:
  db:
    image: mariadb:latest
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
    volumes:
      - db_data:/var/lib/mysql
    secrets:
      - db_password
      - db_root_password
    networks:
      - backend

  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    depends_on:
      - db
    ports:
      - '8080:80'
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - wp_data:/var/www/html
    secrets:
      - db_password
    networks:
      - backend

volumes:
  db_data:
  wp_data:

networks:
  backend:
    driver: bridge

secrets:
  db_password:
    file: ./secrets/db_password.txt
  db_root_password:
    file: ./secrets/db_root_password.txt

The official WordPress image supports _FILE variants for sensitive settings. That lets the container read a mounted secret instead of placing a password directly in the Compose file. The same pattern is used for the MariaDB credentials.

3. Validate and start the stack

  1. From the project directory, run docker compose config. Fix indentation, missing secret files, or invalid variable expansions before starting.
  2. Start both services in the background with docker compose up -d.
  3. Check status with docker compose ps. A healthy result shows both containers running; an initialization delay for the database is normal on the first start.
  4. Inspect startup output with docker compose logs -f db wordpress. Stop following logs with Ctrl-C; that does not stop the containers.
  5. Open http://localhost:8080 and complete the WordPress installer. On a remote host, use its DNS name and put an HTTPS reverse proxy in front of the published port.

depends_on controls startup order, not database readiness. If WordPress starts before MariaDB is accepting connections, the container normally retries or can be restarted; verify the logs rather than assuming that a running process means the application is ready.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

4. Stop, remove, and preserve data correctly

Run docker compose down when you want to remove the containers and the default network while retaining the named volumes. The database and WordPress files remain available for the next docker compose up -d.

Do not use docker compose down --volumes unless you intentionally want to erase the named volumes and start over. That command removes the stored database and WordPress data along with the containers. Treat it as a destructive reset, not as ordinary shutdown.

Secure and maintain the Compose installation

Pin images and rebuild regularly

Choose a WordPress tag and database tag that you have tested together, record them in version control, and update them deliberately. Rebuild or pull the images on a schedule so WordPress core, PHP, the database engine, and operating-system packages receive security fixes. Test updates against a copy of the volumes before changing the live stack.

Reduce writable paths when the site is managed centrally

The official image documents a read-only deployment pattern. A hardened service can set read_only: true, provide temporary writable filesystems for /tmp and /run, and mount a persistent volume only where runtime writes are required, commonly wp-content/uploads. This prevents web requests from modifying core files, but it also disables normal dashboard-based plugin, theme, and core updates. Perform those changes by building and deploying a new image, and test the exact image with your plugins before enabling the restriction.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  wordpress:
    read_only: true
    tmpfs:
      - /tmp
      - /run
    volumes:
      - wp_uploads:/var/www/html/wp-content/uploads

A read-only design is not a drop-in addition to every existing Compose file. Confirm that the image, PHP runtime, cache directories, and plugins do not require another writable path; add only narrowly scoped mounts for paths you have verified.

Back up before changing anything

A volume is persistence, not a backup. Back up the database with a consistent database dump and back up the WordPress files, including uploads and any custom plugins or themes. Store copies outside the Docker host, retain more than one recovery point, and perform a restore into a separate stack. A backup that has never been restored is an assumption, not a recovery plan.

Move the application to Kubernetes

Kubernetes changes the failure model: a Pod can be evicted, restarted, rescheduled to another node, or deleted during an upgrade. A PersistentVolume (PV) represents storage, and a PersistentVolumeClaim (PVC) requests that storage for a workload. Kubernetes documentation describes PVs and PVCs as surviving Pod restart, rescheduling, and deletion, provided the underlying storage and reclaim policy are configured correctly.

Kubernetes’ instructional WordPress/MySQL example is explicitly educational. It warns that the example “is not suitable for production use cases, as it uses single instance WordPress and MySQL Pods.” Treat it as a way to learn object relationships, not as a high-availability design.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The minimum Kubernetes object set

  • A database workload and a PVC for its data directory.
  • A WordPress Deployment and a PVC for the application files, or a read-only image with a separate persistent media mount.
  • A ClusterIP Service for database traffic and a Service for WordPress traffic.
  • Secrets for database credentials, preferably synchronized from an external secret manager.
  • An Ingress and TLS certificate resources when the site must be reachable by a hostname.

Create credentials without putting them in the Deployment

Set shell variables from a protected secret store, then create a Kubernetes Secret. The command below avoids writing literal passwords into a manifest; your shell history and CI logs must still be protected.

kubectl create secret generic wp-db 
  --from-literal=username=wordpress 
  --from-literal=password=$WP_DB_PASSWORD 
  --from-literal=root-password=$MYSQL_ROOT_PASSWORD

Use the same key names in the database and WordPress manifests. In a team or production cluster, use the platform’s approved external-secret workflow so credentials can be rotated without committing them to Git.

Provision claims before deploying workloads

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-files
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

The capacity values are examples, not sizing guidance. Select a storage class and capacity based on the database, media library, retention policy, and growth rate. A claim can remain Pending when the cluster has no matching provisioner or available volume; inspect it with kubectl describe pvc mysql-data.

Deploy MariaDB and expose it internally

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mariadb:latest
          env:
            - name: MYSQL_DATABASE
              value: wordpress
            - name: MYSQL_USER
              valueFrom:
                secretKeyRef:
                  name: wp-db
                  key: username
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: wp-db
                  key: password
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: wp-db
                  key: root-password
          volumeMounts:
            - name: mysql-data
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-data
          persistentVolumeClaim:
            claimName: mysql-data
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  selector:
    app: mysql
  ports:
    - port: 3306
      targetPort: 3306

The Service name mysql becomes the DNS name used by WordPress. Keep this database Service internal; exposing port 3306 to the internet creates an unnecessary attack surface.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Deploy WordPress and its Service

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:latest
          env:
            - name: WORDPRESS_DB_HOST
              value: mysql:3306
            - name: WORDPRESS_DB_NAME
              value: wordpress
            - name: WORDPRESS_DB_USER
              valueFrom:
                secretKeyRef:
                  name: wp-db
                  key: username
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: wp-db
                  key: password
          ports:
            - containerPort: 80
          volumeMounts:
            - name: wordpress-files
              mountPath: /var/www/html
      volumes:
        - name: wordpress-files
          persistentVolumeClaim:
            claimName: wordpress-files
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  selector:
    app: wordpress
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP

Apply the manifests with kubectl apply -f and then check kubectl get pods,pvc,svc. If a Pod is Pending, inspect scheduling and storage events with kubectl describe pod. If it is restarting, read kubectl logs and verify the Secret keys, database hostname, and mounted claim.

For a temporary non-production check, use kubectl port-forward service/wordpress 8080:80 and open http://localhost:8080. For a real hostname, keep the Service internal and configure an Ingress controller, DNS, and a certificate issuer.

Storage choices and the node-affinity trap

Cloud or enterprise clusters commonly use a CSI-backed storage class that can attach a disk to the node running a Pod. Confirm its access mode: ReadWriteOnce generally permits one node to mount a volume, while a multi-reader requirement may need an ReadWriteMany filesystem or a different design.

Local volumes are tied to a particular node. Kubernetes requires node affinity so the scheduler places the Pod where that disk exists. Local storage can be useful for learning or a constrained single-node installation, but it does not become portable merely because it is referenced by a PVC. Losing the node can make the workload unavailable until that storage is recovered.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the database’s durable files separate from WordPress uploads. A storage class that is acceptable for media may not provide the latency, durability, snapshots, or locking behavior required by a transactional database.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Why adding WordPress replicas is not high availability

Setting replicas: 2 creates two web Pods, but it does not solve the shared-state problems:

  • Both Pods must see the same uploads. Otherwise an image written to one Pod can return a 404 when a request reaches the other.
  • Both Pods must use one authoritative database, with backups and a failover plan. Running two independent database Pods against separate disks creates divergent sites.
  • Plugins that write local caches or generated files need a shared, disposable cache strategy or a configuration that writes to object storage.
  • A Service can load-balance requests, but it does not repair inconsistent files or promote a failed database.

A scalable design commonly keeps WordPress core, themes, and plugins in an immutable image, stores media in an object-storage service or a proven shared filesystem, runs the database on a managed or deliberately replicated platform, and adds Redis object caching where the application and plugin stack support it. These are separate architecture decisions, not automatic results of deploying a container.

Autoscale only after the bottleneck is understood

Horizontal Pod Autoscaling can add web capacity from CPU, memory, or application metrics, but it cannot make a single-writer database scale safely. Establish resource requests and limits, measure PHP and database saturation, and confirm that media and sessions behave correctly before enabling autoscaling. A sudden increase in web Pods can otherwise increase database contention without improving response time.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Ingress, TLS, and operational visibility

Expose WordPress through an Ingress or a managed load balancer, redirect HTTP to HTTPS, and renew certificates automatically. Set the external URL consistently so WordPress does not generate mixed-content links after the move. Do not expose the database Service.

Monitor at three layers:

  • Cluster: Pod restarts, scheduling failures, node pressure, PVC attachment errors, and available capacity.
  • Web runtime: HTTP error rate, latency, PHP worker saturation, and ingress responses.
  • Data: database connections, replication or failover health where applicable, disk growth, backup completion, and restore-test results.

Centralize logs and retain enough history to correlate an application error with a Pod replacement, storage event, or database restart. Add readiness and liveness probes only after defining what “ready” means for your image; a probe that checks only that a process exists can send traffic to a site whose database is unavailable.

Upgrade and rollback procedure

  1. Record the current image digests, manifests, plugin and theme versions, and database backup point.
  2. Restore a recent backup into an isolated environment and test the candidate WordPress and database images there.
  3. Build or select the new immutable WordPress image, keeping the old image available for rollback.
  4. Apply the Deployment change and watch rollout status with kubectl rollout status deployment/wordpress, or use docker compose up -d after changing the pinned tag in Compose.
  5. Check application logs, login, media uploads, scheduled jobs, and critical plugin functions.
  6. If the new web image is faulty, use kubectl rollout undo deployment/wordpress or restore the previous Compose tag. Do not roll back a database schema blindly; database migrations may require a tested reverse procedure or restoration from backup.

Failure scenarios and fixes

Symptom Likely cause What to check
WordPress shows the installation screen after a restart The database volume was not mounted, was deleted, or the container points at a new database Inspect volume mounts, docker compose ps or kubectl get pvc, and the database hostname and credentials
Uploads disappear when a Pod changes Uploads were written to a container layer or node-local path Verify the mount at wp-content/uploads and choose shared or external media storage
PVC remains Pending No compatible storage class, capacity, access mode, or topology is available Run kubectl describe pvc and inspect storage-provisioner events
Pods cannot start on another node The volume is local or restricted to one attachment Review PV node affinity and the storage class access mode; plan a portable backend
WordPress cannot connect to MySQL Wrong Service name, Secret key, port, or database initialization state Read both containers’ logs and test DNS and credentials from inside the WordPress Pod
Dashboard updates fail after hardening The image or filesystem is read-only by design Publish a new image containing the tested update, or provide only the narrowly required writable mount
Two web Pods return different media Each Pod has private local files Use object storage or a shared filesystem before increasing replicas

A practical progression from laptop to cluster

  1. Development: Use Compose with WordPress, MariaDB or MySQL, named volumes, an internal network, and a local port mapping.
  2. Hardened image: Pin tested tags, use secret files, minimize writable paths, and rebuild for security updates.
  3. Initial Kubernetes deployment: Create a WordPress Deployment, a database workload, Services, and PVCs. Keep it single-instance while learning and label it non-production.
  4. Production cluster: Select portable or managed storage, establish database backup and availability, externalize media when appropriate, add ingress and TLS, instrument the stack, and rehearse recovery.

This progression keeps platform complexity proportional to the requirement. Compose solves packaging and repeatability on one host; Kubernetes solves scheduling and service coordination across nodes, while persistence, database reliability, media delivery, and recovery remain deliberate engineering work.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Container deploymentDockerDocker ComposeKubernetesMariaDBWordPress

Leave a reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.