Docker Compose deployment for WordPress + MariaDB 11 + Redis 7. Two web server variants: Nginx+PHP-FPM (default, recommended) and Apache. Not a theme/plugin project — the repo defines container images and orchestration.
| File | Web server | Use case |
|---|---|---|
compose.yaml |
Nginx + PHP-FPM (supervisor) | Default, Coolify-ready |
compose.apache.yaml |
Apache mod_php | Legacy / simpler |
compose.dev.yaml |
Overlay only | Adds phpMyAdmin, exposes MariaDB port |
Each compose file is self-contained and Coolify-compatible (uses expose not ports, env vars only, no Docker secrets).
Dockerfile— Nginx+PHP-FPM image onphp:8.3-fpm-bookworm; adds WP-CLI, redis/igbinary/imagick extensions, nginx+supervisor configsDockerfile.apache— Apache image onwordpress:php8.3-apache; adds WP-CLI, redis/igbinary extensions, Apache mods, security headersconfig/— Nginx, PHP-FPM, supervisor, and PHP ini configs for the Nginx variantdocker-entrypoint.sh— Nginx variant: waits for MariaDB/Redis, downloads WordPress, creates wp-config.php, handles migrations, HTTPS detection, permissionsdocker-entrypoint-apache.sh— Apache variant: cache cleanup, optional Redis flush, Matomo dirs/permissions, uploads.htaccess, session purge.env.example— Template for required env vars
- Copy
.env.exampleto.envand fill in passwords docker compose up -d(nginx) ordocker compose -f compose.apache.yaml up -d(apache)- First start: WordPress is auto-downloaded; visit
/wp-admin/install.phpto complete GUI install
- WP Cron disabled by default (
DISABLE_WP_CRONenv var); external scheduler must hitwp-cron.php FLUSH_REDIS_ON_STARTUPenv var (defaultfalse) — settrueto flush Redis on container start- Redis uses igbinary serialization (
WP_REDIS_IGBINARYenabled); requiresigbinary+redisPHP extensions - Nginx variant: uploads PHP execution blocked via nginx
locationblock; rate limiting onwp-login.php;xmlrpc.phpdenied - Apache variant: uploads PHP execution blocked via
.htaccess(secure-uploads.sh) - Both variants: Cloudflare trusted proxy ranges configured;
X-Forwarded-Proto/CF-VisitorsetHTTPS=on - Nginx entrypoint auto-detects migration from Apache variant and handles credential/Redis config update
- All services run with
cap_drop: ALL+ minimalcap_add
Infra repo — no package manager, no tests, no CI. Verify changes with:
docker compose build— rebuild the imagedocker compose up -d && docker compose logs -f— runtime validationdocker compose exec wordpress wp <command>— WP-CLI inside the container
All commits MUST follow Conventional Commits.
type(scope): short description
optional body
| Type | When to use |
|---|---|
feat |
New feature or capability |
fix |
Bug fix |
docs |
Documentation changes only |
style |
Formatting, whitespace, no code change |
refactor |
Code restructuring, no behavior change |
perf |
Performance improvement |
test |
Adding or fixing tests |
chore |
Maintenance, deps, tooling, CI |
ci |
CI/CD pipeline changes |
revert |
Reverting a previous commit |
| Scope | What it covers |
|---|---|
nginx |
Nginx variant: compose.yaml, Dockerfile, docker-entrypoint.sh, config/ |
apache |
Apache variant: compose.apache.yaml, Dockerfile.apache, docker-entrypoint-apache.sh |
dev |
Dev overlay: compose.dev.yaml |
redis |
Redis configuration or entrypoint cache logic |
mariadb |
MariaDB configuration |
docs |
README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT |
ci |
GitHub workflows, templates |
repo |
Root-level files: .gitignore, .env.example, AGENTS.md, LICENSE |
- One type per commit — do not mix
featandfixin the same commit - Lowercase type and scope —
feat(nginx):notFeat(NGINX): - Imperative mood — "add rate limiting" not "added rate limiting"
- No period at end of subject line
- Max 72 chars for subject line
- Body is optional but required for breaking changes
- Breaking changes: append
!to type (feat(nginx)!: ...) and document in body withBREAKING CHANGE:prefix
feat(nginx): add rate limiting on wp-login.php
fix(apache): resolve Redis flush on startup
docs: add CONTRIBUTING.md and SECURITY.md
chore(repo): update .gitignore patterns
refactor(nginx): simplify entrypoint health check logic
perf(mariadb): tune InnoDB buffer pool to 512MB
- Secrets, passwords, API keys,
.envfiles - Generated files or build artifacts
- Unrelated changes bundled together