- Overview
- Reading Guide
- Typical Use Cases
- Development Quick Start
- Production Quick Start
- Configuration
- Operational Commands
- Architecture & Workflow
- Design Decisions
- Quality Gates and CI
- Extensibility
- Contributions
This repository provides a Docker Compose-based WordPress stack for both local development and production deployments, with a shared base, clear separation of concerns, and explicit operational workflows.
The project supports both development and production profiles, with script-driven automation for initialization, URL synchronization, and database backups.
- Quick local startup: Start from Development Quick Start.
- Production deployment: Continue with Production Quick Start.
- Architecture deep dive: Jump to Architecture & Workflow.
- Fresh WordPress setup: Start a new instance with
make up(development) ormake up-prod(production). - Import an existing WordPress project: Place a SQL dump in
db/init/, copy yourwp-contentassets intosrc/, and usemake db-restore SQL_FILE=<file.sql>when you need a manual restore. - Environment migration and URL synchronization:
wp-initreads the current site URL and runs a controlledwp search-replacetowardSITE_URL. - Continuous database safety:
db-backupcreates periodic SQL backups and applies FIFO retention.
- Docker
- Docker Compose
- GNU Make
Commands in this README assume a POSIX-like shell. On Windows, use Git Bash, WSL, or an equivalent environment that provides make and cp.
git clone https://github.com/taygumus/wp-docker-stack.git
cd wp-docker-stack
cp .env.example .envThe default values in .env.example are suitable for a first local run.
make up- WordPress:
http://<SERVER_NAME>:<HTTP_PORT>(default:http://localhost:8000) - phpMyAdmin (optional):
http://localhost:<PHPMYADMIN_PORT>(default:8001)
make downThe production profile is designed to run behind an external reverse proxy.
- Copy
.env.exampleto.env(if not already done). - Replace the example database credentials before any non-local deployment. Do not use the default
.env.examplesecrets in production. - Set
SERVER_NAMEto your public hostname. - Set
SITE_URLtohttps://<SERVER_NAME>.
Create the external proxy network (one-time operation):
docker network create proxyIf the network already exists, Docker reports it and you can continue.
make up-prodmake logs-prodIn your reverse proxy vhost for SERVER_NAME, forward traffic to the upstream alias wp-docker-stack-nginx on port 80 over Docker network proxy.
Example:
location / {
proxy_pass http://wp-docker-stack-nginx:80;
}For a ready-to-use Nginx + Certbot edge setup, see Nginx Docker Reverse Proxy.
make down-prodBaseline sizing for typical small deployments:
| Scenario | vCPU | RAM | Notes |
|---|---|---|---|
| Minimum | 1 | 1 GB | Suitable for low traffic and small datasets. |
| Recommended | 2 | 2 GB | Better headroom for MySQL activity and plugin/theme load. |
| Growth-ready | 2-4 | 4 GB+ | Better resilience for spikes and heavier workloads. |
Typical idle/light-load footprint:
- MySQL: around
350-600 MB(can increase with data size and query complexity) - WordPress PHP-FPM: around
80-200 MB - Nginx + sidecars: around
40-120 MB - Safety headroom: keep at least
20-30%free RAM
Runtime note: production resource values are declared in deploy.resources. Effective enforcement depends on your container runtime/orchestrator. Validate real usage on your host with docker stats --no-stream.
All behavior is configured through .env values.
| Variable | Description |
|---|---|
SERVER_NAME |
Public hostname/domain used by Nginx and URL generation. |
DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD |
MySQL app credentials used by WordPress and sidecars. |
DATABASE_ROOT_PASSWORD |
MySQL root password for administrative actions. |
SKIP_WP_INIT |
Skip wp-init tasks when set to true. |
SITE_URL |
Target WordPress URL used by synchronization. |
SKIP_COLUMNS |
Comma-separated columns excluded from wp search-replace (default: guid). |
SKIP_DB_BACKUP |
Disable periodic backup sidecar when set to true. |
DATABASE_BACKUP_INITIAL_DELAY |
Delay before first backup (s/m/h/d). |
DATABASE_BACKUP_INTERVAL |
Backup interval (s/m/h/d). |
DATABASE_BACKUP_MAX_FILES |
Maximum retained .sql files (FIFO). |
| Variable | Description |
|---|---|
HTTP_PORT |
Host port mapped to Nginx. |
PHPMYADMIN_PORT |
Host port for phpMyAdmin. |
Development profile also mounts:
./srcintowp-content./db/initinto MySQL init directory (processed automatically only when the database volume is initialized for the first time)./db/backupsfor host-visible backup files
| Variable | Description |
|---|---|
LOG_SIZE, LOG_FILES |
Container log rotation settings. |
DB_CPUS, DB_MEM_LIMIT |
Declared resource settings for MySQL. |
WP_CPUS, WP_MEM_LIMIT |
Declared resource settings for WordPress. |
NGINX_CPUS, NGINX_MEM_LIMIT |
Declared resource settings for Nginx. |
WP_INIT_CPUS, WP_INIT_MEM_LIMIT |
Declared resource settings for wp-init. |
DB_BACKUP_CPUS, DB_BACKUP_MEM_LIMIT |
Declared resource settings for db-backup. |
Production profile uses named volume db_backups and external network proxy.
Makefile is the main interface for day-to-day operations.
| Command | Description |
|---|---|
make up |
Build and start development services (docker-compose.yml + docker-compose.dev.yml). |
make down |
Stop development services. |
make clean |
Stop development services and remove volumes (destructive: deletes database and WordPress data). |
make reset |
Full reset (clean + up; destructive for persistent data). |
make logs |
Stream development logs. |
| Command | Description |
|---|---|
make up-prod |
Start production services (docker-compose.yml + docker-compose.prod.yml). |
make down-prod |
Stop production services. |
make logs-prod |
Stream production logs. |
| Command | Description |
|---|---|
make sync-site-url |
Trigger manual site URL synchronization. |
make db-backup |
Run one-off database backup. |
make db-restore SQL_FILE=<file.sql> |
Restore a dump from db/init/ (basename only). |
The stack is organized into presentation, application, data, and operations services.
flowchart LR
Base["docker-compose.yml (base)"] --> Dev["docker-compose.dev.yml (dev)"]
Base --> Prod["docker-compose.prod.yml (prod)"]
Solid arrows show runtime traffic and data dependencies. Dashed arrows show optional or operator-driven interactions.
flowchart LR
Visitor((Visitor))
Operator((Operator))
Proxy["External reverse proxy"]
ProxyNet["Network: proxy"]
subgraph Edge["Presentation layer"]
direction TB
Nginx[Nginx]
PMA["phpMyAdmin (dev only)"]
end
subgraph App["Application layer"]
direction TB
WP[WordPress]
end
subgraph Data["Data layer"]
direction TB
DB[(MySQL)]
V_DB[(db_data volume)]
V_WP[(wordpress volume)]
V_BKP["Backups (dev bind / prod volume)"]
end
subgraph Ops["Operations plane"]
direction TB
WP_INIT[wp-init]
DB_BACKUP[db-backup]
WP_CLI["wp-cli (dev only)"]
DB_CLI["db-cli (dev only)"]
end
Visitor -. dev .-> Nginx
Visitor -->|production| Proxy --> Nginx
Proxy -. attached .-> ProxyNet
Nginx -. attached in prod .-> ProxyNet
Nginx --> WP --> DB
DB --- V_DB
WP --- V_WP
WP_INIT --- V_WP
DB_BACKUP --- V_BKP
PMA -.-> DB
WP_CLI -.-> WP
DB_CLI -.-> DB
WP_INIT --> DB
DB_BACKUP --> DB
Operator -.-> PMA
Operator -.-> WP_CLI
Operator -.-> DB_CLI
Operator -.-> WP_INIT
Operator -.-> DB_BACKUP
| Service | Role | Profile |
|---|---|---|
| Nginx | Reverse proxy and static entrypoint to PHP-FPM. | Development + Production |
| WordPress | Application runtime (PHP-FPM). | Development + Production |
| Database | Persistent MySQL storage (db_data). |
Development + Production |
| wp-init | One-shot URL detection and synchronization. | Development + Production |
| db-backup | Periodic backup and FIFO rotation. | Development + Production |
| wp-cli | Stateless WordPress maintenance shell. | Development only |
| db-cli | Stateless MySQL maintenance shell. | Development only |
| phpMyAdmin | Optional DB web UI. | Development only |
- Explicit automation: initialization, migration, and backup behavior is script-driven and auditable.
- Layered composition: a shared base runtime is extended through development and production profiles.
- Operational sidecars: lifecycle tasks stay isolated from request-serving services.
- Production hardening: production profile adds logging policy, security options, and proxy-network integration.
The GitHub Actions workflow (.github/workflows/ci-lint.yml) validates:
- Shell scripts with
shellcheck - Compose files with
yamllint - Compose resolution for base + development profile via
docker compose config - Makefiles with
checkmake - Markdown files with
markdownlint
This structure supports incremental growth without rewriting the core stack:
- add new Compose overlays (for example, staging)
- add new operational sidecars under
scripts/ - integrate different reverse proxy frontends on network
proxy - extend backup retention and external backup destinations
Contributions are welcome. Please keep changes aligned with the layered architecture and ensure CI checks pass.