前言

你正在看的這個部落格,是我用 Docker + WordPress 自己架的。沒有用 Blogger、沒有用 Medium、沒有用任何託管服務。

為什麼要自己架?因為掌控感。身為後端工程師,我想要完全控制自己的網站——從佈景主題到資料庫,從部署流程到備份策略。而且老實說,把整套系統容器化的過程本身就很好玩。

這篇文章會完整分享本站的技術架構,包含 Docker Compose 設定、WordPress 配置、自訂主題整合,以及自動化 setup 腳本的設計。如果你也想自架部落格,這篇可以當作參考。


為什麼選擇 Docker + WordPress?

為什麼是 Docker?

  1. 環境一致性:不管在哪台機器上,docker compose up 就能跑起一模一樣的環境
  2. 乾淨隔離:WordPress、MySQL 各自在自己的容器裡,不會污染主機環境
  3. 版本管理:整個部署設定都在 docker-compose.yml 裡,可以 git 追蹤
  4. 易於備份遷移:打包 volume 就能搬到另一台機器

為什麼是 WordPress?

你可能會問:後端工程師為什麼不用 Hugo、Gatsby、Hexo 這些靜態網站生成器?

原因有幾個:

  • WordPress 的編輯體驗真的很好,尤其是需要上傳圖片、嵌入程式碼時
  • 我想要動態功能:搜尋、分類、標籤、留言
  • 自訂主題的自由度很高,而且 PHP 其實寫起來不難(對後端工程師來說)
  • WordPress 有龐大的外掛生態系,需要什麼功能通常都找得到

Docker Compose 設定

本站的核心就是一個 docker-compose.yml,定義了兩個服務:

version: '3.8'

services: db: image: mysql:8.0 container_name: blog-db restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpass123} MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: ${DB_PASSWORD:-wppass123} volumes: - db_data:/var/lib/mysql networks: - blog-network healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5

wordpress: image: wordpress:php8.2-apache container_name: blog-wordpress restart: unless-stopped depends_on: db: condition: service_healthy ports: - "8080:80" environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_NAME: wordpress WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: ${DB_PASSWORD:-wppass123} WORDPRESS_TABLE_PREFIX: wp_ volumes: - wp_data:/var/www/html - ./wp-content/themes/techblog:/var/www/html/wp-content/themes/techblog networks: - blog-network

volumes: db_data: wp_data:

networks: blog-network: driver: bridge

幾個設計要點

1. 健康檢查(healthcheck)

MySQL 容器啟動後需要一點時間初始化。加上 healthcheck,WordPress 容器會等到 MySQL 真正準備好才啟動,避免連線錯誤。

2. Volume 掛載策略

我用了兩種 volume:

  • Named volumedb_data, wp_data):由 Docker 管理,資料持久化
  • Bind mount./wp-content/themes/techblog):把本機的主題目錄直接掛進容器,這樣修改主題檔案會即時反映,不需要重建容器
本機檔案系統                           容器內
├── docker-compose.yml
├── setup.sh
└── wp-content/
    └── themes/
        └── techblog/     ←→    /var/www/html/wp-content/themes/techblog/
            ├── style.css
            ├── functions.php
            ├── front-page.php
            └── ...

3. 環境變數

密碼用環境變數帶入,有預設值但可以覆蓋。正式環境記得用 .env 檔或 Docker secrets 來管理敏感資訊。


自動化 Setup 腳本

每次重新部署,都要手動設定 WordPress 很麻煩。所以我寫了一個 setup.sh,把整個初始化流程自動化:

#!/bin/bash
set -e

echo "=== 啟動 Docker 容器 ===" docker compose up -d

echo "=== 等待 WordPress 準備就緒 ===" until docker exec blog-wordpress curl -sf http://localhost > /dev/null 2>&1; do echo " 等待中..." sleep 3 done

echo "=== 安裝 WP-CLI ===" docker exec blog-wordpress bash -c ' if ! command -v wp &> /dev/null; then curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar mv wp-cli.phar /usr/local/bin/wp fi '

echo "=== 安裝 WordPress ===" docker exec blog-wordpress wp core install \ --url="https://shnovaj.com" \ --title="Tech Blog" \ --admin_user=admin \ --admin_password=admin123 \ --admin_email=admin@example.com \ --allow-root

echo "=== 啟用自訂主題 ===" docker exec blog-wordpress wp theme activate techblog --allow-root

echo "=== 設定永久連結 ===" docker exec blog-wordpress wp rewrite structure '/%postname%/' --allow-root

echo "=== 建立分類 ===" CATEGORIES=("後端技術" "程式藝術" "創客" "數學" "心得分享") for cat in "${CATEGORIES[@]}"; do docker exec blog-wordpress wp term create category "$cat" --allow-root 2>/dev/null || true done

echo "=== 建立範例文章 ===" docker exec blog-wordpress wp post create \ --post_title="Hello World — 歡迎來到我的技術部落格" \ --post_content="這是第一篇文章。" \ --post_status=publish \ --post_category=1 \ --allow-root

