Background
Preparing for Your WordPress Docker Migration
For years, hosting a personal WordPress site meant relying on traditional Virtualmin or cPanel web hosting environments on VPS providers like Hostinger. While control panels get the job done, managing multi-tenant setups comes with non-trivial overhead: configuring complex PHP-FPM pools, tweaking FastCGI sockets, wrestling with Unix file permissions (www-data vs user accounts), and paying recurring monthly VPS bills for modest hardware allocations.
When upgrading home hardware to an Apple Silicon Mac mini, an intriguing question emerges: Why pay for a remote VPS when a silent, low-power Unix powerhouse sits right on the desk?
Moving to a self-hosted Docker architecture on local hardware promised full root control, instant local database backups, and massive performance headroom.
Issue
Self-hosting a public website from a home network introduces immediate technical hurdles:
- ISP Restrictions & Dynamic IPs: Most residential Internet Service Providers (ISPs) block inbound ports 80 and 443 entirely and assign dynamic public IP addresses that change periodically.
- Security & Exposure: Opening router ports directly to the internet exposes a home network to relentless port scans, brute-force SSH attacks, and DDoS attempts.
- Reverse Proxy & Mixed Content: Running WordPress behind a local Docker bridge network (localhost: 8080) often breaks URL routing, leading to broken CSS/JS assets, missing icons, and media hardcoded to http:// instead of https://
- Firewall & Proxy Misidentification: Security plugins like All-In-One-Security (AIOS) evalute incoming visitor traffic. When sitting behind a proxy, the firewall mistakes the proxy server’s IP for the visitor’s IP, triggering false positives that can lock out legitimate administration or ban the proxy itself.
Solution
To solve these challenges without opening a single inbound router port, the architecture relies on a Cloudflare Tunnel (cloudflared) paired with a containerised Docker stack:
- Cloudflare Tunnel (cloudflared): An outbound-only daemon running on local machine connects directly to Cloudflare’s edge network. Cloudflare handles SSL termination (https://larrylai.com), DDoS mitigation, and edge caching. No port forwarding on the router is needed.
- WordPress Proxy Awareness: Hardcoding proxy headers inside wp-config.php forces WordPress to recognise HTTPS and pass generated canonical URLs back through Cloudflare seamlessly.
- Real IP Forwarding: Configuring the AIOS security plugin to use the HTTP_CF_CONNECTING_IP header ensures the Web Application Firewall (WAF) inspects the actual visitor’s IP address rather than Cloudflare’s edge proxy addresses.
- Cache Purging: Re-saving plugin project assets (such as Smart Slider 3) regenerates static CSS and JavaScript stylesheets with updated https://larrylai.com domain paths.
Configuration
- docker-compose.yml
Here is the core declarative stack orchestrating WordPress (PHP 8.3 + Apache) and MariaDB with persistent volume mounts:
version: '3.8'
services:
db:
image: mariadb:10.11
container_name: larrylai-wp-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
wordpress:
image: wordpress:php8.3-apache
container_name: larrylai-wp
restart: always
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
volumes:
- ./src:/var/www/html
volumes:
db_data:
2. Reverse Proxy Config in wp-config.php
To ensure WordPress correctly detects HTTPS through the Cloudflare Tunnel, place this block at the top of wp-config.php:
// Force HTTPS recognition behind Cloudflare reverse proxy
if (asset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
define('WP_HOME', 'https://larrylai.com');
define('WP_SITEURL', 'https://larrylai.com');
Tricks
- Container Simplicity vs Virtualmin Overhead:
Coming from Virtualmin or cPanel, Docker feels like a superpower. Because a Docker container single-tenant by design, no multi-user PHP-FPM socket permission nightmares of traditional panels. The web server and PHP run cleanly underwww-datainside their isolated environment. - Multi-Layer Security Architecture:
- Layer 1 (Cloudflare Edge): Blocks bad bots and filters malicious web traffic before it reaches the home router.
- Layer 2 (AIOS Bootstrap WAF): Evaluates incoming requests at PHP initialisation on PHP 8.3 before WordPress or MariaDB even boot up, saving CPU and RAM.
- Layer 3 (Clean Core): Comments disabled, inactive plugins deleted, and anti-spam bloat eliminated.
- The 5-Second Local Database Backup:
While automated cloud backups (via UpdraftPlus to Google Drive) handle offsite recovery, Docker makes pre-update snapshots instantaneous. Running a quick CLI database dump before updating core themes takes under five seconds.
