Skip to content

artysan-code/wordpress-composing

WordPress Composing

Production-grade Docker Compose deployment for WordPress + MariaDB 11 + Redis 7.

Two web server variants: Nginx + PHP-FPM (recommended) and Apache mod_php (legacy). Designed for self-hosting on Coolify, Traefik, Caddy, or any Docker-native reverse proxy.


Quick Start

git clone https://github.com/artysan-code/wordpress-composing.git
cd wordpress-composing
cp .env.example .env
# Edit .env with your passwords
docker compose up -d

Visit http://your-host/wp-admin/install.php to complete the WordPress installation.


Architecture

The stack runs three services on an internal backend network — database and cache are never exposed externally. WordPress sits behind a reverse proxy (Coolify, Traefik, Caddy) that handles TLS termination.

  • WordPress — Nginx + PHP-FPM 8.3 (supervisor) or Apache mod_php, with WP-CLI pre-installed
  • MariaDB 11 — tuned for WordPress with InnoDB buffer pool and slow query log
  • Redis 7 — object cache with igbinary serialization, password-protected

Compose Variants

File Web Server Use Case
compose.yaml Nginx + PHP-FPM (supervisor) Default. Production-ready, Coolify-compatible
compose.apache.yaml Apache mod_php Legacy / simpler setups
compose.dev.yaml Overlay only Adds phpMyAdmin, exposes MariaDB port 3306

The dev overlay is never used alone — always combine it with a base file:

docker compose -f compose.yaml -f compose.dev.yaml up -d

Features

Security

  • cap_drop: ALL with minimal cap_add on every service
  • PHP execution blocked in /uploads directory
  • xmlrpc.php denied; wp-login.php rate limited (Nginx)
  • Security headers: X-Content-Type-Options, X-Frame-Options, Referrer-Policy, X-XSS-Protection
  • Vulnerability scanner bot blocking
  • Cloudflare trusted proxy ranges configured

Performance

  • OPcache: 256 MB, 10 000 files
  • Redis object cache with igbinary serialization
  • Gzip compression (level 6)
  • Static asset caching with 30-day expires
  • MariaDB tuned: 512 MB InnoDB buffer pool, slow query log
  • All resource limits configurable via environment variables

Operations

  • WP-CLI pre-installed in both variants
  • Health checks on all services
  • Structured JSON logging with rotation (configurable size/count)
  • Redis cache flush on startup (opt-in)
  • WP Cron disable (opt-in) — use external scheduler for wp-cron.php

Reverse Proxy & HTTPS

The entrypoint auto-configures HTTPS detection in wp-config.php for:

Header Used By
X-Forwarded-Proto Coolify, Traefik, generic proxies
X-Forwarded-SSL Standard SSL termination
CF-Visitor Cloudflare

If the site URL starts with http:// but the proxy sends HTTPS headers, WordPress URLs are auto-migrated to https:// in the database on container start.


Configuration

All settings are managed through .env. Every variable has a sensible default — only credentials are required.

Required

Variable Description
SERVICE_PASSWORD_WORDPRESS MariaDB WordPress user password
SERVICE_PASSWORD_ROOT MariaDB root password
SERVICE_PASSWORD_REDIS Redis password

Optional

Variable Default Description
SERVICE_USER_WORDPRESS wordpress MariaDB WordPress user
FLUSH_REDIS_ON_STARTUP false Flush Redis on container start (Apache variant)
DISABLE_WP_CRON false Disable WP-Cron; use external scheduler

Resource Limits

Variable Default (Nginx) Default (Apache) Description
WP_CPU_LIMIT 2.0 2.0 WordPress CPU limit
WP_MEMORY_LIMIT 1536M 2048M WordPress memory limit
WP_CPU_RESERVATION 0.5 0.5 WordPress CPU reservation
WP_MEMORY_RESERVATION 384M 512M WordPress memory reservation
DB_CPU_LIMIT 1.0 1.0 MariaDB CPU limit
DB_MEMORY_LIMIT 1024M 1024M MariaDB memory limit
DB_CPU_RESERVATION 0.25 0.25 MariaDB CPU reservation
DB_MEMORY_RESERVATION 256M 256M MariaDB memory reservation
REDIS_CPU_LIMIT 0.5 0.5 Redis CPU limit
REDIS_MEMORY_LIMIT 2048M 512M Redis max memory

