[{"data":1,"prerenderedAt":2389},["ShallowReactive",2],{"blog:\u002Fblog\u002Fnuxt\u002Fsupabase-self-hosted":3},{"id":4,"title":5,"author":6,"body":7,"category":2372,"date":2373,"description":2374,"draft":2375,"extension":2376,"image":2377,"meta":2378,"navigation":316,"path":2379,"seo":2380,"series":2381,"seriesOrder":2382,"seriesTitle":2383,"stem":2384,"tags":2385,"updatedAt":2377,"__hash__":2388},"blog\u002Fblog\u002Fnuxt\u002Fsupabase-self-hosted\u002Findex.md","Self-hosted Supabase 部署與遷移","charles",{"type":8,"value":9,"toc":2321},"minimark",[10,14,18,47,50,53,56,61,157,160,170,172,175,178,184,187,194,269,273,280,357,360,390,392,395,399,402,449,456,554,557,585,587,590,593,596,667,671,674,711,715,718,764,766,770,774,780,791,794,800,804,949,953,1025,1027,1031,1034,1037,1043,1141,1145,1178,1184,1316,1356,1358,1361,1364,1467,1470,1521,1524,1704,1707,1766,1769,1960,1963,1991,1993,1996,1999,2008,2014,2020,2024,2034,2039,2057,2060,2065,2070,2081,2085,2090,2102,2111,2113,2116,2119,2154,2157,2196,2199,2220,2222,2225,2273,2275,2278,2317],[11,12,13],"h2",{"id":13},"這篇要解決什麼問題",[15,16,17],"p",{},"Supabase Cloud 對大多數專案來說已經足夠，但某些情境下你可能需要 Self-hosted：",[19,20,21,29,35,41],"ul",{},[22,23,24,28],"li",{},[25,26,27],"strong",{},"資料主權","：資料必須存放在特定地區或自有伺服器",[22,30,31,34],{},[25,32,33],{},"成本考量","：大量資料或高流量時，自架可能更經濟",[22,36,37,40],{},[25,38,39],{},"網路延遲","：內網部署減少 API 延遲",[22,42,43,46],{},[25,44,45],{},"離線環境","：無法連接外部網路的場景",[15,48,49],{},"這篇文章將說明如何從 Supabase Cloud 遷移到 Self-hosted，以及遷移後的開發流程調整。",[51,52],"hr",{},[11,54,55],{"id":55},"架構對比",[57,58,60],"h3",{"id":59},"cloud-vs-self-hosted","Cloud vs Self-hosted",[62,63,64,80],"table",{},[65,66,67],"thead",{},[68,69,70,74,77],"tr",{},[71,72,73],"th",{},"項目",[71,75,76],{},"Supabase Cloud",[71,78,79],{},"Self-hosted",[81,82,83,95,113,124,135,146],"tbody",{},[68,84,85,89,92],{},[86,87,88],"td",{},"部署方式",[86,90,91],{},"SaaS（託管）",[86,93,94],{},"Docker Compose",[68,96,97,100,107],{},[86,98,99],{},"Migration",[86,101,102,103],{},"CLI ",[104,105,106],"code",{},"supabase db push",[86,108,109,110],{},"手動 ",[104,111,112],{},"docker exec psql",[68,114,115,118,121],{},[86,116,117],{},"網路存取",[86,119,120],{},"公開 API",[86,122,123],{},"Cloudflare Tunnel \u002F VPN",[68,125,126,129,132],{},[86,127,128],{},"維護責任",[86,130,131],{},"Supabase 團隊",[86,133,134],{},"自己",[68,136,137,140,143],{},[86,138,139],{},"費用模式",[86,141,142],{},"按用量計費",[86,144,145],{},"伺服器成本",[68,147,148,151,154],{},[86,149,150],{},"Studio",[86,152,153],{},"雲端 Dashboard",[86,155,156],{},"自架 Web UI",[57,158,159],{"id":159},"遷移決策流程",[161,162,167],"pre",{"className":163,"code":165,"language":166},[164],"language-text","需要 Self-hosted 嗎？\n    │\n    ├─ 資料主權要求？ ──────→ Yes → Self-hosted\n    │\n    ├─ 月費 > 伺服器成本？ ──→ Yes → 評估 Self-hosted\n    │\n    ├─ 需要內網部署？ ───────→ Yes → Self-hosted\n    │\n    └─ 以上皆否 ────────────→ 繼續使用 Cloud\n","text",[104,168,165],{"__ignoreMap":169},"",[51,171],{},[11,173,174],{"id":174},"基礎設施部署",[57,176,177],{"id":177},"部署目錄結構",[161,179,182],{"className":180,"code":181,"language":166},[164],"\u002Fopt\u002Fsupabase\u002F\n├── docker-compose.yml\n├── .env\n└── volumes\u002F\n    ├── api\u002F\n    │   └── kong.yml\n    ├── db\u002F\n    │   └── data\u002F\n    └── storage\u002F\n",[104,183,181],{"__ignoreMap":169},[57,185,186],{"id":186},"端口映射策略",[15,188,189,190,193],{},"為避免與本地開發環境的 ",[104,191,192],{},"supabase start"," 衝突，Self-hosted 使用不同端口：",[62,195,196,212],{},[65,197,198],{},[68,199,200,203,206,209],{},[71,201,202],{},"服務",[71,204,205],{},"預設端口",[71,207,208],{},"Self-hosted 端口",[71,210,211],{},"原因",[81,213,214,228,241,255],{},[68,215,216,219,222,225],{},[86,217,218],{},"Kong (API Gateway)",[86,220,221],{},"8000",[86,223,224],{},"8001",[86,226,227],{},"避免與 Portainer 衝突",[68,229,230,232,235,238],{},[86,231,150],{},[86,233,234],{},"3000",[86,236,237],{},"3001",[86,239,240],{},"避免與 dev server 衝突",[68,242,243,246,249,252],{},[86,244,245],{},"PostgreSQL",[86,247,248],{},"5432",[86,250,251],{},"5433",[86,253,254],{},"避免與其他 DB 衝突",[68,256,257,260,263,266],{},[86,258,259],{},"Supavisor",[86,261,262],{},"6543",[86,264,265],{},"6544",[86,267,268],{},"連線池",[57,270,272],{"id":271},"docker-compose-關鍵配置","Docker Compose 關鍵配置",[15,274,275,276,279],{},"修改 ",[104,277,278],{},".env"," 中的端口設定：",[161,281,285],{"className":282,"code":283,"language":284,"meta":169,"style":169},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","# Kong\nKONG_HTTP_PORT=8001\n\n# Studio\nSTUDIO_PORT=3001\n\n# Database\nPOSTGRES_PORT=5433\n","bash",[104,286,287,296,311,318,324,335,340,346],{"__ignoreMap":169},[288,289,292],"span",{"class":290,"line":291},"line",1,[288,293,295],{"class":294},"sutJx","# Kong\n",[288,297,299,303,307],{"class":290,"line":298},2,[288,300,302],{"class":301},"su5hD","KONG_HTTP_PORT",[288,304,306],{"class":305},"smGrS","=",[288,308,310],{"class":309},"s_sjI","8001\n",[288,312,314],{"class":290,"line":313},3,[288,315,317],{"emptyLinePlaceholder":316},true,"\n",[288,319,321],{"class":290,"line":320},4,[288,322,323],{"class":294},"# Studio\n",[288,325,327,330,332],{"class":290,"line":326},5,[288,328,329],{"class":301},"STUDIO_PORT",[288,331,306],{"class":305},[288,333,334],{"class":309},"3001\n",[288,336,338],{"class":290,"line":337},6,[288,339,317],{"emptyLinePlaceholder":316},[288,341,343],{"class":290,"line":342},7,[288,344,345],{"class":294},"# Database\n",[288,347,349,352,354],{"class":290,"line":348},8,[288,350,351],{"class":301},"POSTGRES_PORT",[288,353,306],{"class":305},[288,355,356],{"class":309},"5433\n",[15,358,359],{},"啟動服務：",[161,361,363],{"className":282,"code":362,"language":284,"meta":169,"style":169},"cd \u002Fopt\u002Fsupabase\ndocker compose up -d\n",[104,364,365,374],{"__ignoreMap":169},[288,366,367,371],{"class":290,"line":291},[288,368,370],{"class":369},"sptTA","cd",[288,372,373],{"class":309}," \u002Fopt\u002Fsupabase\n",[288,375,376,380,383,386],{"class":290,"line":298},[288,377,379],{"class":378},"sbgvK","docker",[288,381,382],{"class":309}," compose",[288,384,385],{"class":309}," up",[288,387,389],{"class":388},"stzsN"," -d\n",[51,391],{},[11,393,394],{"id":394},"網路配置",[57,396,398],{"id":397},"cloudflare-tunnel-設定","Cloudflare Tunnel 設定",[15,400,401],{},"透過 Cloudflare Tunnel 將內部服務暴露到公網，無需開放防火牆端口：",[62,403,404,417],{},[65,405,406],{},[68,407,408,411,414],{},[71,409,410],{},"子域名",[71,412,413],{},"指向服務",[71,415,416],{},"用途",[81,418,419,434],{},[68,420,421,426,431],{},[86,422,423],{},[104,424,425],{},"supabase-api.example.com",[86,427,428],{},[104,429,430],{},"localhost:8001",[86,432,433],{},"REST API",[68,435,436,441,446],{},[86,437,438],{},[104,439,440],{},"supabase-studio.example.com",[86,442,443],{},[104,444,445],{},"localhost:3001",[86,447,448],{},"資料庫管理",[15,450,451,452,455],{},"Tunnel 配置範例（",[104,453,454],{},"config.yml","）：",[161,457,461],{"className":458,"code":459,"language":460,"meta":169,"style":169},"language-yaml shiki shiki-themes material-theme-lighter github-light github-dark","tunnel: your-tunnel-id\ncredentials-file: \u002Froot\u002F.cloudflared\u002Fyour-tunnel-id.json\n\ningress:\n  - hostname: supabase-api.example.com\n    service: http:\u002F\u002Flocalhost:8001\n  - hostname: supabase-studio.example.com\n    service: http:\u002F\u002Flocalhost:3001\n  - service: http_status:404\n","yaml",[104,462,463,476,486,490,498,511,521,532,541],{"__ignoreMap":169},[288,464,465,469,473],{"class":290,"line":291},[288,466,468],{"class":467},"sQzsp","tunnel",[288,470,472],{"class":471},"sP7_E",":",[288,474,475],{"class":309}," your-tunnel-id\n",[288,477,478,481,483],{"class":290,"line":298},[288,479,480],{"class":467},"credentials-file",[288,482,472],{"class":471},[288,484,485],{"class":309}," \u002Froot\u002F.cloudflared\u002Fyour-tunnel-id.json\n",[288,487,488],{"class":290,"line":313},[288,489,317],{"emptyLinePlaceholder":316},[288,491,492,495],{"class":290,"line":320},[288,493,494],{"class":467},"ingress",[288,496,497],{"class":471},":\n",[288,499,500,503,506,508],{"class":290,"line":326},[288,501,502],{"class":471},"  -",[288,504,505],{"class":467}," hostname",[288,507,472],{"class":471},[288,509,510],{"class":309}," supabase-api.example.com\n",[288,512,513,516,518],{"class":290,"line":337},[288,514,515],{"class":467},"    service",[288,517,472],{"class":471},[288,519,520],{"class":309}," http:\u002F\u002Flocalhost:8001\n",[288,522,523,525,527,529],{"class":290,"line":342},[288,524,502],{"class":471},[288,526,505],{"class":467},[288,528,472],{"class":471},[288,530,531],{"class":309}," supabase-studio.example.com\n",[288,533,534,536,538],{"class":290,"line":348},[288,535,515],{"class":467},[288,537,472],{"class":471},[288,539,540],{"class":309}," http:\u002F\u002Flocalhost:3001\n",[288,542,544,546,549,551],{"class":290,"line":543},9,[288,545,502],{"class":471},[288,547,548],{"class":467}," service",[288,550,472],{"class":471},[288,552,553],{"class":309}," http_status:404\n",[57,555,556],{"id":556},"安全考量",[19,558,559,565,579],{},[22,560,561,564],{},[25,562,563],{},"Studio 存取限制","：建議透過 Cloudflare Access 加上認證，或限制 IP",[22,566,567,570,571,574,575,578],{},[25,568,569],{},"API 安全","：依賴 ",[104,572,573],{},"anon key"," 和 ",[104,576,577],{},"service_role key"," 保護",[22,580,581,584],{},[25,582,583],{},"資料庫直連","：僅透過 SSH tunnel 或 VPN 存取，不對外開放 5433",[51,586],{},[11,588,589],{"id":589},"環境配置變更",[57,591,592],{"id":592},"環境變數抽象化",[15,594,595],{},"確保程式碼使用環境變數，而非硬編碼 URL：",[161,597,601],{"className":598,"code":599,"language":600,"meta":169,"style":169},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F ❌ 硬編碼（不要這樣做）\nconst supabaseUrl = \"https:\u002F\u002Fxxxx.supabase.co\";\n\n\u002F\u002F ✅ 環境變數\nconst supabaseUrl = process.env.NUXT_PUBLIC_SUPABASE_URL;\n","typescript",[104,602,603,608,634,638,643],{"__ignoreMap":169},[288,604,605],{"class":290,"line":291},[288,606,607],{"class":294},"\u002F\u002F ❌ 硬編碼（不要這樣做）\n",[288,609,610,614,618,621,625,628,631],{"class":290,"line":298},[288,611,613],{"class":612},"sbsja","const",[288,615,617],{"class":616},"s_hVV"," supabaseUrl",[288,619,620],{"class":305}," =",[288,622,624],{"class":623},"sjJ54"," \"",[288,626,627],{"class":309},"https:\u002F\u002Fxxxx.supabase.co",[288,629,630],{"class":623},"\"",[288,632,633],{"class":471},";\n",[288,635,636],{"class":290,"line":313},[288,637,317],{"emptyLinePlaceholder":316},[288,639,640],{"class":290,"line":320},[288,641,642],{"class":294},"\u002F\u002F ✅ 環境變數\n",[288,644,645,647,649,651,654,657,660,662,665],{"class":290,"line":326},[288,646,613],{"class":612},[288,648,617],{"class":616},[288,650,620],{"class":305},[288,652,653],{"class":301}," process",[288,655,656],{"class":471},".",[288,658,659],{"class":301},"env",[288,661,656],{"class":471},[288,663,664],{"class":616},"NUXT_PUBLIC_SUPABASE_URL",[288,666,633],{"class":471},[57,668,670],{"id":669},"wranglerjsonc-配置","wrangler.jsonc 配置",[15,672,673],{},"Cloudflare Workers 的環境變數設定：",[161,675,679],{"className":676,"code":677,"language":678,"meta":169,"style":169},"language-jsonc shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"vars\": {\n    \"NUXT_PUBLIC_SUPABASE_URL\": \"https:\u002F\u002Fsupabase-api.example.com\",\n    \"NUXT_PUBLIC_SUPABASE_KEY\": \"\u003CANON_KEY>\",\n  },\n}\n","jsonc",[104,680,681,686,691,696,701,706],{"__ignoreMap":169},[288,682,683],{"class":290,"line":291},[288,684,685],{},"{\n",[288,687,688],{"class":290,"line":298},[288,689,690],{},"  \"vars\": {\n",[288,692,693],{"class":290,"line":313},[288,694,695],{},"    \"NUXT_PUBLIC_SUPABASE_URL\": \"https:\u002F\u002Fsupabase-api.example.com\",\n",[288,697,698],{"class":290,"line":320},[288,699,700],{},"    \"NUXT_PUBLIC_SUPABASE_KEY\": \"\u003CANON_KEY>\",\n",[288,702,703],{"class":290,"line":326},[288,704,705],{},"  },\n",[288,707,708],{"class":290,"line":337},[288,709,710],{},"}\n",[57,712,714],{"id":713},"cloudflare-workers-secrets","Cloudflare Workers Secrets",[15,716,717],{},"敏感金鑰使用 Secrets 管理：",[161,719,721],{"className":282,"code":720,"language":284,"meta":169,"style":169},"# 設定 Secrets\nwrangler secret put SUPABASE_URL\nwrangler secret put SUPABASE_KEY\nwrangler secret put SUPABASE_SECRET_KEY\n",[104,722,723,728,742,753],{"__ignoreMap":169},[288,724,725],{"class":290,"line":291},[288,726,727],{"class":294},"# 設定 Secrets\n",[288,729,730,733,736,739],{"class":290,"line":298},[288,731,732],{"class":378},"wrangler",[288,734,735],{"class":309}," secret",[288,737,738],{"class":309}," put",[288,740,741],{"class":309}," SUPABASE_URL\n",[288,743,744,746,748,750],{"class":290,"line":313},[288,745,732],{"class":378},[288,747,735],{"class":309},[288,749,738],{"class":309},[288,751,752],{"class":309}," SUPABASE_KEY\n",[288,754,755,757,759,761],{"class":290,"line":320},[288,756,732],{"class":378},[288,758,735],{"class":309},[288,760,738],{"class":309},[288,762,763],{"class":309}," SUPABASE_SECRET_KEY\n",[51,765],{},[11,767,769],{"id":768},"cicd-變更","CI\u002FCD 變更",[57,771,773],{"id":772},"為什麼移除自動化-db-push","為什麼移除自動化 db push",[15,775,776,777,779],{},"Supabase Cloud 環境下，CI 可以直接執行 ",[104,778,106],{},"。但 Self-hosted 情境：",[19,781,782,785,788],{},[22,783,784],{},"CI 無法直接連接內網資料庫",[22,786,787],{},"需要 SSH tunnel 或 VPN，增加複雜度",[22,789,790],{},"手動執行更可控、更安全",[57,792,793],{"id":793},"新的部署流程",[161,795,798],{"className":796,"code":797,"language":166},[164],"開發環境                           生產環境 (Self-hosted)\n┌──────────────────┐              ┌────────────────────────┐\n│  本地開發         │              │  \u002Fopt\u002Fsupabase         │\n│  supabase start  │              │  docker-compose        │\n└────────┬─────────┘              └───────────┬────────────┘\n         │                                    │\n         ▼                                    ▼\nsupabase migration new             手動執行 migration\nsupabase db reset                  docker cp + psql\n         │                                    │\n         └──────── git push ─────────────────→│\n                                              │\n                                    拉取最新 migration\n                                    手動執行 SQL\n",[104,799,797],{"__ignoreMap":169},[57,801,803],{"id":802},"migration-手動執行步驟","Migration 手動執行步驟",[161,805,807],{"className":282,"code":806,"language":284,"meta":169,"style":169},"# 1. 複製 migration 檔案到容器\ndocker cp supabase\u002Fmigrations\u002F20240124000000_create_table.sql \\\n  supabase-db:\u002Ftmp\u002F\n\n# 2. 執行 migration\ndocker exec -it supabase-db \\\n  psql -U postgres -d postgres -f \u002Ftmp\u002F20240124000000_create_table.sql\n\n# 3. 驗證結果\ndocker exec -it supabase-db \\\n  psql -U postgres -d postgres -c \"\\dt public.*\"\n\n# 4. 清理暫存檔案\ndocker exec supabase-db rm \u002Ftmp\u002F20240124000000_create_table.sql\n",[104,808,809,814,827,832,836,841,856,878,882,887,900,924,929,935],{"__ignoreMap":169},[288,810,811],{"class":290,"line":291},[288,812,813],{"class":294},"# 1. 複製 migration 檔案到容器\n",[288,815,816,818,821,824],{"class":290,"line":298},[288,817,379],{"class":378},[288,819,820],{"class":309}," cp",[288,822,823],{"class":309}," supabase\u002Fmigrations\u002F20240124000000_create_table.sql",[288,825,826],{"class":616}," \\\n",[288,828,829],{"class":290,"line":313},[288,830,831],{"class":309},"  supabase-db:\u002Ftmp\u002F\n",[288,833,834],{"class":290,"line":320},[288,835,317],{"emptyLinePlaceholder":316},[288,837,838],{"class":290,"line":326},[288,839,840],{"class":294},"# 2. 執行 migration\n",[288,842,843,845,848,851,854],{"class":290,"line":337},[288,844,379],{"class":378},[288,846,847],{"class":309}," exec",[288,849,850],{"class":388}," -it",[288,852,853],{"class":309}," supabase-db",[288,855,826],{"class":616},[288,857,858,861,864,867,870,872,875],{"class":290,"line":342},[288,859,860],{"class":309},"  psql",[288,862,863],{"class":388}," -U",[288,865,866],{"class":309}," postgres",[288,868,869],{"class":388}," -d",[288,871,866],{"class":309},[288,873,874],{"class":388}," -f",[288,876,877],{"class":309}," \u002Ftmp\u002F20240124000000_create_table.sql\n",[288,879,880],{"class":290,"line":348},[288,881,317],{"emptyLinePlaceholder":316},[288,883,884],{"class":290,"line":543},[288,885,886],{"class":294},"# 3. 驗證結果\n",[288,888,890,892,894,896,898],{"class":290,"line":889},10,[288,891,379],{"class":378},[288,893,847],{"class":309},[288,895,850],{"class":388},[288,897,853],{"class":309},[288,899,826],{"class":616},[288,901,903,905,907,909,911,913,916,918,921],{"class":290,"line":902},11,[288,904,860],{"class":309},[288,906,863],{"class":388},[288,908,866],{"class":309},[288,910,869],{"class":388},[288,912,866],{"class":309},[288,914,915],{"class":388}," -c",[288,917,624],{"class":623},[288,919,920],{"class":309},"\\dt public.*",[288,922,923],{"class":623},"\"\n",[288,925,927],{"class":290,"line":926},12,[288,928,317],{"emptyLinePlaceholder":316},[288,930,932],{"class":290,"line":931},13,[288,933,934],{"class":294},"# 4. 清理暫存檔案\n",[288,936,938,940,942,944,947],{"class":290,"line":937},14,[288,939,379],{"class":378},[288,941,847],{"class":309},[288,943,853],{"class":309},[288,945,946],{"class":309}," rm",[288,948,877],{"class":309},[57,950,952],{"id":951},"批次執行多個-migration","批次執行多個 Migration",[161,954,956],{"className":282,"code":955,"language":284,"meta":169,"style":169},"# 複製整個 migrations 目錄\ndocker cp supabase\u002Fmigrations supabase-db:\u002Ftmp\u002F\n\n# 依序執行（按檔名排序）\ndocker exec supabase-db bash -c '\n  for f in \u002Ftmp\u002Fmigrations\u002F*.sql; do\n    echo \"Executing: $f\"\n    psql -U postgres -d postgres -f \"$f\"\n  done\n'\n",[104,957,958,963,975,979,984,1000,1005,1010,1015,1020],{"__ignoreMap":169},[288,959,960],{"class":290,"line":291},[288,961,962],{"class":294},"# 複製整個 migrations 目錄\n",[288,964,965,967,969,972],{"class":290,"line":298},[288,966,379],{"class":378},[288,968,820],{"class":309},[288,970,971],{"class":309}," supabase\u002Fmigrations",[288,973,974],{"class":309}," supabase-db:\u002Ftmp\u002F\n",[288,976,977],{"class":290,"line":313},[288,978,317],{"emptyLinePlaceholder":316},[288,980,981],{"class":290,"line":320},[288,982,983],{"class":294},"# 依序執行（按檔名排序）\n",[288,985,986,988,990,992,995,997],{"class":290,"line":326},[288,987,379],{"class":378},[288,989,847],{"class":309},[288,991,853],{"class":309},[288,993,994],{"class":309}," bash",[288,996,915],{"class":388},[288,998,999],{"class":623}," '\n",[288,1001,1002],{"class":290,"line":337},[288,1003,1004],{"class":309},"  for f in \u002Ftmp\u002Fmigrations\u002F*.sql; do\n",[288,1006,1007],{"class":290,"line":342},[288,1008,1009],{"class":309},"    echo \"Executing: $f\"\n",[288,1011,1012],{"class":290,"line":348},[288,1013,1014],{"class":309},"    psql -U postgres -d postgres -f \"$f\"\n",[288,1016,1017],{"class":290,"line":543},[288,1018,1019],{"class":309},"  done\n",[288,1021,1022],{"class":290,"line":889},[288,1023,1024],{"class":623},"'\n",[51,1026],{},[11,1028,1030],{"id":1029},"mcp-server-設定","MCP Server 設定",[15,1032,1033],{},"Supabase 內建 MCP Server，讓 Claude Code 可以直接操作資料庫。",[57,1035,1036],{"id":1036},"本地開發環境",[15,1038,1039,1040,1042],{},"連接 ",[104,1041,192],{}," 啟動的本地環境：",[161,1044,1048],{"className":1045,"code":1046,"language":1047,"meta":169,"style":169},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"mcpServers\": {\n    \"local-supabase\": {\n      \"type\": \"http\",\n      \"url\": \"http:\u002F\u002Flocalhost:54321\u002Fmcp\"\n    }\n  }\n}\n","json",[104,1049,1050,1054,1071,1086,1109,1127,1132,1137],{"__ignoreMap":169},[288,1051,1052],{"class":290,"line":291},[288,1053,685],{"class":471},[288,1055,1056,1060,1064,1066,1068],{"class":290,"line":298},[288,1057,1059],{"class":1058},"s39Yj","  \"",[288,1061,1063],{"class":1062},"sseR_","mcpServers",[288,1065,630],{"class":1058},[288,1067,472],{"class":471},[288,1069,1070],{"class":471}," {\n",[288,1072,1073,1076,1080,1082,1084],{"class":290,"line":313},[288,1074,1075],{"class":1058},"    \"",[288,1077,1079],{"class":1078},"sZMiF","local-supabase",[288,1081,630],{"class":1058},[288,1083,472],{"class":471},[288,1085,1070],{"class":471},[288,1087,1088,1091,1095,1097,1099,1101,1104,1106],{"class":290,"line":320},[288,1089,1090],{"class":1058},"      \"",[288,1092,1094],{"class":1093},"srdBf","type",[288,1096,630],{"class":1058},[288,1098,472],{"class":471},[288,1100,624],{"class":623},[288,1102,1103],{"class":309},"http",[288,1105,630],{"class":623},[288,1107,1108],{"class":471},",\n",[288,1110,1111,1113,1116,1118,1120,1122,1125],{"class":290,"line":326},[288,1112,1090],{"class":1058},[288,1114,1115],{"class":1093},"url",[288,1117,630],{"class":1058},[288,1119,472],{"class":471},[288,1121,624],{"class":623},[288,1123,1124],{"class":309},"http:\u002F\u002Flocalhost:54321\u002Fmcp",[288,1126,923],{"class":623},[288,1128,1129],{"class":290,"line":337},[288,1130,1131],{"class":471},"    }\n",[288,1133,1134],{"class":290,"line":342},[288,1135,1136],{"class":471},"  }\n",[288,1138,1139],{"class":290,"line":348},[288,1140,710],{"class":471},[57,1142,1144],{"id":1143},"透過-ssh-tunnel-連接-self-hosted","透過 SSH Tunnel 連接 Self-hosted",[161,1146,1148],{"className":282,"code":1147,"language":284,"meta":169,"style":169},"# 1. 建立 SSH tunnel（本地 8001 → 遠端 8001）\nssh -L 8001:localhost:8001 user@your-server\n\n# 2. MCP 連接本地轉發的端口\n",[104,1149,1150,1155,1169,1173],{"__ignoreMap":169},[288,1151,1152],{"class":290,"line":291},[288,1153,1154],{"class":294},"# 1. 建立 SSH tunnel（本地 8001 → 遠端 8001）\n",[288,1156,1157,1160,1163,1166],{"class":290,"line":298},[288,1158,1159],{"class":378},"ssh",[288,1161,1162],{"class":388}," -L",[288,1164,1165],{"class":309}," 8001:localhost:8001",[288,1167,1168],{"class":309}," user@your-server\n",[288,1170,1171],{"class":290,"line":313},[288,1172,317],{"emptyLinePlaceholder":316},[288,1174,1175],{"class":290,"line":320},[288,1176,1177],{"class":294},"# 2. MCP 連接本地轉發的端口\n",[15,1179,1180,1181,455],{},"專案設定（",[104,1182,1183],{},".mcp.json",[161,1185,1187],{"className":1045,"code":1186,"language":1047,"meta":169,"style":169},"{\n  \"mcpServers\": {\n    \"local-supabase\": {\n      \"type\": \"http\",\n      \"url\": \"http:\u002F\u002Flocalhost:54321\u002Fmcp\"\n    },\n    \"remote-supabase\": {\n      \"type\": \"http\",\n      \"url\": \"http:\u002F\u002Flocalhost:8001\u002Fmcp\"\n    }\n  }\n}\n",[104,1188,1189,1193,1205,1217,1235,1251,1256,1269,1287,1304,1308,1312],{"__ignoreMap":169},[288,1190,1191],{"class":290,"line":291},[288,1192,685],{"class":471},[288,1194,1195,1197,1199,1201,1203],{"class":290,"line":298},[288,1196,1059],{"class":1058},[288,1198,1063],{"class":1062},[288,1200,630],{"class":1058},[288,1202,472],{"class":471},[288,1204,1070],{"class":471},[288,1206,1207,1209,1211,1213,1215],{"class":290,"line":313},[288,1208,1075],{"class":1058},[288,1210,1079],{"class":1078},[288,1212,630],{"class":1058},[288,1214,472],{"class":471},[288,1216,1070],{"class":471},[288,1218,1219,1221,1223,1225,1227,1229,1231,1233],{"class":290,"line":320},[288,1220,1090],{"class":1058},[288,1222,1094],{"class":1093},[288,1224,630],{"class":1058},[288,1226,472],{"class":471},[288,1228,624],{"class":623},[288,1230,1103],{"class":309},[288,1232,630],{"class":623},[288,1234,1108],{"class":471},[288,1236,1237,1239,1241,1243,1245,1247,1249],{"class":290,"line":326},[288,1238,1090],{"class":1058},[288,1240,1115],{"class":1093},[288,1242,630],{"class":1058},[288,1244,472],{"class":471},[288,1246,624],{"class":623},[288,1248,1124],{"class":309},[288,1250,923],{"class":623},[288,1252,1253],{"class":290,"line":337},[288,1254,1255],{"class":471},"    },\n",[288,1257,1258,1260,1263,1265,1267],{"class":290,"line":342},[288,1259,1075],{"class":1058},[288,1261,1262],{"class":1078},"remote-supabase",[288,1264,630],{"class":1058},[288,1266,472],{"class":471},[288,1268,1070],{"class":471},[288,1270,1271,1273,1275,1277,1279,1281,1283,1285],{"class":290,"line":348},[288,1272,1090],{"class":1058},[288,1274,1094],{"class":1093},[288,1276,630],{"class":1058},[288,1278,472],{"class":471},[288,1280,624],{"class":623},[288,1282,1103],{"class":309},[288,1284,630],{"class":623},[288,1286,1108],{"class":471},[288,1288,1289,1291,1293,1295,1297,1299,1302],{"class":290,"line":543},[288,1290,1090],{"class":1058},[288,1292,1115],{"class":1093},[288,1294,630],{"class":1058},[288,1296,472],{"class":471},[288,1298,624],{"class":623},[288,1300,1301],{"class":309},"http:\u002F\u002Flocalhost:8001\u002Fmcp",[288,1303,923],{"class":623},[288,1305,1306],{"class":290,"line":889},[288,1307,1131],{"class":471},[288,1309,1310],{"class":290,"line":902},[288,1311,1136],{"class":471},[288,1313,1314],{"class":290,"line":926},[288,1315,710],{"class":471},[62,1317,1318,1330],{},[65,1319,1320],{},[68,1321,1322,1325,1327],{},[71,1323,1324],{},"Server",[71,1326,416],{},[71,1328,1329],{},"端口",[81,1331,1332,1344],{},[68,1333,1334,1338,1341],{},[86,1335,1336],{},[104,1337,1079],{},[86,1339,1340],{},"本地開發（supabase start）",[86,1342,1343],{},"54321",[68,1345,1346,1350,1353],{},[86,1347,1348],{},[104,1349,1262],{},[86,1351,1352],{},"Self-hosted 生產環境",[86,1354,1355],{},"8001（透過 SSH tunnel）",[51,1357],{},[11,1359,1360],{"id":1360},"日常維護操作",[57,1362,1363],{"id":1363},"服務狀態檢查",[161,1365,1367],{"className":282,"code":1366,"language":284,"meta":169,"style":169},"cd \u002Fopt\u002Fsupabase\n\n# 檢視所有服務狀態\ndocker compose ps\n\n# 檢視特定服務日誌\ndocker compose logs -f kong      # API Gateway\ndocker compose logs -f rest      # PostgREST\ndocker compose logs -f db        # PostgreSQL\ndocker compose logs -f studio    # Studio UI\n",[104,1368,1369,1375,1379,1384,1393,1397,1402,1419,1435,1451],{"__ignoreMap":169},[288,1370,1371,1373],{"class":290,"line":291},[288,1372,370],{"class":369},[288,1374,373],{"class":309},[288,1376,1377],{"class":290,"line":298},[288,1378,317],{"emptyLinePlaceholder":316},[288,1380,1381],{"class":290,"line":313},[288,1382,1383],{"class":294},"# 檢視所有服務狀態\n",[288,1385,1386,1388,1390],{"class":290,"line":320},[288,1387,379],{"class":378},[288,1389,382],{"class":309},[288,1391,1392],{"class":309}," ps\n",[288,1394,1395],{"class":290,"line":326},[288,1396,317],{"emptyLinePlaceholder":316},[288,1398,1399],{"class":290,"line":337},[288,1400,1401],{"class":294},"# 檢視特定服務日誌\n",[288,1403,1404,1406,1408,1411,1413,1416],{"class":290,"line":342},[288,1405,379],{"class":378},[288,1407,382],{"class":309},[288,1409,1410],{"class":309}," logs",[288,1412,874],{"class":388},[288,1414,1415],{"class":309}," kong",[288,1417,1418],{"class":294},"      # API Gateway\n",[288,1420,1421,1423,1425,1427,1429,1432],{"class":290,"line":348},[288,1422,379],{"class":378},[288,1424,382],{"class":309},[288,1426,1410],{"class":309},[288,1428,874],{"class":388},[288,1430,1431],{"class":309}," rest",[288,1433,1434],{"class":294},"      # PostgREST\n",[288,1436,1437,1439,1441,1443,1445,1448],{"class":290,"line":543},[288,1438,379],{"class":378},[288,1440,382],{"class":309},[288,1442,1410],{"class":309},[288,1444,874],{"class":388},[288,1446,1447],{"class":309}," db",[288,1449,1450],{"class":294},"        # PostgreSQL\n",[288,1452,1453,1455,1457,1459,1461,1464],{"class":290,"line":889},[288,1454,379],{"class":378},[288,1456,382],{"class":309},[288,1458,1410],{"class":309},[288,1460,874],{"class":388},[288,1462,1463],{"class":309}," studio",[288,1465,1466],{"class":294},"    # Studio UI\n",[57,1468,1469],{"id":1469},"重啟服務",[161,1471,1473],{"className":282,"code":1472,"language":284,"meta":169,"style":169},"# 重啟所有服務\ndocker compose restart\n\n# 重啟單一服務\ndocker compose restart kong\ndocker compose restart rest\n",[104,1474,1475,1480,1489,1493,1498,1510],{"__ignoreMap":169},[288,1476,1477],{"class":290,"line":291},[288,1478,1479],{"class":294},"# 重啟所有服務\n",[288,1481,1482,1484,1486],{"class":290,"line":298},[288,1483,379],{"class":378},[288,1485,382],{"class":309},[288,1487,1488],{"class":309}," restart\n",[288,1490,1491],{"class":290,"line":313},[288,1492,317],{"emptyLinePlaceholder":316},[288,1494,1495],{"class":290,"line":320},[288,1496,1497],{"class":294},"# 重啟單一服務\n",[288,1499,1500,1502,1504,1507],{"class":290,"line":326},[288,1501,379],{"class":378},[288,1503,382],{"class":309},[288,1505,1506],{"class":309}," restart",[288,1508,1509],{"class":309}," kong\n",[288,1511,1512,1514,1516,1518],{"class":290,"line":337},[288,1513,379],{"class":378},[288,1515,382],{"class":309},[288,1517,1506],{"class":309},[288,1519,1520],{"class":309}," rest\n",[57,1522,1523],{"id":1523},"資料庫備份",[161,1525,1527],{"className":282,"code":1526,"language":284,"meta":169,"style":169},"# 備份特定 schema\ndocker exec supabase-db pg_dump -U postgres -d postgres \\\n  --schema=public \\\n  --schema=your_schema \\\n  --no-owner \\\n  --no-privileges \\\n  > backup_$(date +%Y%m%d).sql\n\n# 備份全部（排除系統 schema）\ndocker exec supabase-db pg_dump -U postgres -d postgres \\\n  -N auth \\\n  -N storage \\\n  -N realtime \\\n  -N supabase_functions \\\n  --no-owner \\\n  --no-privileges \\\n  > backup_data_$(date +%Y%m%d).sql\n",[104,1528,1529,1534,1555,1562,1569,1576,1583,1606,1610,1615,1635,1645,1654,1663,1672,1679,1686],{"__ignoreMap":169},[288,1530,1531],{"class":290,"line":291},[288,1532,1533],{"class":294},"# 備份特定 schema\n",[288,1535,1536,1538,1540,1542,1545,1547,1549,1551,1553],{"class":290,"line":298},[288,1537,379],{"class":378},[288,1539,847],{"class":309},[288,1541,853],{"class":309},[288,1543,1544],{"class":309}," pg_dump",[288,1546,863],{"class":388},[288,1548,866],{"class":309},[288,1550,869],{"class":388},[288,1552,866],{"class":309},[288,1554,826],{"class":616},[288,1556,1557,1560],{"class":290,"line":313},[288,1558,1559],{"class":388},"  --schema=public",[288,1561,826],{"class":616},[288,1563,1564,1567],{"class":290,"line":320},[288,1565,1566],{"class":388},"  --schema=your_schema",[288,1568,826],{"class":616},[288,1570,1571,1574],{"class":290,"line":326},[288,1572,1573],{"class":388},"  --no-owner",[288,1575,826],{"class":616},[288,1577,1578,1581],{"class":290,"line":337},[288,1579,1580],{"class":388},"  --no-privileges",[288,1582,826],{"class":616},[288,1584,1585,1588,1591,1594,1597,1600,1603],{"class":290,"line":342},[288,1586,1587],{"class":305},"  >",[288,1589,1590],{"class":309}," backup_",[288,1592,1593],{"class":471},"$(",[288,1595,1596],{"class":378},"date",[288,1598,1599],{"class":309}," +%Y%m%d",[288,1601,1602],{"class":471},")",[288,1604,1605],{"class":309},".sql\n",[288,1607,1608],{"class":290,"line":348},[288,1609,317],{"emptyLinePlaceholder":316},[288,1611,1612],{"class":290,"line":543},[288,1613,1614],{"class":294},"# 備份全部（排除系統 schema）\n",[288,1616,1617,1619,1621,1623,1625,1627,1629,1631,1633],{"class":290,"line":889},[288,1618,379],{"class":378},[288,1620,847],{"class":309},[288,1622,853],{"class":309},[288,1624,1544],{"class":309},[288,1626,863],{"class":388},[288,1628,866],{"class":309},[288,1630,869],{"class":388},[288,1632,866],{"class":309},[288,1634,826],{"class":616},[288,1636,1637,1640,1643],{"class":290,"line":902},[288,1638,1639],{"class":388},"  -N",[288,1641,1642],{"class":309}," auth",[288,1644,826],{"class":616},[288,1646,1647,1649,1652],{"class":290,"line":926},[288,1648,1639],{"class":388},[288,1650,1651],{"class":309}," storage",[288,1653,826],{"class":616},[288,1655,1656,1658,1661],{"class":290,"line":931},[288,1657,1639],{"class":388},[288,1659,1660],{"class":309}," realtime",[288,1662,826],{"class":616},[288,1664,1665,1667,1670],{"class":290,"line":937},[288,1666,1639],{"class":388},[288,1668,1669],{"class":309}," supabase_functions",[288,1671,826],{"class":616},[288,1673,1675,1677],{"class":290,"line":1674},15,[288,1676,1573],{"class":388},[288,1678,826],{"class":616},[288,1680,1682,1684],{"class":290,"line":1681},16,[288,1683,1580],{"class":388},[288,1685,826],{"class":616},[288,1687,1689,1691,1694,1696,1698,1700,1702],{"class":290,"line":1688},17,[288,1690,1587],{"class":305},[288,1692,1693],{"class":309}," backup_data_",[288,1695,1593],{"class":471},[288,1697,1596],{"class":378},[288,1699,1599],{"class":309},[288,1701,1602],{"class":471},[288,1703,1605],{"class":309},[57,1705,1706],{"id":1706},"資料庫還原",[161,1708,1710],{"className":282,"code":1709,"language":284,"meta":169,"style":169},"# 複製備份到容器\ndocker cp backup_20240124.sql supabase-db:\u002Ftmp\u002F\n\n# 執行還原\ndocker exec -it supabase-db \\\n  psql -U postgres -d postgres -f \u002Ftmp\u002Fbackup_20240124.sql\n",[104,1711,1712,1717,1728,1732,1737,1749],{"__ignoreMap":169},[288,1713,1714],{"class":290,"line":291},[288,1715,1716],{"class":294},"# 複製備份到容器\n",[288,1718,1719,1721,1723,1726],{"class":290,"line":298},[288,1720,379],{"class":378},[288,1722,820],{"class":309},[288,1724,1725],{"class":309}," backup_20240124.sql",[288,1727,974],{"class":309},[288,1729,1730],{"class":290,"line":313},[288,1731,317],{"emptyLinePlaceholder":316},[288,1733,1734],{"class":290,"line":320},[288,1735,1736],{"class":294},"# 執行還原\n",[288,1738,1739,1741,1743,1745,1747],{"class":290,"line":326},[288,1740,379],{"class":378},[288,1742,847],{"class":309},[288,1744,850],{"class":388},[288,1746,853],{"class":309},[288,1748,826],{"class":616},[288,1750,1751,1753,1755,1757,1759,1761,1763],{"class":290,"line":337},[288,1752,860],{"class":309},[288,1754,863],{"class":388},[288,1756,866],{"class":309},[288,1758,869],{"class":388},[288,1760,866],{"class":309},[288,1762,874],{"class":388},[288,1764,1765],{"class":309}," \u002Ftmp\u002Fbackup_20240124.sql\n",[57,1767,1768],{"id":1768},"定期備份腳本",[161,1770,1772],{"className":282,"code":1771,"language":284,"meta":169,"style":169},"#!\u002Fbin\u002Fbash\n# \u002Fopt\u002Fsupabase\u002Fscripts\u002Fbackup.sh\n\nBACKUP_DIR=\"\u002Fopt\u002Fsupabase\u002Fbackups\"\nDATE=$(date +%Y%m%d_%H%M%S)\n\nmkdir -p $BACKUP_DIR\n\ndocker exec supabase-db pg_dump -U postgres -d postgres \\\n  --schema=public \\\n  --no-owner \\\n  --no-privileges \\\n  | gzip > \"$BACKUP_DIR\u002Fbackup_$DATE.sql.gz\"\n\n# 保留最近 7 天的備份\nfind $BACKUP_DIR -name \"backup_*.sql.gz\" -mtime +7 -delete\n\necho \"Backup completed: backup_$DATE.sql.gz\"\n",[104,1773,1774,1779,1784,1788,1802,1819,1823,1834,1838,1858,1864,1870,1876,1903,1907,1912,1939,1943],{"__ignoreMap":169},[288,1775,1776],{"class":290,"line":291},[288,1777,1778],{"class":294},"#!\u002Fbin\u002Fbash\n",[288,1780,1781],{"class":290,"line":298},[288,1782,1783],{"class":294},"# \u002Fopt\u002Fsupabase\u002Fscripts\u002Fbackup.sh\n",[288,1785,1786],{"class":290,"line":313},[288,1787,317],{"emptyLinePlaceholder":316},[288,1789,1790,1793,1795,1797,1800],{"class":290,"line":320},[288,1791,1792],{"class":301},"BACKUP_DIR",[288,1794,306],{"class":305},[288,1796,630],{"class":623},[288,1798,1799],{"class":309},"\u002Fopt\u002Fsupabase\u002Fbackups",[288,1801,923],{"class":623},[288,1803,1804,1807,1809,1811,1813,1816],{"class":290,"line":326},[288,1805,1806],{"class":301},"DATE",[288,1808,306],{"class":305},[288,1810,1593],{"class":471},[288,1812,1596],{"class":378},[288,1814,1815],{"class":309}," +%Y%m%d_%H%M%S",[288,1817,1818],{"class":471},")\n",[288,1820,1821],{"class":290,"line":337},[288,1822,317],{"emptyLinePlaceholder":316},[288,1824,1825,1828,1831],{"class":290,"line":342},[288,1826,1827],{"class":378},"mkdir",[288,1829,1830],{"class":388}," -p",[288,1832,1833],{"class":301}," $BACKUP_DIR\n",[288,1835,1836],{"class":290,"line":348},[288,1837,317],{"emptyLinePlaceholder":316},[288,1839,1840,1842,1844,1846,1848,1850,1852,1854,1856],{"class":290,"line":543},[288,1841,379],{"class":378},[288,1843,847],{"class":309},[288,1845,853],{"class":309},[288,1847,1544],{"class":309},[288,1849,863],{"class":388},[288,1851,866],{"class":309},[288,1853,869],{"class":388},[288,1855,866],{"class":309},[288,1857,826],{"class":616},[288,1859,1860,1862],{"class":290,"line":889},[288,1861,1559],{"class":388},[288,1863,826],{"class":616},[288,1865,1866,1868],{"class":290,"line":902},[288,1867,1573],{"class":388},[288,1869,826],{"class":616},[288,1871,1872,1874],{"class":290,"line":926},[288,1873,1580],{"class":388},[288,1875,826],{"class":616},[288,1877,1878,1881,1884,1887,1889,1892,1895,1898,1901],{"class":290,"line":931},[288,1879,1880],{"class":305},"  |",[288,1882,1883],{"class":378}," gzip",[288,1885,1886],{"class":305}," >",[288,1888,624],{"class":623},[288,1890,1891],{"class":301},"$BACKUP_DIR",[288,1893,1894],{"class":309},"\u002Fbackup_",[288,1896,1897],{"class":301},"$DATE",[288,1899,1900],{"class":309},".sql.gz",[288,1902,923],{"class":623},[288,1904,1905],{"class":290,"line":937},[288,1906,317],{"emptyLinePlaceholder":316},[288,1908,1909],{"class":290,"line":1674},[288,1910,1911],{"class":294},"# 保留最近 7 天的備份\n",[288,1913,1914,1917,1920,1923,1925,1928,1930,1933,1936],{"class":290,"line":1681},[288,1915,1916],{"class":378},"find",[288,1918,1919],{"class":301}," $BACKUP_DIR ",[288,1921,1922],{"class":388},"-name",[288,1924,624],{"class":623},[288,1926,1927],{"class":309},"backup_*.sql.gz",[288,1929,630],{"class":623},[288,1931,1932],{"class":388}," -mtime",[288,1934,1935],{"class":309}," +7",[288,1937,1938],{"class":388}," -delete\n",[288,1940,1941],{"class":290,"line":1688},[288,1942,317],{"emptyLinePlaceholder":316},[288,1944,1946,1949,1951,1954,1956,1958],{"class":290,"line":1945},18,[288,1947,1948],{"class":369},"echo",[288,1950,624],{"class":623},[288,1952,1953],{"class":309},"Backup completed: backup_",[288,1955,1897],{"class":301},[288,1957,1900],{"class":309},[288,1959,923],{"class":623},[15,1961,1962],{},"加入 crontab：",[161,1964,1966],{"className":282,"code":1965,"language":284,"meta":169,"style":169},"# 每天凌晨 3 點備份\n0 3 * * * \u002Fopt\u002Fsupabase\u002Fscripts\u002Fbackup.sh\n",[104,1967,1968,1973],{"__ignoreMap":169},[288,1969,1970],{"class":290,"line":291},[288,1971,1972],{"class":294},"# 每天凌晨 3 點備份\n",[288,1974,1975,1978,1981,1984,1986,1988],{"class":290,"line":298},[288,1976,1977],{"class":378},"0",[288,1979,1980],{"class":1093}," 3",[288,1982,1983],{"class":616}," *",[288,1985,1983],{"class":616},[288,1987,1983],{"class":616},[288,1989,1990],{"class":309}," \u002Fopt\u002Fsupabase\u002Fscripts\u002Fbackup.sh\n",[51,1992],{},[11,1994,1995],{"id":1995},"踩坑經驗",[57,1997,1998],{"id":1998},"端口衝突問題",[15,2000,2001,2004,2005,2007],{},[25,2002,2003],{},"問題","：本地 ",[104,2006,192],{}," 與 Self-hosted 使用相同端口。",[15,2009,2010,2013],{},[25,2011,2012],{},"症狀","：服務啟動失敗或連到錯誤的環境。",[15,2015,2016,2019],{},[25,2017,2018],{},"解決","：Self-hosted 一律使用不同端口（8001, 3001, 5433），開發時不會衝突。",[57,2021,2023],{"id":2022},"認證不依賴-supabase-auth-的優勢","認證不依賴 Supabase Auth 的優勢",[15,2025,2026,2029,2030,2033],{},[25,2027,2028],{},"發現","：如果認證使用 ",[104,2031,2032],{},"nuxt-auth-utils"," 等外部方案，遷移更簡單。",[15,2035,2036,2038],{},[25,2037,211],{},"：",[19,2040,2041,2051,2054],{},[22,2042,2043,2044,2047,2048],{},"RLS 使用自定義的 ",[104,2045,2046],{},"set_app_context()","，不依賴 ",[104,2049,2050],{},"auth.uid()",[22,2052,2053],{},"不需要遷移 Supabase Auth 的使用者資料",[22,2055,2056],{},"Session 管理與 Supabase 完全解耦",[57,2058,2059],{"id":2059},"程式碼無需修改",[15,2061,2062,2064],{},[25,2063,2028],{},"：只需更新環境變數，程式碼完全不用改。",[15,2066,2067,2038],{},[25,2068,2069],{},"前提",[19,2071,2072,2075,2078],{},[22,2073,2074],{},"所有 URL 和 Key 都透過環境變數配置",[22,2076,2077],{},"沒有硬編碼 Supabase Cloud 的 URL",[22,2079,2080],{},"API 呼叫都經過 PostgREST，沒有直連資料庫",[57,2082,2084],{"id":2083},"kong-配置遺失","Kong 配置遺失",[15,2086,2087,2089],{},[25,2088,2003],{},"：MCP endpoint 無法存取。",[15,2091,2092,2038,2094,2097,2098,2101],{},[25,2093,211],{},[104,2095,2096],{},"kong.yml"," 沒有設定 ",[104,2099,2100],{},"\u002Fmcp"," 路由。",[15,2103,2104,2106,2107,2110],{},[25,2105,2018],{},"：確保 ",[104,2108,2109],{},"volumes\u002Fapi\u002Fkong.yml"," 包含 MCP 路由設定。",[51,2112],{},[11,2114,2115],{"id":2115},"檢查清單",[57,2117,2118],{"id":2118},"遷移前確認",[19,2120,2123,2132,2138,2148],{"className":2121},[2122],"contains-task-list",[22,2124,2127,2131],{"className":2125},[2126],"task-list-item",[2128,2129],"input",{"disabled":316,"type":2130},"checkbox"," 環境變數已抽象化（非硬編碼 URL）",[22,2133,2135,2137],{"className":2134},[2126],[2128,2136],{"disabled":316,"type":2130}," 了解 RLS 策略是否依賴 Supabase Auth",[22,2139,2141,2143,2144,2147],{"className":2140},[2126],[2128,2142],{"disabled":316,"type":2130}," 備份 Cloud 資料庫（",[104,2145,2146],{},"pg_dump"," 或 Dashboard 匯出）",[22,2149,2151,2153],{"className":2150},[2126],[2128,2152],{"disabled":316,"type":2130}," 確認目標伺服器規格足夠",[57,2155,2156],{"id":2156},"部署後確認",[19,2158,2160,2166,2172,2178,2184,2190],{"className":2159},[2122],[22,2161,2163,2165],{"className":2162},[2126],[2128,2164],{"disabled":316,"type":2130}," Docker Compose 所有服務正常運行",[22,2167,2169,2171],{"className":2168},[2126],[2128,2170],{"disabled":316,"type":2130}," Cloudflare Tunnel 連線正常",[22,2173,2175,2177],{"className":2174},[2126],[2128,2176],{"disabled":316,"type":2130}," Studio 可以登入並存取資料",[22,2179,2181,2183],{"className":2180},[2126],[2128,2182],{"disabled":316,"type":2130}," Migration 可手動執行成功",[22,2185,2187,2189],{"className":2186},[2126],[2128,2188],{"disabled":316,"type":2130}," 應用程式可正常讀寫資料",[22,2191,2193,2195],{"className":2192},[2126],[2128,2194],{"disabled":316,"type":2130}," MCP Server 可正常連接",[57,2197,2198],{"id":2198},"上線後維護",[19,2200,2202,2208,2214],{"className":2201},[2122],[22,2203,2205,2207],{"className":2204},[2126],[2128,2206],{"disabled":316,"type":2130}," 設定定期備份（crontab）",[22,2209,2211,2213],{"className":2210},[2126],[2128,2212],{"disabled":316,"type":2130}," 設定監控告警（可選）",[22,2215,2217,2219],{"className":2216},[2126],[2128,2218],{"disabled":316,"type":2130}," 文件化維護流程",[51,2221],{},[11,2223,2224],{"id":2224},"最佳實踐總結",[2226,2227,2228,2234,2240,2248,2257,2263],"ol",{},[22,2229,2230,2233],{},[25,2231,2232],{},"端口規劃","：Self-hosted 使用不同端口，避免與本地開發衝突",[22,2235,2236,2239],{},[25,2237,2238],{},"環境變數","：所有 URL 和 Key 透過環境變數配置，程式碼不需改動",[22,2241,2242,2245,2246],{},[25,2243,2244],{},"Migration 手動","：不依賴 CI 自動執行，使用 ",[104,2247,112],{},[22,2249,2250,2253,2254,2256],{},[25,2251,2252],{},"備份策略","：定期 ",[104,2255,2146],{},"，排除系統 schema，保留多版本",[22,2258,2259,2262],{},[25,2260,2261],{},"存取控制","：Studio 加上認證限制，API 依賴 Key 保護",[22,2264,2265,2268,2269,2272],{},[25,2266,2267],{},"監控日誌","：使用 ",[104,2270,2271],{},"docker compose logs"," 監控服務狀態",[51,2274],{},[11,2276,2277],{"id":2277},"延伸閱讀",[19,2279,2280,2289,2296,2303,2310],{},[22,2281,2282],{},[2283,2284,2288],"a",{"href":2285,"rel":2286},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fself-hosting",[2287],"nofollow","Supabase Self-hosting 官方文件",[22,2290,2291],{},[2283,2292,2295],{"href":2293,"rel":2294},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fself-hosting\u002Fdocker",[2287],"Supabase Docker 部署指南",[22,2297,2298],{},[2283,2299,2302],{"href":2300,"rel":2301},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Fcloudflare-one\u002Fconnections\u002Fconnect-networks\u002F",[2287],"Cloudflare Tunnel 文件",[22,2304,2305,2306],{},"上一篇：",[2283,2307,2309],{"href":2308},"\u002Fblog\u002Fnuxt\u002Fsupabase-function-security","Database Function 安全規範",[22,2311,2312,2313],{},"下一篇：",[2283,2314,2316],{"href":2315},"\u002Fblog\u002Fnuxt\u002Fnitro-api-design","Nitro Server API 設計模式",[2318,2319,2320],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":169,"searchDepth":313,"depth":313,"links":2322},[2323,2324,2328,2333,2337,2342,2348,2352,2359,2365,2370,2371],{"id":13,"depth":298,"text":13},{"id":55,"depth":298,"text":55,"children":2325},[2326,2327],{"id":59,"depth":313,"text":60},{"id":159,"depth":313,"text":159},{"id":174,"depth":298,"text":174,"children":2329},[2330,2331,2332],{"id":177,"depth":313,"text":177},{"id":186,"depth":313,"text":186},{"id":271,"depth":313,"text":272},{"id":394,"depth":298,"text":394,"children":2334},[2335,2336],{"id":397,"depth":313,"text":398},{"id":556,"depth":313,"text":556},{"id":589,"depth":298,"text":589,"children":2338},[2339,2340,2341],{"id":592,"depth":313,"text":592},{"id":669,"depth":313,"text":670},{"id":713,"depth":313,"text":714},{"id":768,"depth":298,"text":769,"children":2343},[2344,2345,2346,2347],{"id":772,"depth":313,"text":773},{"id":793,"depth":313,"text":793},{"id":802,"depth":313,"text":803},{"id":951,"depth":313,"text":952},{"id":1029,"depth":298,"text":1030,"children":2349},[2350,2351],{"id":1036,"depth":313,"text":1036},{"id":1143,"depth":313,"text":1144},{"id":1360,"depth":298,"text":1360,"children":2353},[2354,2355,2356,2357,2358],{"id":1363,"depth":313,"text":1363},{"id":1469,"depth":313,"text":1469},{"id":1523,"depth":313,"text":1523},{"id":1706,"depth":313,"text":1706},{"id":1768,"depth":313,"text":1768},{"id":1995,"depth":298,"text":1995,"children":2360},[2361,2362,2363,2364],{"id":1998,"depth":313,"text":1998},{"id":2022,"depth":313,"text":2023},{"id":2059,"depth":313,"text":2059},{"id":2083,"depth":313,"text":2084},{"id":2115,"depth":298,"text":2115,"children":2366},[2367,2368,2369],{"id":2118,"depth":313,"text":2118},{"id":2156,"depth":313,"text":2156},{"id":2198,"depth":313,"text":2198},{"id":2224,"depth":298,"text":2224},{"id":2277,"depth":298,"text":2277},"Nuxt","2026-01-25","從 Supabase Cloud 遷移到 Self-hosted，涵蓋 Docker 部署、Cloudflare Tunnel 配置、Migration 手動執行與日常維護。",false,"md",null,{},"\u002Fblog\u002Fnuxt\u002Fsupabase-self-hosted",{"title":5,"description":2374},"nuxt-fullstack",8.5,"Nuxt 4 全棧實戰筆記","blog\u002Fnuxt\u002Fsupabase-self-hosted\u002Findex",[2372,2386,245,2387,79],"Supabase","Docker","P6AftI-mfxHb9puu7l3U89jX8a_9RbKpcNWnNoB-roU",1780512499430]