# 用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 不載入問題）：

```nginx
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 判斷（反向代理環境必須）：

```php
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` 正確寫入：

```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，確認狀態：

```bash
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 都在健康狀態。

# 📂 專案目錄結構

```bash
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`

```yaml
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`

```nginx
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`（流程文件）

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk--7"></div>```
# 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

```bash
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:

```nginx
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 健康運作

<div class="contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary" id="bkmrk--8"></div>