Docker & DevOps

Run WordPress with Docker Compose: WordPress, MariaDB and Redis

Build a WordPress stack with Docker Compose: MariaDB, Redis object cache, persistent volumes, environment secrets, a reverse proxy with SSL and simple backups.

IntermediateLinux VPS3 min read

Running WordPress with Docker Compose keeps the whole stack, web server, PHP, database and cache, in one file you can version, copy to another server and rebuild in minutes. This guide builds a production-minded stack with MariaDB and Redis on a Linux VPS.

Before you start

  • A Linux VPS with Docker Engine and the Compose plugin (docker compose version should work).
  • A domain pointing to the server.
  • Ports 80 and 443 free, or an existing reverse proxy.

Create the project folder and secrets

mkdir -p /opt/wp && cd /opt/wp

Create a .env file next to the compose file (and chmod 600 .env):

DB_NAME=wordpress
DB_USER=wpuser
DB_PASSWORD=change-me-long-random
DB_ROOT_PASSWORD=change-me-too

Write docker-compose.yml

services:
  db:
    image: mariadb:11.4
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: ${DB_NAME}
      MARIADB_USER: ${DB_USER}
      MARIADB_PASSWORD: ${DB_PASSWORD}
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru

  wordpress:
    image: wordpress:6-php8.3-apache
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: ${DB_NAME}
      WORDPRESS_DB_USER: ${DB_USER}
      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
      WORDPRESS_CONFIG_EXTRA: |
        define('WP_REDIS_HOST', 'redis');
    volumes:
      - wp_data:/var/www/html
    ports:
      - "127.0.0.1:8080:80"

volumes:
  db_data:
  wp_data:

Key points: the database and Redis have no published ports, so only the WordPress container can reach them over Compose's internal network. WordPress listens only on localhost port 8080, for a reverse proxy to serve over HTTPS. Named volumes keep data when containers are recreated.

Start the stack:

docker compose up -d && docker compose ps

Add HTTPS with a reverse proxy

The simplest approach is Nginx on the host with Certbot, proxying to 127.0.0.1:8080 with these headers:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

So WordPress knows it is served over HTTPS (avoiding redirect loops), add to WORDPRESS_CONFIG_EXTRA:

if (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https') { $_SERVER['HTTPS'] = 'on'; }

Alternatives such as Caddy or Traefik as another container obtain certificates automatically.

Finish the install

  1. Open https://example.com and complete the WordPress installer.
  2. Install the Redis Object Cache plugin and click Enable Object Cache.
  3. Raise upload limits if needed by mounting an uploads.ini into /usr/local/etc/php/conf.d/.

Back up the stack

Dump the database from the running container:

docker compose exec -T db sh -c 'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" --single-transaction "$MARIADB_DATABASE"' | gzip > /backup/wp-db-$(date +%F).sql.gz

Archive the WordPress files volume:

docker run --rm -v wp_wp_data:/data -v /backup:/backup alpine tar -czf /backup/wp-files-$(date +%F).tar.gz -C /data .

Volume names get the project folder as a prefix (wp_ here); list them with docker volume ls.

Update safely

docker compose pull && docker compose up -d

Take a backup first, and pin major versions (as above) so an update never jumps to a new MariaDB major unexpectedly.

Troubleshooting

  • Error establishing a database connection: the DB container is not healthy yet or the credentials in .env changed after the volume was created (they only apply on first start).
  • Too many redirects: the HTTPS detection snippet is missing or the proxy does not send X-Forwarded-Proto.
  • Uploads fail with permission errors: files in the volume must be owned by www-data (UID 33) inside the container.

WordPress Docker FAQ

Is Docker good for WordPress in production?

Yes, if you handle backups, updates and HTTPS as shown. For a single site without container experience, classic hosting is simpler.

Should I use the Apache or FPM image?

The Apache image is self-contained and easiest. The FPM image needs a separate Nginx container but gives more control.

Can I move this stack to another server?

Yes: copy the folder, restore the database dump and files volume, and run docker compose up -d.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.