目錄

用 Docker Compose 架設 WordPress(含 MySQL)

這一節用 Docker Compose 架一個完整的 WordPress 網站。完成後你會有:

  • WordPress(PHP + Apache,官方映像檔 wordpress:7-apache)
  • MySQL 8.4 資料庫,加上健康檢查,資料庫準備好才啟動 WordPress
  • 資料保存在 Volume,容器刪掉重建也不會遺失
  • 上傳檔案大小調整為 64 MB
  • 需要時才啟動的 phpMyAdmin 管理介面

本節用到的 Compose 語法(depends_on、healthcheck、profiles、.env)在 Compose 入門 與 docker compose 指令 都介紹過。

建立專案

建立一個資料夾 wordpress,放入三個檔案:

wordpress/
├── compose.yaml
├── .env
└── uploads.ini

.env:密碼

MYSQL_ROOT_PASSWORD=change-this-root-password
MYSQL_PASSWORD=change-this-wp-password

請換成你自己的密碼。Compose 會自動讀取同資料夾的 .env,把 compose.yaml 中的 ${MYSQL_PASSWORD} 等變數換成這裡的值。.env 不要提交到 Git。

uploads.ini:上傳大小

WordPress 映像檔預設的 PHP 上傳上限只有 2 MB,上傳稍大的圖片或佈景主題就會失敗。用一個 ini 檔覆寫:

upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M

官方 PHP 映像檔會讀取 /usr/local/etc/php/conf.d/ 下所有的 .ini,所以只要把這個檔案掛載進去即可。

compose.yaml

services:
  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s
    restart: unless-stopped

  wordpress:
    image: wordpress:7-apache
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - wp_data:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
    restart: unless-stopped

  phpmyadmin:
    image: phpmyadmin
    depends_on:
      - db
    ports:
      - "127.0.0.1:8081:80"
    environment:
      PMA_HOST: db
    profiles: [tools]

volumes:
  db_data:
  wp_data:

重點說明:

設定作用
MYSQL_DATABASE / MYSQL_USER / MYSQL_PASSWORDMySQL 映像檔第一次啟動(資料夾是空的)時,自動建立資料庫與帳號
healthcheck每 10 秒用 mysqladmin ping 檢查 MySQL 是否可以連線。要寫 -h 127.0.0.1 而不是 localhost,否則初始化還沒完成就會被判定為 healthy,原因見 MySQL
condition: service_healthy等 db 變成 healthy 才啟動 WordPress,避免 WordPress 先起來卻連不到資料庫
WORDPRESS_DB_HOST: db用服務名稱 db 連線,Compose 的網路會自動解析成 MySQL 容器的 IP
db_data:/var/lib/mysqlMySQL 的資料存在 Volume
wp_data:/var/www/htmlWordPress 程式、外掛、佈景主題、上傳的媒體存在 Volume
profiles: [tools]phpMyAdmin 預設不啟動,加 --profile tools 才會啟動
127.0.0.1:8081:80phpMyAdmin 只綁定本機,外部連不到

MySQL 只在 Compose 內部網路中被 WordPress 存取,所以 db 沒有設定 ports,不對外開放。

啟動

在 wordpress 資料夾中執行:

$ docker compose up -d
[+] up 32/32
 ✔ Image wordpress:7-apache        Pulled                                                                                                        28.0s
 ✔ Network wordpress_default       Created                                                                                                        0.1s
 ✔ Volume wordpress_db_data        Created                                                                                                        0.0s
 ✔ Volume wordpress_wp_data        Created                                                                                                        0.0s
 ✔ Container wordpress-db-1        Healthy                                                                                                       11.1s
 ✔ Container wordpress-wordpress-1 Started                                                                                                       11.3s

注意順序:Compose 先建立網路與兩個 Volume,wordpress-db-1 等了 11 秒變成 Healthy 之後,wordpress-wordpress-1 才 Started,這就是 condition: service_healthy 的效果。第一次執行需要下載映像檔,時間會比較久。

確認狀態:

$ docker compose ps
NAME                    IMAGE                COMMAND                  SERVICE     CREATED          STATUS                    PORTS
wordpress-db-1          mysql:8.4            "docker-entrypoint.s…"   db          16 seconds ago   Up 16 seconds (healthy)   3306/tcp, 33060/tcp
wordpress-wordpress-1   wordpress:7-apache   "docker-entrypoint.s…"   wordpress   16 seconds ago   Up 5 seconds              0.0.0.0:8080->80/tcp, [::]:8080->80/tcp

