用Docker打造最佳效能的Wordpress環境
要做的事:Docker → WordPress 可用流程
1. 建立 Docker 架構
-
撰寫 docker-compose.yml,服務包含:
-
nginx:前端 Web server(只負責容器內請求,對外由主機 Proxy)
-
php (php-fpm):執行 WordPress/PHP
-
mariadb:資料庫,儲存 WordPress 資料
-
redis:Object Cache(提供效能優化)
-
wp-cli:命令列管理工具
-
cron:跑 WordPress 排程(wp-cron)
-
-
設定:
-
Nginx 靜態檔案快取、購物流程繞過快取。
-
PHP FPM(memory_limit、pm.max_children 等)依你的 4C/4G RAM 調整。
-
MariaDB 用 volume 儲存資料、utf8mb4 編碼。
-
Redis 用獨立 volume,內建設定。
-
2. 主機層 Proxy (Ubuntu Nginx)
-
主機 Nginx 反向代理 →
127.0.0.1:8080
(容器內 nginx)。 -
加入 SSL (Let’s Encrypt Certbot),伺服器區塊
server_name mysite.com;
。 -
解決 502 問題:確保容器內 nginx 配置正確、port 綁在 127.0.0.1:8080。
-
調整 proxy header,讓 WordPress 正確認定 HTTPS(解決 CSS/JS 不載入問題):
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
3. WordPress 部署
-
使用 wp-cli 下載 WordPress 核心:
docker compose exec wp-cli wp core download --allow-root
-
產生
wp-config.php
,連線資訊:-
DB 名稱:
wp
-
帳號:
wpuser
-
密碼:
change_me
-
Host:
db:3306
← 注意 Docker 要用服務名稱,不是 localhost
-
-
透過安裝頁完成初始化(語言、站名、管理員帳密)。
4. 修正容器問題
-
Nginx:
-
修掉
fastcgi_cache_path
放錯區塊(要在 http 層級,不是 server)。 -
最小化設定檔 → 先跑起站再逐步加快取規則。
-
-
cron:
-
原本 Restarting → 新增
cron/wp-cron.sh
腳本,正確執行排程。
-
-
wp-config.php:
-
修正 Redis 設定錯誤(
define('WP_REDIS_HOST', redis );
→define('WP_REDIS_HOST', 'redis');
) -
加入 HTTPS 判斷(反向代理環境必須):
-
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
5. Redis 整合
-
確認 Redis 容器正常:
docker compose exec redis redis-cli ping → PONG
。 -
在
wp-config.php
正確寫入:
define( 'WP_REDIS_HOST', 'redis' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 1 );
-
重啟 PHP 讓 opcache 重載:
docker compose restart php
。 -
使用
wp-cli
啟用 Redis drop-in,確認狀態:
docker compose exec wp-cli wp redis enable --allow-root
docker compose exec wp-cli wp redis status --allow-root
6. WordPress 網站成功啟動
-
首頁、安裝頁能正常載入 CSS/JS(不再 502、不再純文字)。
-
安裝流程完成,能登入後台。
-
Redis Object Cache 狀態正常,顯示連到
redis:6379
。 -
主機 Proxy + 容器 Nginx + PHP-FPM + DB 都在健康狀態。
📂 專案目錄結構
wordpress-perf/
├── docker-compose.yml
├── .env
├── nginx/
│ └── conf.d/
│ ├── wordpress.conf
│ └── 00-cache.conf
├── cron/
│ └── wp-cron.sh
└── README.md
⚙️ 檔案內容
1. .env
HTTP_PORT=8080
DB_NAME=wp
DB_USER=wpuser
DB_PASSWORD=change_me
DB_ROOT_PASSWORD=change_root
# PHP 記憶體/子程序調整(4C/4G RAM)
PHP_MEMORY_LIMIT=256M
PHP_MAX_CHILDREN=10
PHP_MAX_REQUESTS=400
2. docker-compose.yml
services:
nginx:
image: nginx:1.25-alpine
restart: always
ports:
- "127.0.0.1:${HTTP_PORT:-8080}:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- wp_data:/var/www/html:ro
- cache_data:/var/cache/nginx
depends_on:
- php
php:
build:
context: .
dockerfile_inline: |
FROM wordpress:php8.3-fpm-alpine
RUN docker-php-ext-install mysqli pdo pdo_mysql
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
restart: always
environment:
WORDPRESS_DB_NAME: ${DB_NAME}
WORDPRESS_DB_USER: ${DB_USER}
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_HOST: db:3306
PHP_MEMORY_LIMIT: ${PHP_MEMORY_LIMIT}
volumes:
- wp_data:/var/www/html
db:
image: mariadb:10.11
restart: always
environment:
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
redis:
image: redis:7-alpine
restart: always
volumes:
- redis_data:/data
wp-cli:
image: wordpress:cli-php8.3
depends_on:
- php
- db
volumes:
- wp_data:/var/www/html
entrypoint: wp
cron:
image: wordpress:cli-php8.3
depends_on:
- php
volumes:
- wp_data:/var/www/html
- ./cron:/cron
entrypoint: /cron/wp-cron.sh
volumes:
wp_data:
db_data:
redis_data:
cache_data:
4. nginx/conf.d/wordpress.conf
server {
listen 80 default_server;
server_name _;
root /var/www/html;
index index.php index.html;
# 跳過快取條件
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in_|woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session_") { set $skip_cache 1; }
if ($request_uri ~* "^/(wp-admin/|wp-login\.php|cart|checkout|my-account|wc-api/|wp-json/)") { set $skip_cache 1; }
if ($request_uri ~* "add-to-cart=") { set $skip_cache 1; }
if ($request_uri ~* "admin-ajax\.php") { set $skip_cache 1; }
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass php:9000;
fastcgi_buffers 16 32k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 120s;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache PHP_MICRO;
fastcgi_cache_valid 200 301 302 5s;
add_header X-Cache $upstream_cache_status;
fastcgi_param HTTPS on;
fastcgi_param SERVER_PORT 443;
fastcgi_param HTTP_X_FORWARDED_PROTO https;
}
location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|webp|ico|ttf|otf|woff|woff2)$ {
expires 30d;
access_log off;
}
client_max_body_size 64m;
}
5. cron/wp-cron.sh
#!/bin/sh
set -e
# 每 1 分鐘跑一次 wp-cron
while true; do
wp --path=/var/www/html --allow-root cron event run --due-now >/dev/null 2>&1 || true
sleep 60
done
6. README.md
(流程文件)
# WordPress on Docker (WooCommerce Ready)
## Services
- Nginx 1.25 (reverse proxy inside container)
- PHP-FPM 8.3 (with mysqli, pdo_mysql)
- MariaDB 10.11
- Redis 7 (object cache)
- WP-CLI
- Cron container for wp-cron
## Usage
```bash
docker compose up -d
WordPress setup
docker compose exec wp-cli wp core download --allow-root
docker compose exec wp-cli wp config create \
--dbname=$DB_NAME --dbuser=$DB_USER --dbpass=$DB_PASSWORD \
--dbhost=db:3306 --skip-check --allow-root
docker compose exec wp-cli wp core install \
--url="https://mysite.com" \
--title="MySite Shop" \
--admin_user="admin" \
--admin_password="ChangeMe123!" \
--admin_email="you@example.com" \
--allow-root
Redis setup
docker compose exec wp-cli wp config set WP_REDIS_HOST 'redis' --allow-root
docker compose exec wp-cli wp config set WP_REDIS_PORT 6379 --raw --allow-root
docker compose exec wp-cli wp config set WP_REDIS_DATABASE 1 --raw --allow-root
docker compose restart php
docker compose exec wp-cli wp redis enable --allow-root
Proxy setup (Host machine)
Nginx reverse proxy:
server {
listen 443 ssl http2;
server_name mysite.com;
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
Result
-
WordPress 正常啟動 (
https://mysite.com
) -
Redis Object Cache 啟用
-
Nginx + PHP-FPM + MariaDB + Redis 健康運作