Extremely Serious

Category: Container

Running WordPress and MariaDB with Docker Compose

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.3 pins a recent stable MariaDB release, which helps keep behavior consistent and avoids surprises from latest. environment sets up the initial database: MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, and MYSQL_ROOT_PASSWORD are read by the entrypoint to create the schema and users on first run.
  • volumes: - db_data:/var/lib/mysql stores 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.sh is a script shipped in the MariaDB official Docker image specifically for health monitoring and supports multiple tests.
  • --connect verifies that a client can successfully connect to the server using the dedicated healthcheck user created by the image.
  • --innodb_initialized ensures that the InnoDB storage engine has finished initialization, preventing false positives during the initial database bootstrap.
  • start_period: 60s gives MariaDB extra time to initialize before failed checks count against the retries limit, 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-apache uses 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_healthy tells 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 at http://localhost:8080.
  • volumes: - wordpress_data:/var/www/html keeps 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_data holds the MariaDB data directory, which contains all databases defined by your instance.
  • wordpress_data holds 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:

  1. Create a directory (for example, wordpress-stack) and save the YAML above as docker-compose.yml in it.

  2. Edit the environment values for passwords and database names to suit your environment and security policies, ideally moving them into an .env file.

  3. From that directory, start the stack in detached mode:

    docker compose up -d
  4. Wait for the containers to reach a healthy state, then open:

    http://localhost:8080

    and 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.

Basic Docker Container Management

Run Command

Run a command in a new container

Display the help information using the following command:

docker container run --help

Example of creating a container for the swaggerapi/swagger-editor image with the name swagger-editor.

docker container run -it -p 80:8080 --name swagger-editor swaggerapi/swagger-editor

Since the -it options were used, you can stop the container using CTRL+C.

Access the swagger-editor using the following address:

http://localhost

This command can only be used for creating a container based on an image. Once the container was created you can start it using the start command. If you are not sure about the name of the container find it using the list command.

It is better to have a name for the container so that you know what to start (or stop or delete).

List Command

List containers.

Display the help information using the following command:

docker container ls --help

Example of displaying all the containers.

docker container ls --all

Start Command

Start one or more stopped containers

Display the help information using the following command:

docker container start --help

Example of starting the container created from the run command section.

docker container start swagger-editor

Stop Command

Stop one or more running containers

Display the help information using the following command:

docker container stop --help

Example of stopping the container created from the run command section.

docker container stop swagger-editor

Delete Command

Remove one or more containers

Display the help information using the following command:

docker container rm --help

Example of deleting the container created from the run command section.

docker container rm swagger-editor

Container Aware JVM Parameters for Heap Memory

JVM Parameters

Modify the java heap memory based on container memory using one of the following JVM parameters:

Parameter Description
-XX:InitialRAMPercentage The initial size of the heap based on the total container memory.
-XX:MinRAMPercentage The maximum heap size based on the size of the JVM running on small heap. The small is heap is of approximately 125MB.
-XX:MaxRAMPercentage The maximum heap size based on the size of the JVM running on greater than small heap.

Displaying the Current Value

Use the following command the display the current value of the container aware JVM Parameters using the following:

docker container run -it --rm openjdk:17.0.2-slim java -XX:+PrintFlagsFinal -version | grep -E ".*RAMPercentage"

Expect to see something like the following:

   double InitialRAMPercentage                     = 1.562500
                   {product} {default}
   double MaxRAMPercentage                         = 25.000000
                   {product} {default}
   double MinRAMPercentage                         = 50.000000
                   {product} {default}

Checking the Current Memory and CPU using JShell

  1. Open a jshell in the container using the following:

    docker container run -it --rm openjdk:17.0.2-slim jshell
  2. Paste the following command in jshell:

    var rt = Runtime.getRuntime();
    System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", 
     rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
  3. Press enter.

    Expect to see something similar to the following:

    rt ==> java.lang.Runtime@1d81eb93
    Heap size: 252MB
    Maximum size of heap: 3966MB
    Available processors: 16
    $2 ==> java.io.PrintStream@34c45dca
  4. Type the following in jshell and press enter.

    /exit

Allocating a Memory and CPU to a Container

  1. Execute the following command to allocate 100mb of memory and 1 CPU to the container:

    docker container run -it --rm -m 100m --cpus=1 openjdk:17.0.2-slim jshell
  2. Paste the following command in jshell:

    var rt = Runtime.getRuntime();
    System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", 
     rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
  3. Press enter.

    Expect to see something similar to the following:

    rt ==> java.lang.Runtime@6659c656
    Heap size: 7MB
    Maximum size of heap: 48MB
    Available processors: 1
    $2 ==> java.io.PrintStream@2d8e6db6

    Notice the following:

    Value
    Heap size 7MB
    Maximum size of heap 48MB
    Available processors 1
  4. Type the following in jshell and press enter.

    /exit

Modifying the Allocated Maximum Size of Heap Memory

  1. Execute the following command to allocate 100mb of memory and 1 CPU to the container:

    docker container run -it --rm -m 100m --cpus=1 openjdk:17.0.2-slim jshell -R-XX:MinRAMPercentage=80
  2. Paste the following command in jshell:

    var rt = Runtime.getRuntime();
    System.out.printf("Heap size: %dMB%nMaximum size of heap: %dMB%nAvailable processors: %d%n", 
     rt.totalMemory()/1024/1024, rt.maxMemory()/1024/1024, rt.availableProcessors());
  3. Press enter.

    Expect to see something similar to the following:

    rt ==> java.lang.Runtime@6659c656
    Heap size: 7MB
    Maximum size of heap: 77MB
    Available processors: 1
    $2 ==> java.io.PrintStream@2d8e6db6

    Notice the Maximum size of heap become 77MB. This is because of the JVM argument -XX:MinRAMPercentage=80 passed in jshell as:

    -R-XX:MinRAMPercentage=80

    We use the --XX:MinRAMPercentage=80 because the memory allocated is a small heap.

  4. Type the following in jshell and press enter.

    /exit