db 的 STATUS 顯示 (healthy),而且 PORTS 只有容器內部的 3306/tcp,沒有對應到主機;wordpress 則把主機的 8080 對應到容器的 80。

兩個 Volume 也建立好了,名稱是「專案名稱_Volume 名稱」,專案名稱預設就是資料夾名稱:

$ docker volume ls
DRIVER    VOLUME NAME
local     wordpress_db_data
local     wordpress_wp_data

在瀏覽器完成安裝

打開 http://localhost:8080,第一個畫面是選擇語言。清單中選擇你要的後台語言(例如「繁體中文」),按 Continue。本教學的截圖為了與其他語言版本共用,選擇的是 English (United States)。

接著填寫網站資訊:

WordPress 安裝畫面:填寫網站標題、使用者名稱、密碼與 Email
安裝畫面:Site Title、Username、Password、Your Email,最後按 Install WordPress
欄位說明
Site Title網站標題,之後可在後台修改
Username管理員帳號。不要用 admin,容易被猜中(本範例只是示範)
Password管理員密碼,WordPress 會顯示強度
Your Email管理員 Email,用來接收通知與重設密碼
Search engine visibility勾選後會要求搜尋引擎不要收錄,網站還在測試時可以勾選

這裡不會出現資料庫設定畫面,因為 WORDPRESS_DB_* 環境變數已經提供了連線資訊,映像檔啟動時自動產生了 wp-config.php。

按 Install WordPress 後,看到 Success 畫面就表示安裝完成:

WordPress 安裝成功畫面
安裝成功,按 Log In 前往登入頁
WordPress 登入頁面
登入頁 http://localhost:8080/wp-login.php

用剛才設定的帳號密碼登入,就會進入管理後台。歡迎區塊顯示的版本是 7.1.3,也就是 wordpress:7-apache 映像檔當下內含的版本:

WordPress 7.1.3 管理後台
WordPress 管理後台(Dashboard)

前台 http://localhost:8080 已經可以看到預設的 Hello world! 文章:

WordPress 網站前台
網站前台(登入狀態下頂端會有管理工具列)

確認上傳大小限制

到後台 Media → Add Media File,上傳區下方會顯示目前的上限。顯示 Maximum upload file size: 64 MB,表示 uploads.ini 有生效:

WordPress 上傳頁面顯示 Maximum upload file size: 64 MB
Upload Media 頁面下方顯示上限 64 MB

如果還是 2 MB,檢查 uploads.ini 的掛載路徑是否正確,修改後要用 docker compose up -d 重建容器(restart 不會套用新設定)。

用 phpMyAdmin 管理資料庫(選用)

phpMyAdmin 設定了 profiles: [tools],平常的 docker compose up -d 不會啟動它。需要時加上 --profile tools:

$ docker compose --profile tools up -d phpmyadmin
[+] up 25/25
 ✔ Image phpmyadmin                 Pulled                                                      6.3s
 ✔ Container wordpress-db-1         Running                                                     0.0s
 ✔ Container wordpress-phpmyadmin-1 Started                                                     0.6s

指令最後指定 phpmyadmin,只啟動它(以及它依賴的 db,已經在執行中)。

打開 http://localhost:8081,Username 填 root,Password 填 .env 中的 MYSQL_ROOT_PASSWORD:

phpMyAdmin 登入畫面
phpMyAdmin 登入畫面,伺服器已由 PMA_HOST 指定為 db

登入後點左側的 wordpress 資料庫,就能看到 WordPress 建立的 12 個資料表(wp_posts、wp_options、wp_users…):

phpMyAdmin 顯示 wordpress 資料庫的資料表
wordpress 資料庫的資料表

phpMyAdmin 的連接埠綁定在 127.0.0.1,只有本機能連。如果是遠端伺服器,請用 SSH 通道(ssh -L 8081:127.0.0.1:8081 使用者@伺服器)連線,不要把它開放到公網。用完可以只停掉它:

docker compose --profile tools stop phpmyadmin

資料存在哪裡

