Why WordPress Was Slow on Docker Desktop for Windows
Table of Contents
Our local WordPress multisite environment suddenly started feeling much slower than expected. The first instinct was to ask the usual questions: is WordPress heavy, did a plugin slow it down, or is Docker under pressure? This post documents how we measured the problem, ruled out the wrong suspects, and fixed the real bottleneck.
The Symptom
The local site at http://localhost:8000/ was visibly slow to load. The admin area also felt sluggish. Early measurements showed homepage TTFB values between two and five seconds:
home total=2.320s ttfb=2.300s
run1 total=2.970s ttfb=2.954s
run2 total=3.602s ttfb=3.585s
run3 total=2.970s ttfb=2.944s
Those numbers are high for a local WordPress install. When static files respond quickly but PHP pages are slow, the investigation usually moves toward WordPress bootstrap cost, theme/plugin code, cron, the database, or the filesystem layer.
First Checks
We started with the Docker containers:
docker ps
docker stats --no-stream
WordPress, MariaDB, and phpMyAdmin were running. MariaDB looked healthy. The WordPress container showed occasional CPU activity, but that alone did not explain the delay.
Then we measured a static file response:
curl.exe -o NUL -s -w "static total=%{time_total}s ttfb=%{time_starttransfer}s\n" \
http://localhost:8000/wp-content/themes/twentytwentyfive/style.css
The static file came back in about 0.04s. That was an important separation: Docker port forwarding and Apache serving simple files were not the bottleneck. The delay appeared when PHP and WordPress had to load.
Was a Plugin the Culprit?
At the time, these network-active plugins were enabled:
multisite-translation-relations 1.0.4
plugin-check 1.9.0
We temporarily deactivated them and measured the homepage again:
wp plugin deactivate plugin-check --network
wp plugin deactivate multisite-translation-relations --network
The result was not a dramatic improvement. Even with the plugins disabled, responses were still in the 2.9-3.8 second range. That was a strong signal that plugin code was not the root cause.
WP-Cron Was Checked Too
The WP-Cron queue had a few events due immediately:
wp cron event list
Running due events showed that Site Health checks could be slow:
wp_version_check: 2.754s
wp_site_health_scheduled_check: 16.683s
This could add occasional weight to page loads, but even after clearing cron events the homepage TTFB remained around three to five seconds. Cron was a factor, not the root cause.
Database and Autoload Options
We also checked autoloaded options:
SELECT COUNT(*) AS autoload_options,
ROUND(SUM(LENGTH(option_value))/1024/1024, 3) AS autoload_mb
FROM wp_options
WHERE autoload IN ('yes','on','auto-on','auto');
The result was about 0.046 MB, which is tiny. The database tables also looked normal for a test environment. A bloated database or huge autoload payload was unlikely.
The Root Cause: Windows Bind Mounts
The breakthrough came from measuring WordPress bootstrap time on two different filesystems. First, we measured the normal setup where the WordPress directory was bind-mounted from the Windows filesystem into the Docker container. Then we copied the same WordPress files into the container's Linux filesystem and ran the same bootstrap from there.
bind-mount wp-load seconds=5.5932
linux-copy wp-load seconds=0.4811
That difference clarified the root cause. WordPress itself was not inherently slow. The expensive part was Docker Desktop reading thousands of PHP files through a Windows bind mount. Because WordPress touches many PHP files on every request, that filesystem overhead directly increased TTFB.
The Fix: Named Volume for WordPress Core, Bind Mounts for Our Code
Instead of bind-mounting the entire WordPress directory, we moved to a more balanced layout:
/var/www/htmlbecame a Docker named volume for WordPress core.- Only the theme and plugin folders we actively develop remained bind mounts.
wp-config.php,.htaccess, anduploadsstayed controlled from the host.- Debug and automatic update checks were disabled in the fast local mode.
The override file looked like this:
services:
wordpress:
environment:
WORDPRESS_DEBUG: "false"
WORDPRESS_CONFIG_EXTRA: |
define( 'DISABLE_WP_CRON', true );
define( 'WP_AUTO_UPDATE_CORE', false );
define( 'AUTOMATIC_UPDATER_DISABLED', true );
volumes:
- wp_core:/var/www/html
- ./wordpress/wp-config.php:/var/www/html/wp-config.php
- ./wordpress/.htaccess:/var/www/html/.htaccess
- ./wordpress/wp-content/themes/mudos-theme:/var/www/html/wp-content/themes/mudos-theme
- ./wordpress/wp-content/plugins/mudos-multilingual-relations:/var/www/html/wp-content/plugins/mudos-multilingual-relations
- ./wordpress/wp-content/plugins/multisite-translation-relations:/var/www/html/wp-content/plugins/multisite-translation-relations
- ./wordpress/wp-content/plugins/plugin-check:/var/www/html/wp-content/plugins/plugin-check
- ./wordpress/wp-content/uploads:/var/www/html/wp-content/uploads
volumes:
wp_core:
We started the environment with:
docker compose -f docker-compose.yml -f docker-compose.fast.yml up -d
The Result
After the change, homepage TTFB dropped sharply:
fixed-fast run1 total=0.478s ttfb=0.475s
fixed-fast run2 total=0.166s ttfb=0.164s
fixed-fast run3 total=0.133s ttfb=0.127s
fixed-fast run4 total=0.343s ttfb=0.333s
fixed-fast run5 total=0.131s ttfb=0.128s
The /tr/ subsite also responded at roughly 0.24s. Network-active plugins and the multisite site list continued to work correctly.
Takeaway
When developing WordPress on Docker Desktop for Windows, performance issues are not always caused by PHP, theme code, or plugin code. For applications like WordPress that read many files per request, the bind mount layer can become the bottleneck.
The practical pattern is simple:
- Keep WordPress core files inside a Docker named volume when they do not need constant editing.
- Bind-mount only the theme and plugin folders you are actively developing.
- Manage WP-Cron, debug behavior, and update checks consciously in local fast mode.
- Separate guesses from measurements by checking TTFB and isolated bootstrap timing such as
wp-load.php.
This approach keeps the development workflow comfortable while significantly reducing the filesystem overhead of the Windows and Docker Desktop combination.