Running WordPress with Docker Compose gives you a reproducible, portable environment that is easy to spin up for local development or small deployments. In this article, we will build a minimal but robust setup that uses WordPress with MariaDB, including persistent storage and a proper health check using MariaDB’s built-in healthcheck.sh script.
Overview of the architecture
Our stack consists of two containers on the same Docker network: a MariaDB container as the database and a WordPress container running Apache and PHP 8.2. We add named volumes for persistence, environment variables for configuration, and a health check on MariaDB so WordPress waits for a fully initialized InnoDB engine before attempting to connect.
The docker-compose.yml
Below is the full docker-compose.yml that works with current MariaDB and WordPress images:
version: "3.9"
services:
db:
image: mariadb:11.3
container_name: wordpress-db
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress_password
MYSQL_ROOT_PASSWORD: root_password
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
wordpress:
image: wordpress:php8.2-apache
container_name: wordpress-app
depends_on:
db:
condition: service_healthy
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress_password
WORDPRESS_DB_NAME: wordpress
ports:
- "8080:80"
volumes:
- wordpress_data:/var/www/html
volumes:
db_data:
wordpress_data:
This configuration keeps the database and WordPress state in named volumes and wires WordPress to wait for a healthy MariaDB instance using the official health check script.
Service: MariaDB (db)
The db service defines the MariaDB database container and uses the official MariaDB image with its integrated health check script.
Key aspects:
image: mariadb:11.3pins a recent stable MariaDB release, which helps keep behavior consistent and avoids surprises fromlatest.environmentsets up the initial database:MYSQL_DATABASE,MYSQL_USER,MYSQL_PASSWORD, andMYSQL_ROOT_PASSWORDare read by the entrypoint to create the schema and users on first run.volumes: - db_data:/var/lib/mysqlstores the MariaDB data directory in a named volume so your data persists across container recreations and upgrades.
The health check is where this service becomes more robust:
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
healthcheck.shis a script shipped in the MariaDB official Docker image specifically for health monitoring and supports multiple tests.--connectverifies that a client can successfully connect to the server using the dedicated healthcheck user created by the image.--innodb_initializedensures that the InnoDB storage engine has finished initialization, preventing false positives during the initial database bootstrap.start_period: 60sgives MariaDB extra time to initialize before failed checks count against theretrieslimit, which is useful for first startup on larger or slower disks.
This approach is more accurate than using mysqladmin ping, which can report success even while initialization or upgrade routines are still running.
Service: WordPress (wordpress)
The wordpress service defines the application container that runs WordPress on Apache with PHP 8.2.
Important points:
image: wordpress:php8.2-apacheuses the official WordPress image variant that bundles Apache and PHP 8.2, giving you a maintained, up-to-date runtime.depends_on: db: condition: service_healthytells Docker Compose not just to start containers in order, but to wait until MariaDB’s health check passes, which avoids race conditions at startup.- The database environment variables (
WORDPRESS_DB_HOST,WORDPRESS_DB_USER,WORDPRESS_DB_PASSWORD,WORDPRESS_DB_NAME) mirror the MariaDB configuration and instruct WordPress how to connect to the database over the internal Docker network. ports: "8080:80"publishes WordPress on port 8080 of your host so you can access it athttp://localhost:8080.volumes: - wordpress_data:/var/www/htmlkeeps the WordPress core, plugins, themes, and uploads persistent in a named volume, which protects content and configuration from container lifecycle changes.
This container design keeps WordPress stateless from the perspective of the image and pushes state into Docker-managed volumes, which aligns well with container best practices.
Volumes: persistence layer
The volumes section declares two named volumes used by the services:
volumes:
db_data:
wordpress_data:
db_dataholds the MariaDB data directory, which contains all databases defined by your instance.wordpress_dataholds the WordPress application files and uploaded media, including themes and plugins.
Named volumes are managed by Docker and are not removed by a plain docker compose down, which means your data survives service restarts and configuration tweaks. If you explicitly run docker compose down --volumes, Docker will remove these volumes and you will lose the database and WordPress content.
Running and managing the stack
To bring this stack up:
-
Create a directory (for example,
wordpress-stack) and save the YAML above asdocker-compose.ymlin it. -
Edit the environment values for passwords and database names to suit your environment and security policies, ideally moving them into an
.envfile. -
From that directory, start the stack in detached mode:
docker compose up -d -
Wait for the containers to reach a healthy state, then open:
http://localhost:8080and complete the WordPress installation wizard.
To stop the stack while preserving data:
docker compose down
To completely tear down including volumes and stored data:
docker compose down --volumes
This workflow gives you a repeatable, self-contained WordPress + MariaDB environment that starts reliably, thanks to the health-checked database service.
Leave a Reply