Docker is useful for WordPress in two scenarios: local development (where it shines) and production deployment (where it adds complexity but pays off for teams). Here is a practical setup for each.
The basic Docker setup for WordPress local development
Create a docker-compose.yml in your project:
services:
wordpress:
image: wordpress:php8.2-apache
ports:
- "8080:80"
volumes:
- ./wp-content:/var/www/html/wp-content
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_NAME: wp_local
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: strong_password_here
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: wp_local
MYSQL_USER: wp_user
MYSQL_PASSWORD: strong_password_here
MYSQL_ROOT_PASSWORD: root_password_here
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mysql_data:
Create uploads.ini for upload limits:
file_uploads = On
upload_max_filesize = 256M
post_max_size = 256M
memory_limit = 512M
Start it:
docker compose up -d
docker compose logs -f wordpress
Visit http://localhost:8080 to run the WordPress installer.
Why mount wp-content as a volume
The key line is:
- ./wp-content:/var/www/html/wp-content
Without this, plugins and themes installed in the container disappear on the next docker compose up. Mounting the local directory means:
- Plugins installed via the WordPress admin survive restarts
- You can use your local editor and Git
- The container stays stateless
Using WP-CLI in a Docker container
WP-CLI is pre-installed in the official WordPress Docker image. Run it with:
docker compose exec wordpress wp plugin list
docker compose exec wordpress wp theme list
docker compose exec wordpress wp db export backup.sql
If you need to run a WP-CLI command that takes a long time (plugin updates, database imports), increase the PHP execution time in uploads.ini or pass --allow-root if WP-CLI requires it.
Database access from the host
For Sequel Ace, TablePlus, or MySQL Workbench:
# Get the container's internal IP
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' your_project_mysql_1
Or add a port mapping to the compose file:
mysql:
image: mysql:8.0
ports:
- "3307:3306"
# ...
Then connect to localhost:3307 from your host database client.
Production Docker: the differences
Development Docker and production Docker are different problems. In production:
- You should not use the official WordPress image directly
- You need a custom
Dockerfileto install your specific plugins and theme during the image build - Database should not be in a container (use a managed database or at minimum an external MySQL instance)
- You need health checks, restart policies, and log management
A minimal production Dockerfile:
FROM wordpress:php8.2-apache
# Install custom plugins and theme at build time
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN composer require wpackagist-plugin/wordfence wpackagist-plugin/wp-super-cache
# Production configuration
RUN a2enmod ssl headers rewrite
docker-compose for staging
A staging setup with Nginx, PHP-FPM, and Redis:
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./wp-content:/var/www/html/wp-content
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
php:
image: wordpress:php8.2-fpm-alpine
volumes:
- ./wp-content:/var/www/html/wp-content
- ./php.ini:/usr/local/etc/php/conf.d/custom.ini:ro
environment:
WORDPRESS_DB_HOST: ${DB_HOST}
WORDPRESS_DB_NAME: ${DB_NAME}
WORDPRESS_DB_USER: ${DB_USER}
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
depends_on:
- redis
redis:
image: redis:alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
Common Docker + WordPress problems
Permissions errors
The Apache process runs as www-data. If your local files are owned by your user, the container cannot write to them.
Fix:
# Change ownership to match the container
sudo chown -R 33:33 ./wp-content
Or run with a named user that matches your UID:
user: "${UID}:33"
Slow performance on macOS
Docker on macOS uses a virtualised Linux kernel, making file I/O significantly slower than on Linux. For large WordPress sites, this matters.
Fixes:
- Use Docker Desktop with VirtioFS (faster than the older gRPC-FUSE backend)
- Move database and cache inside containers (not mounted volumes)
- Use
docker compose upwithout-dduring active development if performance is critical
Container IP changes after restart
Use service names (like mysql, redis) as hostnames — Docker’s internal DNS resolves these to the correct container IPs, regardless of IP changes.
Database container filling up
The MySQL container’s data lives in a Docker volume. Monitor it:
docker volume inspect your_project_mysql_data
du -sh /var/lib/docker/volumes/your_project_mysql_data/_data
Periodically clean up old images and stopped containers:
docker system prune -f