MariaDB Tuning

Variable Default Description
DB_INNODB_BUFFER_POOL 512M InnoDB buffer pool size
DB_MAX_CONNECTIONS 500 Max concurrent connections
DB_MAX_ALLOWED_PACKET 64M Max packet size
DB_SLOW_QUERY_TIME 2 Slow query threshold (seconds)
DB_WAIT_TIMEOUT 300 Connection idle timeout (seconds)

Redis Tuning

Variable Default Description
REDIS_MAX_MEMORY_POLICY allkeys-lru Eviction policy

Logging

Variable Default Description
LOG_MAX_SIZE 10m Max log file size before rotation
LOG_MAX_FILES 3 Number of rotated log files to keep

Deployment

Coolify

  1. Create a new service, select Docker Compose
  2. Set the repository URL: https://github.com/artysan-code/wordpress-composing
  3. Configure environment variables in Coolify's env editor (match .env.example)
  4. Deploy — Coolify handles reverse proxy and TLS termination

The compose files use expose instead of ports, which is the Coolify-compatible pattern.

Manual (Docker Compose)

# Nginx (recommended)
docker compose up -d

# Apache
docker compose -f compose.apache.yaml up -d

# With dev tools
docker compose -f compose.yaml -f compose.dev.yaml up -d

WP-CLI

WP-CLI is pre-installed in both variants.

# List plugins
docker compose exec wordpress wp plugin list --allow-root

# Update WordPress core
docker compose exec wordpress wp core update --allow-root

# Search-replace URLs (after domain change)
docker compose exec wordpress wp search-replace 'old.com' 'new.com' --allow-root

# Redis cache management
docker compose exec wordpress wp redis status --allow-root
docker compose exec wordpress wp cache flush --allow-root

Development

Dev Overlay

Adds phpMyAdmin and exposes MariaDB on host port 3306:

docker compose -f compose.yaml -f compose.dev.yaml up -d

Common Commands

# Rebuild images
docker compose build

# View logs
docker compose logs -f wordpress
docker compose logs -f mariadb
docker compose logs -f redis

# Shell access
docker compose exec wordpress bash

Migration: Apache to Nginx

Switching from the Apache variant to Nginx is automatic. The entrypoint detects the migration and:

  1. Temporarily disables Redis object cache
  2. Flushes Redis (removes incompatible serialized data)
  3. Clears WordPress file caches (preserves Matomo)
  4. Re-configures Redis settings for igbinary
  5. Re-enables Redis object cache

No manual intervention required.


Project Structure

.
├── compose.yaml                 # Nginx variant (default, production)
├── compose.apache.yaml          # Apache variant (legacy)
├── compose.dev.yaml             # Dev overlay (phpMyAdmin + MariaDB port)
├── Dockerfile                   # Nginx + PHP-FPM image
├── Dockerfile.apache            # Apache mod_php image
├── docker-entrypoint.sh         # Nginx entrypoint
├── docker-entrypoint-apache.sh  # Apache entrypoint
├── .env.example                 # Environment variable template
├── config/
│   ├── nginx/                   # Nginx configuration
│   ├── php/                     # PHP-FPM and PHP ini
│   └── supervisor/              # Supervisor process manager
├── CONTRIBUTING.md              # Contribution guidelines
├── SECURITY.md                  # Security policy
└── CODE_OF_CONDUCT.md           # Community standards

Contributing

Contributions are welcome. Please read CONTRIBUTING.md for guidelines on reporting issues, submitting pull requests, and our development workflow.

For security vulnerabilities, see SECURITY.md.


License

This project is licensed under the GNU General Public License v3.0.

About

Production-grade Docker Compose deployment for WordPress with variants

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors