WordPress Docker: Slow Performance — Fix
The Problem
WordPress runs noticeably slower in Docker than on bare metal. Pages take 2-5+ seconds to load. The admin dashboard feels sluggish. TTFB (Time to First Byte) exceeds 1 second even on fast hardware.
This is one of the most common complaints about self-hosted WordPress with Docker, and it has several fixable causes.
The Causes
WordPress-in-Docker performance issues usually come from one or more of these:
- Slow volume mounts — Docker’s file system layer adds overhead, especially on macOS/Windows
- Untuned PHP-FPM — Default worker counts are too low for WordPress
- Missing OPcache — PHP recompiles every file on every request
- Unoptimized MySQL/MariaDB — Default database settings aren’t tuned for WordPress
- No page caching — Every request hits PHP and the database
- Missing Redis/Memcached — Object cache isn’t configured
The Fix
Method 1: Enable OPcache (Biggest Impact)
OPcache pre-compiles PHP files so they don’t need to be parsed on every request. This alone can cut response times by 50-70%.
Create a php-custom.ini file:
; OPcache settings
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=0
; PHP performance
upload_max_filesize=64M
post_max_size=64M
memory_limit=256M
max_execution_time=300
max_input_time=300
Mount it in your Docker Compose:
services:
wordpress:
image: wordpress:6.7-php8.3-fpm
volumes:
- wordpress_data:/var/www/html
- ./php-custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro
restart: unless-stopped
Method 2: Tune MySQL/MariaDB
Default MySQL settings allocate minimal memory. Add these to a custom config:
Create mysql-custom.cnf:
[mysqld]
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
query_cache_type = 1
query_cache_size = 32M
query_cache_limit = 2M
tmp_table_size = 64M
max_heap_table_size = 64M
max_connections = 50
Mount it in your database service:
services:
wordpress-db:
image: mariadb:11.4
volumes:
- db_data:/var/lib/mysql
- ./mysql-custom.cnf:/etc/mysql/conf.d/custom.cnf:ro
environment:
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: change-this-password
MARIADB_ROOT_PASSWORD: change-this-root-password
restart: unless-stopped
Method 3: Add Redis Object Cache
WordPress queries the database for the same data repeatedly. Redis caches these queries in memory.
Add Redis to your Docker Compose:
services:
wordpress-redis:
image: redis:7.4-alpine
container_name: wordpress-redis
restart: unless-stopped
volumes:
- redis_data:/data
Then install the Redis Object Cache plugin in WordPress:
- Install “Redis Object Cache” by Till Krüss from the WordPress plugin directory
- Add to
wp-config.php(or mount via volume):
define('WP_REDIS_HOST', 'wordpress-redis');
define('WP_REDIS_PORT', 6379);
- Activate the plugin and enable Redis from Settings → Redis
Method 4: Use PHP-FPM Instead of Apache
The default wordpress:latest image uses Apache with mod_php. The FPM variant is significantly faster:
services:
wordpress:
image: wordpress:6.7-php8.3-fpm # FPM variant, not apache
volumes:
- wordpress_data:/var/www/html
environment:
WORDPRESS_DB_HOST: wordpress-db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: change-this-password
restart: unless-stopped
wordpress-nginx:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- wordpress_data:/var/www/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- wordpress
restart: unless-stopped
Create nginx.conf:
server {
listen 80;
server_name _;
root /var/www/html;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass wordpress:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Method 5: Fix Volume Performance (macOS/Windows)
On macOS and Windows, Docker volumes are significantly slower than on Linux due to file system translation. If you’re developing locally:
services:
wordpress:
image: wordpress:6.7-php8.3-fpm
volumes:
- wordpress_data:/var/www/html # Use named volume, not bind mount
Named volumes perform better than bind mounts (./wordpress:/var/www/html) on non-Linux hosts. On Linux, there’s no difference.
Optimized Complete Stack
Here’s a full Docker Compose with all optimizations applied:
services:
wordpress:
image: wordpress:6.7-php8.3-fpm
volumes:
- wordpress_data:/var/www/html
- ./php-custom.ini:/usr/local/etc/php/conf.d/custom.ini:ro
environment:
WORDPRESS_DB_HOST: wordpress-db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: change-this-password
depends_on:
wordpress-db:
condition: service_healthy
restart: unless-stopped
wordpress-nginx:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
- wordpress_data:/var/www/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- wordpress
restart: unless-stopped
wordpress-db:
image: mariadb:11.4
volumes:
- db_data:/var/lib/mysql
- ./mysql-custom.cnf:/etc/mysql/conf.d/custom.cnf:ro
environment:
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: change-this-password
MARIADB_ROOT_PASSWORD: change-this-root-password
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
wordpress-redis:
image: redis:7.4-alpine
restart: unless-stopped
volumes:
- redis_data:/data
volumes:
wordpress_data:
db_data:
redis_data:
Prevention
- Always use the PHP-FPM variant (
wordpress:x.x-phpx.x-fpm) with Nginx, not the Apache variant - Mount a custom
php.iniwith OPcache enabled from day one - Tune MariaDB/MySQL memory settings for your available RAM
- Install Redis Object Cache early — it’s easier than retrofitting later
- Use a caching plugin (WP Super Cache or W3 Total Cache) for full-page caching
- Monitor TTFB with browser developer tools — target under 200ms
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments