A production-parity local development environment for WordPress. The stack mirrors a real-world server setup so what runs locally behaves the same way in production.
| Service | Version |
|---|---|
| WordPress | Latest |
| PHP | 8.3 (FPM) |
| nginx | 1.24 |
| MariaDB | 10.11 |
| ImageMagick | 6.9.x (Imagick PHP ext included) |
| Ghostscript | 10.x (PDF thumbnail support) |
- Docker Desktop (or Docker Engine + Compose v2)
# 1. Copy the environment file and fill in your passwords
cp .env.example .env
# 2. Build the image and start all services
docker compose up -d --build
# 3. Open WordPress in your browser
open http://localhostOn first boot WordPress will walk you through the five-minute install wizard.
.
├── Dockerfile # WordPress + ImageMagick + Ghostscript
├── docker-compose.yml # nginx + WordPress + MariaDB
├── .env.example # Environment variable template
├── nginx/
│ └── default.conf # nginx server block (pretty permalinks, 64 MB uploads)
├── php/
│ ├── custom.ini # PHP overrides (upload limits, memory, timeouts)
│ └── imagemagick-policy.xml # Resource limits + PDF/PS/EPS coder rules
├── db/
│ └── my.cnf # MariaDB charset, packet size, connection limits
├── plugins/ # Drop custom plugins here (tracked by git)
├── themes/ # Drop custom themes here (tracked by git)
└── uploads/ # User-uploaded media (gitignored)
Copy .env.example to .env and adjust as needed.
| Variable | Default | Description |
|---|---|---|
DB_ROOT_PASSWORD |
— | MariaDB root password (required) |
DB_NAME |
wordpress |
Database name |
DB_USER |
wpuser |
Database user |
DB_PASSWORD |
— | Database user password (required) |
TABLE_PREFIX |
wp_ |
WordPress table prefix |
WP_DEBUG |
0 |
Set to 1 to enable WP_DEBUG |
DOMAIN |
localhost |
Sets WP_HOME and WP_SITEURL |
HTTP_PORT |
80 |
Host port nginx listens on |
# Start all services in the background
docker compose up -d
# Stop all services (data is preserved in Docker volumes)
docker compose down
# Rebuild the WordPress image after Dockerfile changes
docker compose up -d --build wordpress
# Tail logs for all services
docker compose logs -f
# Tail logs for a single service
docker compose logs -f wordpress
docker compose logs -f nginx
docker compose logs -f db
# Open a shell inside the WordPress container
docker compose exec wordpress bash
# Run WP-CLI commands
docker compose exec wordpress wp --infoDrop your theme or plugin folder into themes/ or plugins/ respectively. They are bind-mounted directly into the container, so changes appear instantly without a restart.
themes/my-theme/ → /var/www/html/wp-content/themes/my-theme/
plugins/my-plugin/ → /var/www/html/wp-content/plugins/my-plugin/
WordPress's bundled themes (twentytwenty*) and default plugins (akismet, hello.php) are excluded from git — they are managed by the Docker image, not this repository.
Key values set in php/custom.ini (all match production defaults):
| Setting | Value |
|---|---|
upload_max_filesize |
64M |
post_max_size |
64M |
max_file_uploads |
20 |
memory_limit |
256M |
max_execution_time |
300s |
max_input_vars |
3000 |
ImageMagick is configured in php/imagemagick-policy.xml with the following resource limits:
| Resource | Limit |
|---|---|
| Memory | 1 GiB |
| Map | 2 GiB |
| Disk | 2 GiB |
| Files | 768 |
| Threads | 1 |
PDF, PS, and EPS coders are explicitly allowed so WordPress can generate thumbnails from document uploads. Remote URL delegates are blocked for security.
| Data | Stored in |
|---|---|
| WordPress core files | wordpress_data Docker volume |
| Database | db_data Docker volume |
| Uploads | ./uploads/ bind mount |
| Custom themes/plugins | ./themes/ / ./plugins/ bind mounts |
To fully reset the environment (including the database and all WordPress files):
docker compose down -v