VPS 上用 Docker 自行代管 Supabase
學會在自有伺服器執行官方 Supabase Docker stack:替換公開示範 secrets、了解 14 個服務、預留 8 GB RAM,並安全備份與更新。
建置內容
自行代管 Supabase,表示在自己的伺服器上執行官方 Docker Compose stack,其中包含 Postgres、位於其前端的 REST API、驗證服務、檔案儲存、即時 WebSocket,以及 Studio 儀表板。您只需複製一個 repository、編輯一個 .env 檔案,然後啟動約 14 個 containers;這些元件會共同運作,形成一個由您控管的 Supabase 專案。
安裝步驟很短。最常出問題的是 .env 檔案。該檔案附帶 repository 中公開的示範 secrets,使用這些預設值啟動的 stack,任何找到它的人都能存取。本指南說明必須替換的 secrets、各項服務的用途、stack 實際需要多少記憶體,以及如何在不刪除資料庫的情況下更新 stack。
如果您不熟悉 Compose,請先閱讀 VPS 上的 Docker Compose 基礎。以下所有步驟都假設執行 docker compose version 已能顯示版本。
堆疊實際包含的元件
Supabase 不是單一程式。Compose 檔案會在同一個網路上啟動一組獨立服務。了解各服務的用途後,才能將一長串容器名稱轉化為可供除錯的資訊。
db是載入 Supabase 擴充功能的 PostgreSQL。其他所有服務都會與它通訊。如果此容器狀態異常,其他服務也會全部失敗。kong是 API gateway。它監聽 port 8000,並將/rest/v1/、/auth/v1/和/storage/v1/路由至正確的後端。這是唯一應該對外公開的容器。rest是 PostgREST。它讀取 Postgres schema,並將其提供為 REST API,因此新增資料表後,無須撰寫程式碼即可產生新的 endpoint。auth是 GoTrue。它會簽發用來識別使用者的 JSON web token(JWT)。storage和imgproxy負責處理檔案上傳與影像調整大小。realtime會透過 websockets 串流資料庫變更。studio和meta分別是 dashboard,以及其背後的 admin API。analytics(Logflare)和vector會收集日誌,而supavisor是 PostgreSQL connection pooler。
這份清單說明了下方資源數值的由來。你執行的不只是資料庫,而是資料庫加上十多個支援服務。
容量規劃:預留 8 GB RAM
截至 July 2026,在全新安裝且尚未計入自有資料或網路流量時,這套服務堆疊閒置時約使用 2.5 至 3 GB 的常駐記憶體。analytics service 與 Studio Node.js process 是使用記憶體最多的兩個單一元件。2 GB 伺服器會先啟動容器,接著由 kernel out-of-memory killer 終止其中一個,通常是 analytics 或 db。常見症狀是容器持續重新啟動,並以 exit code 137 結束。
對任何依賴這套服務的環境,請配置 8 GB RAM 與 4 vCPU。若能接受重型查詢與 Studio 工作階段同時執行時速度緩慢,4 GB 可用於單人開發環境。磁碟空間同樣重要,因為 Postgres、儲存磁碟區與日誌資料都位於 project directory 下。先配置 40 GB,再持續監控使用量。選擇方案前先盤點服務,是自架任何服務都值得保留的習慣,因為 PhotoPrism 與 Immich 的實際記憶體最低需求,遠高於其快速入門頁面所暗示的容量。
安裝:複製官方儲存庫
支援的方式是將主要儲存庫中的 docker 目錄複製到您自行建立的專案目錄。這種分離很重要,因為後續執行 git pull 時,不會覆寫您的 .env。
git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/* supabase-project
cp supabase/docker/.env.example supabase-project/.env
cd supabase-project
docker compose pulldocker compose pull 會下載數 GB 的映像檔。完成後,每個服務都應標示為 Pulled。如果這裡出現 manifest unknown 錯誤,表示上游已移除指定的映像檔標籤。此時應重新拉取較新的儲存庫版本,不要手動修改標籤。
首次啟動前必須變更的 secret
請在啟動 stack 前完成這些設定,不要等到啟動後才處理。其中數個值會在首次啟動時寫入資料,因此之後才變更就必須重設資料庫。
repository 提供一個 generator,可正確產生所有值,包括必須使用新的 JWT secret 簽署的兩個 API key。
sh utils/generate-keys.sh --update-env該 script 會將 JWT_SECRET、ANON_KEY、SERVICE_ROLE_KEY、SECRET_KEY_BASE、REALTIME_DB_ENC_KEY、VAULT_ENC_KEY、PG_META_CRYPTO_KEY 和 Logflare tokens 的新值寫入 .env。它需要 openssl;任何一般的 Ubuntu image 都已提供此工具。
以下兩個值不會由它設定,必須在 .env 中手動編輯:
POSTGRES_PASSWORD。只能使用字母與數字。此處若包含標點符號,會破壞數個服務透過串接字串建立的 connection strings。錯誤看起來會像 authentication error,而不是 parsing error,導致排查方向錯誤。DASHBOARD_USERNAME和DASHBOARD_PASSWORD。這是 Studio 的 basic authentication credentials。預設密碼實際上就是this_password_is_insecure_and_should_be_updated。
請理解為何不能自行編造 ANON_KEY 和 SERVICE_ROLE_KEY。兩者都是使用 JWT_SECRET 簽署的 JWT。gateway 會在每個 request 上驗證該 signature,因此與你的 secret 不相符的 key 會被 {"message":"Invalid authentication credentials"} 拒絕。這是 self-hosting 最常見的失敗原因:operator 變更了 JWT_SECRET,卻保留 demo keys。請務必一併產生這三個值。
請將 SERVICE_ROLE_KEY 視同 root password。它會完全繞過 row level security。只能放在 server side code 中,不得放在其他位置。
將 SITE_URL 和 API_EXTERNAL_URL 設為使用者實際連線的網址,例如 https://supabase.example.com。Auth 會根據這些值建立 email confirmation 與 OAuth callback links。若保留為 http://localhost:8000,所有使用者都會被導向各自的電腦。
接著檢查目前的設定:
sh run.sh secrets啟動並確認服務狀態正常
sh run.sh start
docker compose psrun.sh start 包裝 docker compose up -d --wait,因此會持續等待,直到健康檢查通過。每個服務都應顯示 running (healthy) 或 running。首次啟動需要 2 到 4 分鐘,因為 Postgres 會先執行初始化指令碼,其他服務必須等到完成後才能連線。
如果容器持續重新啟動,請依服務名稱查看其日誌:
docker compose logs db
docker compose logs authStudio 會使用 8000 埠,並要求輸入您設定的 dashboard 使用者名稱與密碼。
不要將連接埠 8000 暴露在公用網際網路上
Kong 在 8000 上使用純 HTTP。每組 API key 與使用者密碼都會以明文穿過網路;Studio 憑證採用基本驗證,其本質是 base64 編碼,而非加密。
在前方配置反向代理,並在該處終止 TLS(傳輸層安全性)。同時將 Kong 綁定至 loopback 位址,讓其他來源無法連線。在 docker-compose.yml 中,kong 連接埠映射會變成 127.0.0.1:8000:8000,代理再將請求轉送至該位址。在多個 Compose 應用程式前配置 Traefik 說明憑證相關設定。
也要在防火牆封鎖其餘連接埠,因為 Docker 會自行寫入 iptables 規則來發布連接埠,單純的 ufw 設定通常無法攔截這些流量。Docker 容器為何會忽略 ufw 規則 說明了這個常見陷阱。
備份資料庫,不要備份目錄
Postgres 資料位於 ./volumes/db/data 的 bind mount 中。容器執行期間複製該目錄會產生不完整的副本,因為 Postgres 會緩衝寫入,而磁碟上的檔案只有在 checkpoint 時才會保持一致。還原這類副本通常可以成功,但有時會在沒有明顯錯誤的情況下遺失最後幾筆交易。這是備份最不應出現的失敗模式。
請改用 dump。pg_dumpall 會在容器內執行,並產生一致的快照:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql確認檔案不是空的,再信任該備份。接著依排程將這些 dump 傳送到伺服器外部,這就是使用 restic 進行加密的異地備份的用途。同時備份 .env。遺失 JWT_SECRET 會使所有已簽發的 token 失效,且無法讀取所有已儲存的加密 secret。
上傳的檔案位於 ./volumes/storage,這些檔案是一般檔案,因此直接複製即可。
更新而不遺失資料
Supabase 會在 docker-compose.yml 中固定映像檔版本,因此除非手動更新,版本不會自行變更。無論組建哪種手動管理的堆疊,都值得採用版本固定;因此 自架的 RustDesk relay 會固定其兩個伺服器映像檔的版本,而不是追蹤會變動的標籤:升級應該在你安排好時間的早晨主動執行。每次都要先建立 dump。
docker compose pull
sh run.sh recreaterecreate 會停止堆疊,並使用新的映像重新啟動。資料仍會保留,因為資料位於主機上的 bind mount 中,而不是容器內。進行主要版本升級前,請先閱讀儲存庫中的 CHANGELOG.md,因為 PostgreSQL 主要版本升級不會自動執行,必須先建立傾印,再進行還原。
若要套用 Compose 檔案本身的變更,請再次複製上游儲存庫,並將其中的 docker 目錄複製到專案中,同時注意不要覆寫 .env。
完整重設會刪除包括資料庫在內的所有內容。這是另一個獨立的 script,執行時會要求確認:
sh reset.shFAQ
為什麼我的 API 呼叫會回傳「Invalid authentication credentials」?
您的 ANON_KEY 或 SERVICE_ROLE_KEY 並未使用目前位於 .env 的 JWT_SECRET 進行簽署。閘道會驗證每個請求的簽章,簽章不相符時便會拒絕請求。使用 sh utils/generate-keys.sh --update-env 重新產生這三項內容,然後執行 sh run.sh recreate,讓服務讀取新的值。
我可以在 2 GB VPS 上執行自架 Supabase 嗎?
無法可靠執行。截至 2026 年 7 月,這套 stack 閒置時即接近 3 GB,因為會執行約 14 個服務。因此,2 GB 主機會因 out of memory killer 而終止容器,並在 docker compose ps 中看到 exit code 137。正式環境請使用 8 GB,單人開發則至少需要 4 GB。
自架 Supabase 是否包含 edge functions?
包含。Compose 檔案包含以 Deno 為基礎的 functions runtime,並會提供您放在 ./volumes/functions 下的內容。它不包含代管平台的全球部署網路,因此您的 functions 只會在單一位置的單一伺服器上執行。
如何直接連線至 Postgres 資料庫?
在伺服器本機使用 docker exec -it supabase-db psql -U postgres 開啟互動式 shell。若使用外部用戶端,請透過 port 5432 連線至 Supavisor,並使用使用者 postgres.<POOLER_TENANT_ID> 與您的 POSTGRES_PASSWORD。不要將該 port 開放至網際網路。請透過 VPN 或 SSH tunnel 連線。
為什麼我的 auth 確認電子郵件會連到 localhost?
.env 中的 SITE_URL 與 API_EXTERNAL_URL 仍保留預設值。auth service 會根據這兩個值建立所有確認與密碼重設連結,因此會傳送設定值指定的網址。將兩者設為實際的公開 URL,然後重新建立 stack。