Docker-based self-hosted WordPress deployment system with: - Four-container stack (nginx, wordpress/php-fpm, mariadb, certbot) - Automatic SSL via Let's Encrypt with self-signed fallback - First-boot WordPress setup via WP-CLI (GeneratePress + child theme, plugins) - Interactive setup wizard and one-line install script - Backup, update, healthcheck, and SSL renewal scripts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Critical Rules
- NEVER build, test, or run anything locally. Always use Docker for all build/test/run operations.
websitebox-brief (2).mdis the source of truth for all project decisions and specifications. Always defer to it.- Do not load
websitebox-diagram (1).jsxunless the user explicitly asks for it — it is reference material only.
Project Overview
WebsiteBox is a Docker-based, self-hosted WordPress deployment system. Users provision a VPS, run an install script, and get a working SSL-secured WordPress portfolio site with docker compose up -d. The project prioritizes zero-trust transparency, minimal CLI interaction, and VPS-agnostic deployment.
Architecture
Four-container Docker Compose stack on a single Docker network (websitebox_internal):
- nginx (custom
nginx:alpine) — Reverse proxy, SSL termination via Let's Encrypt. Only container exposing ports (80, 443). Entrypoint auto-acquires SSL certs on first boot. - wordpress (custom
wordpress:php8.2-fpm-alpine) — PHP-FPM with WP-CLI bundled. First-boot entrypoint installs WordPress core, GeneratePress theme, child theme, and plugins (Age Gate, Wordfence, UpdraftPlus) via WP-CLI. - db (
mariadb:11) — Internal only, never exposed to host. - certbot (
certbot/certbot) — Renewal loop every 12h, signals nginx via shared volume file trigger (no Docker socket mount).
All persistent data uses bind mounts under ./websitebox-data/ (not named Docker volumes):
wordpress/,database/,certs/,certbot-webroot/,certbot-signal/,backups/
Key Design Decisions
- Restart policy:
restart: unless-stoppedon all containers (notalways). - SSL bootstrap: Handled entirely in nginx entrypoint — no separate ssl-init.sh script. Nginx detects missing certs, serves HTTP for ACME challenge, acquires certs, reloads with SSL.
- Certbot-to-nginx reload: Certbot writes trigger file to shared
certbot-signal/volume; nginx background loop detects and reloads. No Docker socket mounting. - WordPress setup: All via WP-CLI in container entrypoint, not the mu-plugin. Uses marker files (
.websitebox-setup-complete/.websitebox-setup-partial) for idempotency. - Backup path: UpdraftPlus writes to
/var/backups/websitebox(mapped to./websitebox-data/backups/), deliberately outside/var/www/htmlto avoid nested bind mount conflicts. - XML-RPC disable: In mu-plugin via
add_filter('xmlrpc_enabled', '__return_false'), NOT in wp-config.php (plugin API not loaded at wp-config time). - WP-CLI: Bundled in WordPress Docker image. Run commands as
www-data, never use--allow-root. - Table prefix:
wbox_(not defaultwp_). - wp-config: Sets
DISALLOW_FILE_EDIT(blocks theme/plugin editor) but NOTDISALLOW_FILE_MODS(would prevent plugin/theme updates via admin GUI).
User Flow
curl -fsSL <url>/install.sh | bash— installs Docker, clones repo, runs setup wizardsetup.sh— interactive wizard, generates.env, createswebsitebox-data/structure- User configures DNS A record
docker compose up -d— everything self-configures
File Structure Conventions
nginx/entrypoint.sh— SSL auto-bootstrap logicwordpress/entrypoint.sh— First-boot WP-CLI setup (theme/plugin install, site config)wordpress/wp-content/mu-plugins/websitebox-setup.php— Lightweight status checker + XML-RPC disable (not the installer)wordpress/wp-content/themes/websitebox/— Child theme (parent: GeneratePress)scripts/—backup.sh,update.sh,ssl-renew.sh,healthcheck.sh.env/.env.example— Configuration (gitignored / template)
Commands
# Start the stack
docker compose up -d
# Rebuild after Dockerfile changes
docker compose up -d --build
# View logs
docker compose logs -f [service]
# Restart a specific service
docker compose restart wordpress
# Re-run first-boot setup (force clean)
docker compose exec wordpress rm /var/www/html/.websitebox-setup-complete /var/www/html/.websitebox-setup-partial
docker compose restart wordpress
# Update WebsiteBox
./scripts/update.sh
# Manual backup
./scripts/backup.sh
Healthchecks
| Service | Check | Retries |
|---|---|---|
| nginx | curl -f http://localhost/nginx-health |
3 |
| wordpress | php-fpm-healthcheck |
3 |
| db | healthcheck --su-mysql --connect --innodb_initialized |
5 |
WordPress depends on db with condition: service_healthy.
Error Handling Patterns
- WordPress entrypoint: MariaDB retry loop (30 attempts, 2s apart). On partial failure, creates
.websitebox-setup-partialmarker and starts PHP-FPM anyway. All WP-CLI commands are idempotent — safe to re-run entire sequence. - nginx entrypoint: On SSL failure, serves HTTP placeholder explaining DNS isn't ready. User retries with
docker compose restart nginx. - install.sh: Uses
sg dockerfor immediate Docker group activation without logout/login.
Non-Goals (v1)
Payment processing, multi-site WordPress, automatic VPS provisioning, built-in CDN, email server (SMTP relay config only).