echo "=== 設定完成!===" echo "前台:https://shnovaj.com" echo "後台:https://shnovaj.com/wp-admin" echo "帳號:admin / admin123"

WP-CLI 的威力

setup.sh 的核心是 WP-CLI——WordPress 的命令列工具。有了它,所有原本需要在網頁後台操作的事情,都可以用指令完成:

# 安裝外掛
wp plugin install classic-editor --activate --allow-root

# 匯入文章 wp post create --post_title="文章標題" --post_content="$(cat article.html)" --allow-root

# 匯出資料 wp db export backup.sql --allow-root

# 更新 WordPress wp core update --allow-root

這代表整個部落格的設定是可重現的——刪掉所有 Docker volume,重新跑 setup.sh,就能得到一模一樣的部落格。這對後端工程師來說,是不是很有安全感?


自訂主題的整合

本站使用自訂的 techblog 主題,設計理念是模仿 iquilezles.org 的暗色極簡風格。

主題的目錄結構:

techblog/
├── style.css          # 主樣式表(含主題資訊)
├── functions.php      # 主題功能設定
├── front-page.php     # 首頁模板
├── single.php         # 單篇文章模板
├── archive.php        # 分類/標籤列表
├── page.php           # 靜態頁面
├── search.php         # 搜尋結果
├── 404.php            # 404 頁面
├── header.php         # 頁首
├── footer.php         # 頁尾
├── sidebar.php        # 側邊欄
└── screenshot.png     # 主題預覽圖

因為主題目錄是用 bind mount 掛進容器的,所以我在本機用編輯器修改 PHP/CSS 檔案,瀏覽器重新整理就能看到效果,開發體驗非常順暢。


備份策略

自架部落格最怕的就是資料遺失。我的備份策略很簡單:

資料庫備份

#!/bin/bash
# backup.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="./backups"
mkdir -p "$BACKUP_DIR"

# 匯出資料庫 docker exec blog-db mysqldump \ -u wordpress \ -pwppass123 \ wordpress > "$BACKUP_DIR/db_$DATE.sql"

# 備份上傳的媒體檔 docker cp blog-wordpress:/var/www/html/wp-content/uploads \ "$BACKUP_DIR/uploads_$DATE"

echo "備份完成:$BACKUP_DIR"

還原

# 還原資料庫
docker exec -i blog-db mysql \
  -u wordpress \
  -pwppass123 \
  wordpress < backups/db_20260225_120000.sql

# 還原媒體檔 docker cp backups/uploads_20260225_120000/uploads \ blog-wordpress:/var/www/html/wp-content/


開發環境 vs 正式環境

目前本站是跑在本機的開發環境。如果要搬到正式環境,需要加上:

1. Nginx 反向代理 + SSL

# 正式環境增加 nginx 服務
nginx:
  image: nginx:alpine
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./nginx.conf:/etc/nginx/conf.d/default.conf
    - ./certbot/conf:/etc/letsencrypt
  depends_on:
    - wordpress

2. Let’s Encrypt 自動續約

# 用 certbot 取得免費 SSL 憑證
docker run --rm -v ./certbot/conf:/etc/letsencrypt \
  -v ./certbot/www:/var/www/certbot \
  certbot/certbot certonly --webroot \
  -w /var/www/certbot -d yourdomain.com

3. 資源限制

# 限制容器的記憶體和 CPU 使用
wordpress:
  deploy:
    resources:
      limits:
        memory: 512M
        cpus: '1.0'

遇到的坑與解法

坑 1:檔案權限問題

WordPress 容器內的 Apache 用 www-data 用戶執行,但 bind mount 的檔案可能是你本機用戶的權限。

# 解法:調整容器內的權限
docker exec blog-wordpress chown -R www-data:www-data \
  /var/www/html/wp-content/themes/techblog

坑 2:WordPress 生成錯誤的 URL

如果你的 WORDPRESS_CONFIG_EXTRA 沒設好,WordPress 可能會把 URL 寫死成 http://localhost

environment:
  WORDPRESS_CONFIG_EXTRA: |
    define('WP_HOME', 'https://shnovaj.com');
    define('WP_SITEURL', 'https://shnovaj.com');

坑 3:MySQL 初始化太慢

有時候 MySQL 第一次啟動要很久(特別是在 ARM Mac 上),WordPress 容器會因為連不上資料庫而失敗。這就是為什麼 healthcheck + depends_on.condition 很重要。


小結

用 Docker 自架部落格的好處是完全的掌控權可重現的部署流程。壞處是你要自己處理所有的維運工作——備份、安全性更新、SSL、效能調校。

對後端工程師來說,這些其實都是日常工作會接觸到的東西,做起來不會太陌生。而且在自己的部落格上實踐這些技術,本身就是一種很好的學習方式。

如果你決定要自架,我的建議是:先在本機用 Docker 跑起來,等內容累積到一定程度、確認有持續寫作的動力後,再考慮搬到 VPS 上。過早租伺服器,最後可能只是多花錢養蚊子。

延伸閱讀