- Docker Compose stack for local WordPress development.
- WordPress core is placed under
/var/www/html/wp, and mutable content is placed under/var/www/html/content. php,webserver, andwp-cliare custom multi-stage images that run ongcr.io/distroless/static-debian13.- The stack includes
wp-cli, phpMyAdmin, and Mailpit.
- Nginx (
bin/webserver/nginx/Dockerfile) - Apache HTTP Server (
bin/webserver/httpd/Dockerfile) - WordPress + PHP-FPM (
bin/wordpress/php83/Dockerfile,bin/wordpress/php84/Dockerfile,bin/wordpress/php85/Dockerfile) - MySQL (
bin/database/mysql80/Dockerfileorbin/database/mysql84/Dockerfile) - phpMyAdmin (
phpmyadmin) - Mailpit (
axllent/mailpit)
.env: default environment variables, ports, and image selectorsMakefile: helper targets to build local/Fargate app imagesdocker-compose.yml: service definitions, mounts, and networkingwww/wp-config.php: WordPress runtime configuration (mounted to/var/www/html/wp/wp-config.php)www/content: host-mounted content (languages,mu-plugins,plugins,themes,uploads)config/php/php.ini: PHP configuration forphpimage and phpMyAdminconfig/nginx/nginx.conf: top-level Nginx configuration copied into the imageconfig/nginx/conf.d/default.conf: HTTP server rules and WordPress routingconfig/nginx/conf.d/default-ssl.conf: optional HTTPS server block templateconfig/httpd/httpd.conf: Apache global configuration copied into the image whenWEBSERVER=httpdconfig/httpd/conf.d/default.conf: Apache vhost rules and PHP-FPM proxy settingsconfig/initdb: default path for DB initialization SQL/scripts mounted to/docker-entrypoint-initdb.dbin/wordpress/cli/update-wordpress-languages.sh: one-time language update script forwp-cli
/var/www/html
├── index.php # loads /wp/wp-blog-header.php
├── assets
│ ├── languages
│ ├── mu-plugins
│ ├── plugins
│ ├── themes
│ └── uploads
└── wp
├── wp-admin
├── wp-includes
├── wp-config.php
└── ...
- Start (first run, or after Dockerfile/config changes):
docker compose up -d --build - Open:
http://localhost:8080(site)http://localhost:8080/wp/wp-admin(WordPress admin)http://localhost:9080(phpMyAdmin)http://localhost:19980(Mailpit UI)
- Stop:
docker compose stop
To reset persistent named volumes (dbdata, mailpitdata):
docker compose down -vdocker compose up -d --build
Default ports in .env:
- HTTP:
HOST_MACHINE_UNSECURE_HOST_PORT=8080 - HTTPS:
HOST_MACHINE_SECURE_HOST_PORT=8443 - phpMyAdmin HTTP/HTTPS:
HOST_MACHINE_PMA_PORT=9080,HOST_MACHINE_PMA_SECURE_PORT=9443 - Mailpit UI:
HOST_MACHINE_MAILPIT_PORT=19980(mail:1025is available only inside the Docker network)
Database access:
- The
databaseservice is exposed only inside the Compose network (expose: 3306). - Use service name
database:3306from other containers, or phpMyAdmin from the host.
- Show available targets:
make - Build local app images (
DEPLOY_ENV=local):make build-local - Build Fargate app images (
DEPLOY_ENV=fargate):make build-fargate - Rebuild local images without cache:
make rebuild-local - Rebuild Fargate images without cache:
make rebuild-fargate build-localuses tag suffix-local,build-fargateuses-fargate- Example tags:
distroless-wp:wordpress-php84-local,distroless-wp:wordpress-php84-fargate build-*targets build these app images:php83,php84,php85,nginx,httpd,wp-cli- To limit variants:
make build-fargate PHP_VERSIONS="php84" WEBSERVERS="nginx" - To override suffixes:
make build-fargate IMAGE_TAG_SUFFIX_FARGATE=-ecs
- Command format:
docker compose exec wp-cli wp <command> - Examples:
docker compose exec wp-cli wp core versiondocker compose exec wp-cli wp plugin listdocker compose exec wp-cli wp core update-dbdocker compose exec -T wp-cli wp option get home
PHPVERSIONselects the PHP image Dockerfile (php83,php84,php85), default:php84DATABASEselects the database Dockerfile (mysql80,mysql84), default:mysql84WEBSERVERselects the web server image Dockerfile (httpd,nginx), default:nginxWP_VERSIONcontrols WordPress core source for the PHP build, default:latestDEPLOY_ENVswitches image runtime tuning (localorfargate), default:localIMAGE_TAG_SUFFIXappends to image tags (default: empty, example:-local)- After changing these values in
.env, rebuild withdocker compose up -d --build - Container names are prefixed by
COMPOSE_PROJECT_NAME(default:wp), so the web server is${COMPOSE_PROJECT_NAME}-httpdor${COMPOSE_PROJECT_NAME}-nginx - For Fargate-compatible images, set
DEPLOY_ENV=fargatebefore build.
To send mail to the bundled Mailpit service from WordPress:
- Activate
FluentSMTP. - Configure SMTP settings:
- Host:
mail - Port:
1025 - Authentication:
disable - Encryption:
none - Auto TLS:
disable
- Host:
- Put
cert.pemandcert-key.peminconfig/ssl. - If using Nginx, uncomment the HTTPS server block in
config/nginx/conf.d/default-ssl.conf. - Access
https://localhost:8443.
STAGEcontrols WordPress debug-related constants inwww/wp-config.php(productiondisables debug flags).DEPLOY_ENV=fargateapplies these image-level settings:- Web server -> PHP-FPM upstream:
127.0.0.1:9000 - Nginx/Apache access and error logs:
STDOUT/STDERR - PHP-FPM access and error logs:
STDOUT/STDERR
- Web server -> PHP-FPM upstream:
- DB credentials and auth salts are injected through Compose environment variables.
- WordPress content is mounted from
${WP_CONTENT_DIR-./www/content}(bind mount), while MySQL and Mailpit data are stored in named volumes (dbdata,mailpitdata). - Log mount directories are configurable via
.env(NGINX_LOG_DIR,HTTPD_LOG_DIR,PHP_FPM_LOG_DIR,MYSQL_LOG_DIR). - Replace placeholder auth salts in
.envbefore any non-local use. - On startup,
wp-cliruns language updates once and writes/var/www/html/.wp-language-updated.