Volume內容
wordpress_db_dataMySQL 資料庫:文章、頁面、設定、使用者、留言
wordpress_wp_dataWordPress 程式、佈景主題、外掛、上傳的媒體(wp-content/uploads)

兩個都要備份,缺一個都無法完整還原網站。

備份

在專案資料夾中執行。第一步用 mysqldump 匯出資料庫,第二步用一個臨時的 alpine 容器把 wp_data Volume 打包成 tar.gz:

$ docker compose exec -T db sh -c 'mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" wordpress' > wordpress.sql
mysqldump: [Warning] Using a password on the command line interface can be insecure.

$ ls -lh wordpress.sql && head -n 5 wordpress.sql
-rw-r--r--    1 root     root      880.8K Oct 13 00:22 wordpress.sql
-- MySQL dump 10.13  Distrib 8.4.11, for Linux (x86_64)
--
-- Host: localhost    Database: wordpress
-- ------------------------------------------------------
-- Server version	8.4.11

$ docker run --rm -v "$(basename "$PWD")_wp_data:/data:ro" -v "$PWD:/backup" alpine:3.23 tar czf /backup/wp_data.tar.gz -C /data .

$ ls -lh wp_data.tar.gz
-rw-r--r--    1 root     root       33.9M Oct 13 00:22 wp_data.tar.gz

說明:

  • exec -T:關閉 TTY,輸出才能正確地用 > 重新導向到檔案。
  • sh -c '... -p"$MYSQL_ROOT_PASSWORD"':用單引號,讓變數在容器內展開(MySQL 容器有這個環境變數),主機不需要知道密碼。
  • [Warning] Using a password on the command line interface can be insecure. 是 MySQL 的提醒,寫在 stderr,不會混進 wordpress.sql,可以忽略。
  • wordpress.sql 開頭是 MySQL dump … Distrib 8.4.11,表示匯出成功。
  • $(basename "$PWD")_wp_data 會組出 Volume 名稱 wordpress_wp_data。如果你的資料夾名稱不同,請用 docker volume ls 確認。
  • :ro 表示唯讀掛載,備份過程不會改到資料。

把 wordpress.sql 和 wp_data.tar.gz 複製到其他機器或雲端儲存,才算真正的備份。

Windows 使用者的備份

PowerShell 不支援 <,Windows PowerShell 5.1 的 > 會把 SQL 檔存成 UTF-16。改成在容器內產生檔案、再用 docker compose cp 複製;打包 wp_data 時直接寫出 Volume 名稱(預設是 資料夾名稱_wp_data):

docker compose exec db sh -c 'mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --result-file=/tmp/wordpress.sql wordpress'
docker compose cp db:/tmp/wordpress.sql wordpress.sql
docker run --rm -v wordpress_wp_data:/data:ro -v "$(pwd):/backup" alpine:3.23 tar czf /backup/wp_data.tar.gz -C /data .
docker compose exec db sh -c 'mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --result-file=/tmp/wordpress.sql wordpress'
docker compose cp db:/tmp/wordpress.sql wordpress.sql
docker run --rm -v wordpress_wp_data:/data:ro -v "${PWD}:/backup" alpine:3.23 tar czf /backup/wp_data.tar.gz -C /data .
docker compose exec db sh -c "mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --result-file=/tmp/wordpress.sql wordpress"
docker compose cp db:/tmp/wordpress.sql wordpress.sql
docker run --rm -v wordpress_wp_data:/data:ro -v "%cd%:/backup" alpine:3.23 tar czf /backup/wp_data.tar.gz -C /data .

還原時反過來:docker compose cp wordpress.sql db:/tmp/restore.sql,再 docker compose exec db sh -c 'mysql -uroot -p$MYSQL_ROOT_PASSWORD wordpress < /tmp/restore.sql'。tar 的打包與解壓縮都在 alpine 容器內進行,不受 Windows 影響。

還原

在新機器(或清空後)放好 compose.yaml、.env、uploads.ini 和兩個備份檔,然後:

# 1. 只啟動資料庫,等它 healthy
docker compose up -d --wait db

# 2. 匯入資料庫
docker compose exec -T db sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" wordpress' < wordpress.sql

# 3. 還原 WordPress 檔案到 Volume
docker run --rm \
  -v "$(basename "$PWD")_wp_data:/data" \
  -v "$PWD:/backup" \
  alpine:3.23 sh -c "cd /data && tar xzf /backup/wp_data.tar.gz"

# 4. 啟動全部服務
docker compose up -d

--wait 會等服務變成 healthy 才返回,避免資料庫還沒準備好就開始匯入。.env 中的密碼要和備份時相同,因為 wp-config.php(在 wp_data 中)記錄的是舊的資料庫密碼。

更新

WordPress 核心、佈景主題、外掛可以直接在後台更新,檔案會寫進 wp_data Volume。

要更新映像檔本身(PHP、Apache、MySQL 小版本):

$ docker compose pull
[+] pull 2/2
 ✔ Image wordpress:7-apache Pulled                                                              0.6s
 ✔ Image mysql:8.4          Pulled                                                              0.7s

$ docker compose up -d
[+] up 2/2
 ✔ Container wordpress-wordpress-1 Running                                                      0.0s
 ✔ Container wordpress-db-1        Healthy                                                      0.5s

pull 下載新版映像檔;up -d 只會重建映像檔有變動的容器。上面的例子映像檔沒有新版本,所以容器維持 Running。

注意事項:

  • 更新前先備份。
  • wordpress:7-apache 會跟著 7.x 更新;如果要固定版本,可以寫完整版本號,例如 wordpress:7.1.3-apache。
  • wp_data 已經有 WordPress 檔案時,映像檔不會覆蓋它們,所以 WordPress 核心版本以後台更新為準。
  • MySQL 跨大版本(例如 8.0 → 8.4)前,請先閱讀 MySQL 官方的升級說明,不要直接改標籤。

刪除整個環境

練習完要清除,加上 --profile tools 才會連 phpMyAdmin 一起刪除;-v 會刪除 Volume,網站資料會全部消失:

$ docker compose --profile tools down -v
[+] down 6/6
 ✔ Container wordpress-wordpress-1  Removed                                                     1.5s
 ✔ Container wordpress-phpmyadmin-1 Removed                                                     1.5s
 ✔ Container wordpress-db-1         Removed                                                     2.3s
 ✔ Volume wordpress_wp_data         Removed                                                     0.3s
 ✔ Network wordpress_default        Removed                                                     0.2s
 ✔ Volume wordpress_db_data         Removed                                                     0.3s

正式上線注意事項

  • 在前面加一個反向代理處理 HTTPS,例如 Nginx、Caddy 或 Traefik,參考 Nginx 靜態網站與反向代理。
  • 資料庫不要用 ports 對外開放,phpMyAdmin 不要開放到公網。
  • 管理員帳號不要用 admin,密碼要夠強。
  • 定期自動備份(例如用 cron 執行上面的備份指令),並把備份存到其他機器。
  • .env 不要提交到 Git。

重點回顧

  • WordPress 與 MySQL 用 Compose 管理,資料分別存在 db_data 與 wp_data 兩個 Volume。
  • MySQL 健康檢查要用 -h 127.0.0.1,避免初始化期間就被判定為 healthy。
  • phpMyAdmin 放在 profile 中、只綁定 127.0.0.1。
  • 備份要同時包含資料庫與網站檔案,並存到其他機器。

動手練習

練習 1:用一個指令確認 PHP 的上傳上限已經是 64M。

答案
docker compose exec wordpress php -r "echo ini_get('upload_max_filesize');"

輸出 64M。

練習 2:網站要搬到另一台主機,需要帶走哪些檔案?

答案

compose.yaml、.env、uploads.ini,以及兩份備份:wordpress.sql(資料庫)與 wp_data.tar.gz(網站檔案)。在新主機上依本章「還原」的步驟操作。

常見問題

安裝畫面為什麼沒有要我填資料庫設定?

WORDPRESS_DB_* 環境變數已經提供了連線資訊,映像檔啟動時自動產生了 wp-config.php。

上傳檔案時出現超過大小上限?

WordPress 映像檔預設 PHP 上傳上限是 2 MB,用本章的 uploads.ini 調整,修改後用 docker compose up -d 重建容器。

Windows 上怎麼備份?

PowerShell 不支援 <,而且 Windows PowerShell 5.1 的 > 會改變檔案編碼。本章「備份」一節附有在容器內匯出再 docker cp 出來的寫法。

更新於 2026年10月14日