Common Docker issues and solutions for local WordPress development with this template.
Related: LOCAL-DEVELOPMENT.md | General Troubleshooting
- Docker Desktop Won't Start
- Port 8080 or 8081 Already in Use
- Database Connection Refused on First Start
- Volume Permission Denied (Linux)
- Mounted Theme/Plugin Files Not Visible in WordPress
- Container Keeps Restarting
- MySQL Data Corruption After Unclean Shutdown
- Cannot Connect Between Containers (Networking)
- Docker Compose "Version" Warning
- Slow File I/O on macOS
- WSL2 Backend Issues (Windows)
- Out of Disk Space / Large Docker Volumes
- WP-CLI "Not Found" Inside Container
- phpMyAdmin Cannot Connect to Database
- Environment Variables Not Loading from .env
Symptoms: docker compose commands fail with "Cannot connect to the Docker daemon" or Docker Desktop stays on "Starting..."
Solutions:
| Platform | Fix |
|---|---|
| Windows | Ensure WSL2 is enabled: wsl --status. If missing, run wsl --install and restart. |
| macOS | Grant Docker Desktop full disk access in System Settings > Privacy & Security. |
| Linux | Start the daemon: sudo systemctl start docker. Add yourself to the docker group: sudo usermod -aG docker $USER then log out/in. |
All platforms:
- Open Docker Desktop and wait for the green status icon.
- If stuck, quit Docker Desktop completely and relaunch.
- Verify with:
docker info
Symptoms: Bind for 0.0.0.0:8080 failed: port is already allocated
Find the conflict:
# Windows (PowerShell)
netstat -ano | findstr :8080
# macOS / Linux
lsof -i :8080Fix — Option A: Stop the conflicting process.
Fix — Option B: Change the port in docker-compose.yml:
services:
wordpress:
ports:
- "9090:80" # WordPress on port 9090 instead of 8080
phpmyadmin:
ports:
- "9091:80" # phpMyAdmin on port 9091 instead of 8081After changing ports, update the install URL:
docker compose exec wordpress wp option update siteurl "http://localhost:9090" --allow-root
docker compose exec wordpress wp option update home "http://localhost:9090" --allow-rootSymptoms: "Error establishing a database connection" in the browser, or WordPress container logs show MySql Connection Error: (2002) Connection refused
Why: MySQL takes 30-60 seconds to initialise on first run. WordPress may attempt to connect before MySQL is ready.
Solutions:
- Wait and refresh. Give MySQL up to 60 seconds on first boot.
- Check MySQL is running:
docker compose ps db # Should show "Up" / "running" - Check logs for MySQL errors:
docker compose logs db
- Verify credentials match. The
WORDPRESS_DB_*variables indocker-compose.yml(or.env) must match theMYSQL_*variables:WordPress var Must match MySQL var WORDPRESS_DB_HOSTService name ( db)WORDPRESS_DB_USERMYSQL_USERWORDPRESS_DB_PASSWORDMYSQL_PASSWORDWORDPRESS_DB_NAMEMYSQL_DATABASE - Nuclear option — reset everything:
./wordpress-local.sh clean ./wordpress-local.sh start ./wordpress-local.sh install
Symptoms: WordPress cannot write to wp-content/themes or wp-content/plugins inside the container, or you see Permission denied errors in logs.
Why: On native Linux, Docker runs as root inside the container (UID 33 for www-data), but bind-mounted host directories may be owned by your user (UID 1000).
Solutions:
Option A — Fix host directory ownership:
sudo chown -R 33:33 themes/ plugins/ mu-plugins/Option B — Match UIDs (recommended for development):
Add to docker-compose.yml under the wordpress service:
user: "${UID:-1000}:${GID:-1000}"Option C — Use permissive mode (development only):
chmod -R 777 themes/ plugins/ mu-plugins/Note: This issue does not affect macOS or Windows (Docker Desktop uses a VM, so file ownership is handled transparently).
Symptoms: Files exist on your host in themes/ or plugins/ but WordPress doesn't see them.
Solutions:
- Verify the volume mounts in
docker-compose.yml:volumes: - ./themes:/var/www/html/wp-content/themes - ./plugins:/var/www/html/wp-content/plugins - ./mu-plugins:/var/www/html/wp-content/mu-plugins
- Check inside the container:
docker compose exec wordpress ls /var/www/html/wp-content/themes/ - Named volume conflict: The
wordpress_datanamed volume persists WordPress core files. If you started containers before adding the bind mounts, the named volume may have populatedwp-content/themesfirst, causing Docker to ignore the bind mount. Fix:./wordpress-local.sh clean # Removes named volumes ./wordpress-local.sh start # Recreates with correct mounts ./wordpress-local.sh install
- Restart containers after changing
docker-compose.yml:docker compose down && docker compose up -d
Symptoms: docker compose ps shows a container in Restarting state with increasing restart counts.
Diagnose:
docker compose logs wordpress # or 'db' for MySQLCommon causes and fixes:
| Cause | Fix |
|---|---|
| MySQL not ready | WordPress auto-retries; wait 60 seconds |
| Corrupt MySQL data | ./wordpress-local.sh clean and restart |
Invalid .env values |
Check .env for typos or missing values |
| Port conflict inside container | Check no other service occupies port 80 internally |
If a container keeps crash-looping, stop and remove it:
docker compose down
docker compose up -dSymptoms: MySQL container won't start, logs show InnoDB: Recovery failed or Table is marked as crashed.
Why: Shutting down Docker without stopping containers (e.g., killing Docker Desktop, system crash, or force-quitting) can corrupt InnoDB files.
Solutions:
-
Try recovery mode. Add to the
dbservice indocker-compose.yml:command: --innodb-force-recovery=1
Start containers, export your data, then remove the flag and clean:
docker compose exec db mysqldump -u wordpress -pwordpress wordpress > backup.sql ./wordpress-local.sh clean ./wordpress-local.sh start
-
Skip recovery — reset the database:
./wordpress-local.sh clean ./wordpress-local.sh start ./wordpress-local.sh install
Prevention: Always stop containers gracefully:
./wordpress-local.sh stop
# or
docker compose downSymptoms: WordPress can't reach the database at hostname db, or phpMyAdmin shows "Connection refused".
Solutions:
-
Verify all containers are on the same network:
docker network ls docker network inspect flavian_default
All three services (wordpress, db, phpmyadmin) should appear in the network.
-
Use the service name, not
localhost. Within Docker Compose, containers reach each other by service name. The database host isdb, notlocalhostor127.0.0.1. -
Recreate the network:
docker compose down docker compose up -d
-
Check for custom networks. If you've added a custom
networks:block indocker-compose.yml, ensure all services are attached to it.
Symptoms: Warning: the attribute 'version' is obsolete
Why: Docker Compose V2 (the docker compose plugin, not the standalone docker-compose) ignores the version key. The warning is harmless.
Fix (optional): Remove the version: '3.8' line from docker-compose.yml. The file remains compatible with both V1 and V2.
Check your Compose version:
docker compose version # V2 (plugin)
docker-compose --version # V1 (standalone, legacy)This project's
wordpress-local.shusesdocker-compose(V1 syntax). If you only have V2 installed, either install the standalone binary or alias it:alias docker-compose="docker compose".
Symptoms: Page loads take 5-10+ seconds, especially on large themes. docker compose logs shows no errors.
Why: macOS Docker Desktop uses a Linux VM. File synchronisation between host and VM has historically been slow for bind mounts.
Solutions:
- Use VirtioFS (recommended). In Docker Desktop: Settings > General > "Choose file sharing implementation" > VirtioFS. Restart Docker Desktop.
- Reduce mounted paths. Only mount what you need (the template already does this —
themes/,plugins/,mu-plugins/only). - Use Mutagen-based sync. Docker Desktop offers this as an alternative file sync option in Settings > General.
- Avoid watching too many files. Large
node_modulesor.gitdirectories inside mounted volumes slow sync. They should not be insidethemes/orplugins/.
Symptoms: Docker Desktop fails to start, shows "WSL 2 is not installed", or containers are extremely slow.
Solutions:
- Install/update WSL2:
wsl --install wsl --update
- Set WSL2 as default:
wsl --set-default-version 2
- Enable WSL2 backend in Docker Desktop: Settings > General > "Use the WSL 2 based engine" (checked).
- Allocate resources. Create or edit
%USERPROFILE%\.wslconfig:Then restart WSL:[wsl2] memory=4GB processors=2
wsl --shutdown - Store project files in WSL filesystem for best performance. Accessing files on the Windows filesystem (
/mnt/c/...) from WSL is slower than native WSL paths (~/projects/...).
Symptoms: No space left on device errors, Docker builds fail, or containers won't start.
Solutions:
- Check Docker disk usage:
docker system df
- Prune unused resources:
docker system prune # Remove stopped containers, unused networks, dangling images docker volume prune # Remove unused volumes (⚠️ deletes data!) docker image prune -a # Remove all unused images
- Increase disk allocation. Docker Desktop: Settings > Resources > Disk image size.
- Check volume sizes:
docker system df -v
Warning:
docker volume prunewill delete thewordpress_dataanddb_datavolumes if containers are stopped. Only run it when you're prepared to lose local WordPress data.
Symptoms: wp: command not found when running WP-CLI commands via docker compose exec.
Solutions:
-
Use the helper script:
./wordpress-local.sh shell # Then run wp commands inside the container wp theme list --allow-root -
Always include
--allow-rootwhen running as root in Docker:docker compose exec wordpress wp theme list --allow-root -
If WP-CLI is genuinely missing from the WordPress image:
docker compose exec wordpress bash -c \ "curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \ chmod +x wp-cli.phar && \ mv wp-cli.phar /usr/local/bin/wp"
Symptoms: phpMyAdmin shows "Connection refused" or login fails at http://localhost:8081.
Solutions:
- Check the
dbcontainer is running:docker compose ps db
- Verify phpMyAdmin environment variables in
.env:PMA_HOSTshould bedb(the Docker service name)PMA_USERandPMA_PASSWORDmust matchMYSQL_USER/MYSQL_PASSWORD
- Wait for MySQL to finish initialising (30-60 seconds on first run).
- Restart phpMyAdmin:
docker compose restart phpmyadmin
Symptoms: Containers start but WordPress shows "Error establishing a database connection", or env vars appear blank in container inspection.
Why: Docker Compose looks for a .env file in the same directory as docker-compose.yml. If the file is missing, misspelled, or has syntax errors, variables resolve to empty strings.
Solutions:
- Verify
.envexists in the project root alongsidedocker-compose.yml. - Check syntax. Each line should be
KEY=valuewith no spaces around=:WORDPRESS_DB_HOST=db WORDPRESS_DB_USER=wordpress WORDPRESS_DB_PASSWORD=wordpress WORDPRESS_DB_NAME=wordpress MYSQL_ROOT_PASSWORD=rootpassword PMA_USER=wordpress PMA_PASSWORD=wordpress
- No quotes needed unless the value contains spaces.
- Verify values reach containers:
docker compose exec wordpress env | grep WORDPRESS
- Restart after changes:
docker compose down && docker compose up -d
- Use VirtioFS for file sharing (Settings > General) — significantly faster than gRPC FUSE.
- If you see "Docker Desktop requires a newer macOS version", update macOS or use an older Docker Desktop release.
- Rosetta 2 emulation on Apple Silicon can slow x86 images. Use ARM-native images where available (the official
wordpress:latestandmysql:8.0images support ARM64).
- Run
dockerwithoutsudoby adding your user to thedockergroup:sudo usermod -aG docker $USER - File permissions are the most common issue — see Issue #4.
- If using Docker Desktop for Linux (not Docker Engine), the VM layer adds the same file sharing overhead as macOS.
- Install Docker Compose V2 plugin:
sudo apt install docker-compose-plugin
- WSL2 backend is required — Hyper-V backend is deprecated for most use cases.
- For best performance, clone the project into the WSL2 filesystem (
\\wsl$\Ubuntu\home\...) rather than/mnt/c/. - If Git Bash or MINGW is your shell, prefix interactive commands with
winpty:winpty docker compose exec wordpress bash - See Issue #11 for WSL2 setup.
A: On macOS and Windows, Docker Desktop is required (it provides the Linux VM). On Linux, you can use either Docker Engine (lighter, no VM) or Docker Desktop for Linux. Both work with this template.
A: Podman is largely Docker-compatible. Run podman compose up -d instead of docker compose up -d. You may need to adjust volume mount syntax and ensure podman-compose is installed. This is not officially tested with this template.
A:
./wordpress-local.sh clean # Removes containers AND volumes
./wordpress-local.sh start # Fresh containers
./wordpress-local.sh install # Reinstall WordPressA: Yes, but you must use different ports. Copy docker-compose.yml, change the port mappings (e.g., 8082:80), and use a different project name:
docker compose -p mysite2 up -dA: Use your machine's local IP instead of localhost:
# Find your IP
# Windows: ipconfig
# macOS/Linux: ifconfig or ip addrThen visit http://<your-ip>:8080. You may need to update the WordPress site URL:
docker compose exec wordpress wp option update siteurl "http://<your-ip>:8080" --allow-root
docker compose exec wordpress wp option update home "http://<your-ip>:8080" --allow-rootA: docker-compose is the standalone V1 tool (Python-based, legacy). docker compose is the V2 plugin (Go-based, current). This project's wordpress-local.sh uses V1 syntax. Both produce identical results for this template. If you only have V2, alias it: alias docker-compose="docker compose".
A:
docker compose exec wordpress wp user update admin --user_pass=newpassword --allow-rootA:
docker compose exec wordpress wp core update --allow-rootOr pull a newer WordPress image:
docker compose pull wordpress
docker compose up -dRun these commands to diagnose most issues:
# 1. Is Docker running?
docker info > /dev/null 2>&1 && echo "Docker OK" || echo "Docker NOT running"
# 2. Are containers up?
docker compose ps
# 3. Any errors in logs?
docker compose logs --tail=50
# 4. Can WordPress reach the database?
docker compose exec wordpress wp db check --allow-root
# 5. Are volumes mounted correctly?
docker compose exec wordpress ls /var/www/html/wp-content/themes/
# 6. Is the site responding?
curl -sI http://localhost:8080 | head -1
# 7. Disk space OK?
docker system dfIf all checks pass and you're still stuck, try a full reset:
./wordpress-local.sh clean && ./wordpress-local.sh start && ./wordpress-local.sh install