Skip to content

InstaZDLL/wp-splendid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WordPress Splendid logo

WordPress Splendid

wp-splendid

License: WTFPL WordPress PHP Nginx MySQL Redis Docker Podman

Self-hosted WordPress stack defined entirely as a Compose deployment (Docker or Podman).
No application code lives in this repository — only the infrastructure configuration.


Architecture

Internet → Nginx :8080 → PHP-FPM (wordpress:9000)
                              ↓              ↓
                           MySQL          Redis
Service Image Role
nginx nginx:alpine Reverse proxy, static cache, security headers, rate-limit
wordpress wordpress:php8.5-fpm PHP-FPM only, never exposed to the host
db mysql:lts Database, persisted under ./db/
redis redis:8-alpine WordPress object cache (W3TC / Redis Object Cache)

All services share the internal bridge network wp-network. Redis and MySQL are not published on the host.


Requirements

On Fedora / RHEL with SELinux enforcing, the volume mounts in docker-compose.yml use :Z / :z labels — no extra configuration is required.


Quick start

# 1. Clone and enter the directory
git clone <url> wp-splendid && cd wp-splendid

# 2. Create the secrets file
cp .env.example .env

# Generate WordPress salts:
curl -s https://api.wordpress.org/secret-key/1.1/salt/

# Generate strong passwords:
openssl rand -base64 32

# 3. Start the stack
podman compose up -d        # or: docker compose up -d

# 4. Open the site
xdg-open http://localhost:8080

Common commands

# Tail logs
podman compose logs -f nginx wordpress db redis

# Validate Compose syntax
podman compose config --quiet

# Shell into the WordPress container (for wp-cli, etc.)
podman compose exec wordpress bash

# MySQL client
podman compose exec db mysql -u${DB_USER} -p ${DB_NAME}

# Stop without deleting data
podman compose down

# Wipe all runtime data (DB, Redis snapshot, WordPress files)
scripts/cleanup.sh
scripts/cleanup.sh -y     # skip confirmation

Repository layout

.
├── docker-compose.yml       # Service definitions, hardening, healthchecks
├── .env.example             # Environment variable template
├── assets/
│   └── logo.svg             # Project logo
├── scripts/
│   └── cleanup.sh           # Stop the stack and wipe runtime data
├── nginx/
│   ├── default.conf         # Virtual host (FastCGI, W3TC cache, rate-limit)
│   └── header.conf          # Security headers (CSP, HSTS, frame, …)
├── db/
│   └── my.cnf               # InnoDB / MySQL tuning
├── redis-data/              # Persisted Redis data (gitignored)
└── wordpress/               # WordPress core + plugins + themes (gitignored)

Configuration

Environment variables (.env)

Variable Description
DB_NAME MySQL database name
DB_USER MySQL user
DB_PASSWORD MySQL password (plaintext, hashed by MySQL)
REDIS_PASSWORD Redis requirepass value
WP_*_KEY / WP_*_SALT 8 WordPress salts (see .env.example)

Never commit .env — it is excluded via .gitignore.

Page cache (W3 Total Cache)

Nginx looks for pre-rendered HTML files under wp-content/cache/page_enhanced/ before passing the request to PHP. The cache is bypassed for:

  • POST requests
  • non-empty query strings
  • WordPress / WooCommerce / comment-author cookies

If you swap caching plugins, update the try_files chain in nginx/default.conf.

Security headers

nginx/header.conf is mounted at /etc/nginx/conf.d/headers.conf and re-included in every location block. It provides:

  • X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, X-Download-Options, X-Permitted-Cross-Domain-Policies
  • Content-Security-Policy (tighten per your third-party domains)
  • HSTS — commented; enable only after HTTPS is in place
  • CORS — disabled by default; enable per origin if needed

SSL / HTTPS

Not configured in this repository (HTTP only on port 8080). To enable HTTPS:

  1. Mount your certificates into the nginx container.
  2. Add a listen 443 ssl http2 block in nginx/default.conf.
  3. Uncomment the Strict-Transport-Security header in nginx/header.conf.
  4. Review the CSP if you add third-party HTTPS resources.

Hardening

Container layer:

  • security_opt: no-new-privileges:true on every service
  • cap_drop: [ALL] on nginx and redis, with a minimal cap_add set
  • mem_limit set per service (nginx 128m, wordpress 512m, db 1g, redis 256m)
  • logging driver capped at 10 MB × 3 files to prevent log-disk fill
  • Healthchecks on db and redis; wordpress waits on both via service_healthy

Application layer:

  • PHP-FPM never exposed to the host — only reachable by Nginx on the internal network
  • Redis and MySQL never exposed to the host
  • wp-login.php rate-limited to 5 req/min per IP (burst 3)
  • Author enumeration blocked (?author=N → 403)
  • xmlrpc.php and wp-config.php denied at the Nginx layer
  • server_tokens off — Nginx version hidden
  • MYSQL_RANDOM_ROOT_PASSWORD=1 — root password is randomized and unknown
  • WordPress hardening constants: DISALLOW_FILE_EDIT, WP_AUTO_UPDATE_CORE='minor', WP_DEBUG=false, WP_DEBUG_DISPLAY=false

License

This project is released under the WTFPL — see LICENSE. Do What The Fuck You Want To Public License.

About

Self-hosted, hardened WordPress stack on Docker/Podman Compose — Nginx, PHP-FPM, MySQL, Redis object cache, healthchecks, SELinux-friendly.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages