WordPress Docker Production Deployment Pitfalls Complete Guide
Why Docker Instead of宝塔 or Lnmp
WordPress hosting in 2026 isn't just "install a panel" anymore. I used 宝塔, Lnmp, and manual compilation, and eventually switched to Docker for one reason: environment consistency.
When you have 3 servers and need to migrate WordPress on all of them, 宝塔's version differences will drive you crazy. With docker-compose up -d on one file, all 3 machines run identical environments.
But deploying WordPress on Docker has a unique set of pitfalls. This article covers 7 I actually hit.
My docker-compose.yml Configuration (Latest 2026)
Here's the complete configuration I tested working in 2026, including Nginx+PHP-FPM+MySQL+Redis:
version: '3.8'
services:
nginx:
image: nginx:1.25-alpine
container_name: wp_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./wordpress:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
restart: unless-stopped
php:
image: php:8.3-fpm-alpine
container_name: wp_php
volumes:
- ./wordpress:/var/www/html
depends_on:
- db
environment:
- WORDPRESS_DB_HOST=db
- WORDPRESS_DB_NAME=wordpress
- WORDPRESS_DB_USER=wp_user
- WORDPRESS_DB_PASSWORD=your_strong_password
restart: unless-stopped
db:
image: mysql:8.0
container_name: wp_mysql
volumes:
- db_data:/var/lib/mysql
environment:
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wp_user
- MYSQL_PASSWORD=your_strong_password
- MYSQL_ROOT_PASSWORD=root_strong_password
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: wp_redis
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
db_data:
redis_data:
Key Version Confirmations (May 2026):
- Nginx: 1.25 (alpine slim variant)
- PHP: 8.3-fpm (WordPress official minimum PHP 7.4, recommended 8.0+)
- MySQL: 8.0 (WordPress 6.5+ recommended)
- Redis: 7 (for object caching)
Pitfall 1: UID 1000 Permission Problem—The Root Cause of Plugin Update Failures
Symptom: WordPress admin shows "Unable to write files," plugin updates fail, media upload errors.
Cause: Docker containers run as www-data (UID 33) or custom users, while host-mounted volumes belong to your user account (UID 1000). The UID mismatch on both sides causes permission failures.
Solution: Unify PHP container user UID in docker-compose.yml:
php:
image: php:8.3-fpm-alpine
user: "1000:1000" # matches host user
volumes:
- ./wordpress:/var/www/html
But this creates a new problem—if the host runs containers as root, UID 1000 may not exist. A better approach uses PGID 1000:
php:
image: php:8.3-fpm-alpine
user: "1000:82" # 1000=your user, 82=www-data group
**Verification method**: Run ls -ln wordpress/ on host to confirm file ownership, then check inside container:
docker exec -it wp_php sh -c "id"
# should display uid=1000 gid=82
Pitfall 2: Nginx Cannot Read WordPress Files—Volume Mount Path Problem
Symptom: Visiting the site shows "File not found," but container logs show PHP-FPM running normally.
**Cause**: Official WordPress image places files in /var/www/html, but your host directory is mounted to the wrong path.
Correct nginx configuration (must be written in nginx/default.conf):
server {
listen 80;
server_name yourdomain.com;
root /var/www/html;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
**Note**: The fastcgi_pass php:9000; uses php as the docker-compose service name, not localhost.
Pitfall 3: MySQL Data Persistence Failure—Common Volume Mount Error
Symptom: After restarting containers, database is empty, all WordPress content lost.
Cause: Not using Docker volume correctly, data was stored in container writable layer (deleted when container removed).
Correct configuration (already shown in docker-compose.yml above):
volumes:
db_data: # named volume, managed by Docker
# instead of:
# volumes:
# - /var/lib/mysql # host path, may have permission issues
Verify persistence is working:
docker volume ls | grep wp
# should see wp_db_data
docker inspect wp_mysql | grep -A5 Mounts
# should see Destination: /var/lib/mysql type: volume
Pitfall 4: WordPress Cannot Connect to Redis—Inter-Container Communication DNS Problem
Symptom: Installed Redis Object Cache plugin but connection fails, site is still slow.
**Cause**: When WordPress container connects to Redis, hostname should be redis (docker-compose service name), not localhost or 127.0.0.1.
Configure Redis in wp-config.php:
define('WP_REDIS_HOST', 'redis');
define('WP_REDIS_PORT', '6379');
define('WP_REDIS_TIMEOUT', '2.5');
define('WP_REDIS_READ_TIMEOUT', '2.5');
define('WP_REDIS_DATABASE', '0');
Verify Redis is reachable:
docker exec -it wp_php sh -c "nc -zv redis 6379"
# success output: redis (172.x.x.x:6379) open
Pitfall 5: 502 Bad Gateway After WordPress Upgrade—PHP-FPM Process Crash
Symptom: Upgrading theme or plugin in WordPress admin, site immediately shows 502 error.
Cause: PHP-FPM default process count configured too low, all processes crash during high concurrency or plugin execution timeout.
Adjust PHP-FPM configuration in docker-compose.yml:
php:
image: php:8.3-fpm-alpine
volumes:
- ./wordpress:/var/www/html
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini:ro
php/local.ini configuration content:
memory_limit = 256M
max_execution_time = 300
upload_max_filesize = 64M
post_max_size = 64M
[www]
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
Pitfall 6: HTTP Protocol Problem—Blank Page or Redirect Loop
Symptom: After visiting the site, redirects to HTTPS, or page is blank with no errors.
Cause: Nginx inside Docker doesn't know if external proxy/CDN is used, need to force correct protocol in wp-config.php.
Add to wp-config.php:
// Force HTTP_HOST correct
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_HOST'] ?? 'yourdomain.com';
// If using reverse proxy/load balancer
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
Nginx proxy_headers configuration (if external proxy exists):
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Pitfall 7: Site Won't Open After docker-compose down—Network Cleanup Problem
Symptom: After executing docker-compose down then up, site inaccessible but all containers appear to be running normally.
Cause: Docker network got cleaned up but wasn't correctly rebuilt, containers can't resolve each other's hostnames.
Solution: Use docker-compose down --remove-orphans instead of plain down:
# Stop and clean (recommended)
docker-compose down --remove-orphans
# Full restart (completely solves network issues)
docker-compose down -v # -v deletes volumes, use with caution!
docker-compose up -d
Daily maintenance commands:
# View all container status
docker-compose ps
# View logs
docker-compose logs -f nginx
# Restart single service
docker-compose restart php
# Full config reload
docker-compose down && docker-compose up -d
Summary: Core Principles for Deploying WordPress with Docker
1. UID matching: Host user UID and container user UID must match, otherwise all file operations encounter permission issues
2. Use named volumes: MySQL and Redis data must use Docker-managed named volumes, not host paths
3. Service name is hostname: Inter-container communication uses docker-compose service names (php, db, redis), not localhost
4. Configure PHP-FPM proactively: Default configuration is too low, WordPress plugins easily crash during execution
5. Network rebuild uses --remove-orphans: Avoid network disconnection after down/up
If it's your first time deploying WordPress on Docker, test the complete workflow on a local VM first, then migrate to production servers.
👉 Start now: https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: