1. 簡介
在 Docker 容器化部署中,預設的具名磁碟卷 (Named Volume) 雖然方便,但其資料隱藏在 Docker 的內部系統目錄中,不利於直接備份與後續維護。將資料庫轉移至宿主機的絕對路徑 (Bind Mount),能顯著提升資料的掌控度與備份便利性。
本指南以 Zipline 服務搭配 PostgreSQL 16 為例,完整記錄從舊有具名磁碟卷搬移至自訂絕對路徑的標準作業流程 (SOP)。本次指南特別強化了「如何進入容器內部查詢資料庫與使用者名稱」的關鍵細節。此流程不僅適用於 Zipline,其底層邏輯與除錯方法亦可完全套用於後續的 Immich 或其他依賴 PostgreSQL 的服務移轉。
2. 先決條件
在開始執行轉移之前,請確保您的環境具備以下條件:
- 具備伺服器的 SSH 終端機存取權限 (具有 sudo 權限)。
- 已安裝 Docker 與 Portainer。
- 來源服務 (如 Zipline) 處於可正常停止的狀態。
- 熟悉基本的 Docker Compose 語法。


3. 變數與環境參數清單
為保護隱私與提升部署靈活性,本指南的所有設定皆採用環境變數處理。以下為本次轉移過程中所提取並確認有效之參數清單:
| 參數類別 | 變數名稱 / 實際設定檔配置 | 說明 |
|---|---|---|
| 路徑設定 | ${DOCKER_DATA_PATH} | 宿主機存放 Docker 資料的根目錄 (例如:/mnt/cloud1/dockerData)。 |
| 舊有磁碟卷 | zipline_zipline_pgdata | Docker 自動管理的舊有 PostgreSQL 具名磁碟卷名稱。 |
| 資料庫憑證 | ${POSTGRESQL_USER} | 實際寫入舊資料庫中的超級管理員角色名稱 (取代預設的 postgres)。 |
| 資料庫憑證 | ${POSTGRESQL_DB} | 實際寫入舊資料庫中的目標資料庫名稱。 |
| 資料庫憑證 | ${POSTGRESQL_PASSWORD} | 資料庫連線密碼。 |
| 內部通訊埠 | 3000 | Zipline 應用程式容器對 Traefik 暴露的內部通訊埠。 |
4. 逐步教學
4.1. 步驟 1:確認舊有磁碟卷名稱並停止服務
在搬移資料前,必須先停止容器,確保資料庫檔案不在寫入狀態。
- 透過 SSH 登入伺服器,找出 PostgreSQL 正在使用的具名磁碟卷名稱:
docker volume ls | grep pgdata
- 進入 Portainer 的 Stacks 介面,將目標服務 (Zipline) 停止 (Stop this stack)。
4.2. 步驟 2:建立目標絕對路徑與設定權限
PostgreSQL 在容器內預設使用 UID 999 執行。為了讓容器啟動後有權限讀寫宿主機上的絕對路徑,必須正確設定資料夾擁有者。
# 1. 建立存放資料庫檔案的絕對路徑 (請替換變數為實際路徑)
sudo mkdir -p ${DOCKER_DATA_PATH}/zipline/pgdata
# 2. 將該目錄的擁有者更改為 999 (PostgreSQL 專用 UID)
sudo chown -R 999:999 ${DOCKER_DATA_PATH}/zipline/pgdata
4.3. 步驟 3:使用救難容器進行資料搬移
為確保檔案權限與隱藏屬性完整轉移,請使用輕量級的 Alpine 容器作為搬遷橋樑。
# 使用救難容器將舊卷資料完整複製到新目錄
# 參數 -a 代表封存模式 (保留權限),-v 代表顯示過程
docker run --rm \
-v zipline_zipline_pgdata:/from \
-v ${DOCKER_DATA_PATH}/zipline/pgdata:/to \
alpine ash -c "cp -av /from/. /to/"
4.4. 步驟 4:修改 Docker Compose 設定檔並啟動
回到 Portainer 的 Stack 編輯器,將資料庫服務的 Volume 掛載方式改為絕對路徑。
version: '3.8'
services:
zipline_db:
image: postgres:16
container_name: zipline_db
restart: unless-stopped
networks:
- default
environment:
POSTGRES_USER: ${POSTGRESQL_USER}
POSTGRES_PASSWORD: ${POSTGRESQL_PASSWORD}
POSTGRES_DB: ${POSTGRESQL_DB}
TZ: Asia/Taipei
volumes:
# 將原本的具名磁碟卷替換為宿主機的絕對路徑
- ${DOCKER_DATA_PATH}/zipline/pgdata:/var/lib/postgresql/data
# ... (省略其他服務設定) ...
# 務必刪除或註解掉檔案最底部的 volumes 宣告區塊
# volumes:
# zipline_pgdata:
更新 Stack 並啟動容器 (Update the stack -> Re-create containers)。此時資料庫容器會啟動,但由於 PostgreSQL 偵測到已有資料,會跳過初始化。這表示它不會理會您剛剛在環境變數中設定的新名稱,而是沿用舊有設定。
4.5. 步驟 5:查詢舊資料庫內部的角色與名稱 (關鍵細節)
這是在搬移過程中最重要的除錯步驟。如果應用程式 (如 Zipline) 報錯無法連線,通常是因為舊資料庫內部的名稱與現有 .env 不符。我們必須進入資料庫內部進行查詢。
- 嘗試一般登入:
# 嘗試使用您的環境變數帳號登入
docker exec -it zipline_db psql -U ${POSTGRESQL_USER} -d postgres
- (若失敗) 使用系統管理員強制登入:
如果在建立舊資料庫時,預設的
postgres帳號已被取代,上述指令或使用-U postgres可能會報錯FATAL: role "postgres" does not exist。此時請改用作業系統的 postgres 權限強制進入:
# 透過容器內的 linux 使用者 postgres 身份進入 psql,繞過角色驗證
docker exec -u postgres -it zipline_db psql
- 執行查詢指令:
成功進入 psql 提示字元 (如
postgres=#或zipline_admin=#) 後,請輸入以下指令進行盤點:
查詢所有使用者 (Roles):輸入
\du並按下 Enter。查看清單中擁有Superuser權限的角色名稱究竟是什麼。查詢所有資料庫 (Databases):輸入
\l並按下 Enter。查看清單中除了template與postgres之外,存放您資料的實體資料庫名稱是什麼。退出介面:輸入
\q退出 psql。

4.6. 步驟 6:校正資料庫內部的名稱與密碼
根據步驟 5 查詢到的結果,若發現舊名稱與您現在 .env 中設定的 ${POSTGRESQL_USER} 或 ${POSTGRESQL_DB} 不同,請在 psql 介面中執行以下指令強制統一 (請替換為實際的舊名稱與變數):
-- 1. 將舊角色名稱改為新的環境變數名稱
ALTER ROLE 舊角色名稱 RENAME TO 您的新使用者名稱;
-- 2. 強制更新密碼以確保連線無誤
ALTER ROLE 您的新使用者名稱 WITH PASSWORD '您的新密碼';
-- 3. 將舊資料庫名稱改為新的環境變數名稱
ALTER DATABASE 舊資料庫名稱 RENAME TO 您的新資料庫名稱;
完成校正後,重新啟動應用程式容器 (如 Zipline),即可恢復正常連線。
5. 常見問題與故障排除
5.1. 故障 1:Traefik Dashboard 找不到路由,網頁顯示 404
- 現象:容器皆顯示 Running,但 Traefik 儀表板未顯示該服務網址。
- 無效嘗試:反覆檢查 Traefik 的 Labels 語法與 Cloudflare DNS 設定。
- 解決邏輯:經查核為 Portainer 中應用程式容器 (zipline) 狀態顯示為
unhealthy。Traefik 預設會隱藏不健康的容器路由。原因在於 Docker Compose 中的healthcheck指令依賴的工具 (如wget) 在 Zipline 容器內不存在,或因資料庫連線延遲導致檢查超時。 - 解決方法:先於
docker-compose.yml中將應用程式服務的healthcheck區塊全數註解,強制容器狀態恢復為健康,使 Traefik 路由正常載入並方便進行後續連線除錯。
6. 進階優化
- 健康檢查指令優化:若日後需恢復應用程式容器的健康檢查機制,為避免因啟動時間差或缺少工具造成的誤判,建議加入
start_period參數,並使用相容性較高的 Shell 邏輯:
healthcheck:
# 改用 CMD-SHELL 並結合 curl 與 wget 提高相容性
test: ["CMD-SHELL", "curl -f http://localhost:3000/api/healthcheck || wget -q --spider http://localhost:3000/api/healthcheck || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s # 給予應用程式 60 秒的啟動緩衝期
- 舊資料清理:當確認新絕對路徑的資料庫穩定運行數日後,可執行
docker volume rm zipline_zipline_pgdata以釋放伺服器儲存空間。
7. 結論
將 PostgreSQL 資料庫從具名磁碟卷轉移至絕對路徑的核心挑戰,在於理解 Docker 容器啟動機制與資料庫內部既有資料的關聯。只要確保檔案權限 (UID 999) 正確,並學會進入容器內部使用 \du 與 \l 指令查驗真實狀態,再配合 ALTER 指令校正,即可順利完成無痛轉移。這套流程與排錯邏輯,亦可完全套用於後續 Immich 等任何依賴 PostgreSQL 服務的資料庫搬遷。