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 versionshould 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/wpCreate 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-tooWrite 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 psAdd 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
- Open
https://example.comand complete the WordPress installer. - Install the Redis Object Cache plugin and click Enable Object Cache.
- Raise upload limits if needed by mounting an
uploads.iniinto/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.gzArchive 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 -dTake 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
.envchanged 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.
