SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-16

LinkBreeze 自行代管與 Docker Compose 部署指南

使用 Docker Compose 與 Caddy 在 VPS 部署 LinkBreeze,固定 image tag、啟用無 cookie 點擊追蹤,並掌握保存整個網站的唯一 volume。

LinkBreeze 是什麼

LinkBreeze 是一個自行代管的 Linktree 替代方案:單一 Docker 容器會提供公開的個人簡介連結頁面與管理儀表板,所有狀態資料都儲存在單一 SQLite 檔案中。它採用 MIT 授權,以 TypeScript 和 Next.js 編寫,並發布為 ghcr.io/manak-hash/linkbreeze。若要執行,您需要一台 VPS、一個 A 記錄指向該 VPS 的網域、開放 80 和 443 埠,以及安裝 Docker Compose plugin 的 Docker Engine。

本指南涵蓋該 repository 實際支援的部署方式:在會自動取得憑證的反向代理後方使用 Docker Compose。本指南也說明可能導致故障的情況,因為個人簡介中的連結是其他人會點擊的公開 URL,連結失效就會失去點擊。

在開始之前,請先清楚了解這個專案仍處於早期階段。

LinkBreeze 是否已成熟到足以用於公開個人頁面連結?

截至 2026 年 8 月,該 repository 有 178 顆 stars、17 個 forks,且只有 1 位維護者。第一個標記版本 v1.0.0 的日期是 2026 年 7 月 1 日。這是存在幾週的專案,不是存在幾年的專案。

ChartLinkBreeze tagged releases per week, v1.0.0 to v1.2.7
The data behind this chart
[
  {
    "week": "2026-06-29",
    "releases": 3,
    "cumulative": 3
  },
  {
    "week": "2026-07-06",
    "releases": 3,
    "cumulative": 6
  },
  {
    "week": "2026-07-13",
    "releases": 1,
    "cumulative": 7
  },
  {
    "week": "2026-07-20",
    "releases": 2,
    "cumulative": 9
  },
  {
    "week": "2026-07-27",
    "releases": 3,
    "cumulative": 12
  },
  {
    "week": "2026-08-03",
    "releases": 2,
    "cumulative": 14
  },
  {
    "week": "2026-08-10",
    "releases": 3,
    "cumulative": 17
  }
]

自 v1.0.0 起,專案已在 17 個日曆週內發布 7 個標記版本。撰寫本指南時,該圖表的最後一週仍在進行中,當時已包含其中 3 個版本。

請將這視為兩項不同的事實。維護者很活躍,錯誤通常能在幾天內修正。但 schema 與預設值也仍在變動,因此部署後置之不理的 instance,會逐漸與當時正在撰寫的程式碼產生很大差異。

授權條款能讓你避開最糟的情況。MIT 授權、container image 與儲存在自有磁碟上的 SQLite 檔案,表示即使開發停止,現有部署仍會持續執行。但它無法避免公開 Web 應用程式停止接收安全修補,並隨時間成為責任。請將此服務部署成你會持續更新的系統,並從第一天起確保以下的備份流程正常運作。

固定映像標籤,不要使用 latest

每個版本的發布流程只會推送 2 個標籤:latest,以及移除開頭 v 後的版本號。因此,發布版本 v1.2.7 的固定標籤是 ghcr.io/manak-hash/linkbreeze:1.2.7。填寫 :v1.2.7 不會拉取任何內容,Docker 會回報 manifest unknown,因為從未推送過該標籤。

之所以要固定標籤,是因為 latest 會變動。依照上方圖表中的頻率,針對 latest 執行 docker compose pull,代表未經審查就升級受眾正在使用的頁面。使用固定標籤後,只有在編輯檔案時才會升級。

關於映像還有一點需要注意。發布流程建置時未設定 platforms:,因此發布的映像只有 linux/amd64。在 arm64 主機上拉取時會失敗,並顯示 no matching manifest for linux/arm64/v8 in the manifest list entries。如果您使用的是 ARM VPS,而不是 x86,請直接在該主機上建置映像:

git clone --branch v1.2.7 --depth 1 https://github.com/Manak-hash/LinkBreeze.git
cd LinkBreeze
docker build -t linkbreeze:1.2.7 .

接著在下方的 compose 檔案中,使用 linkbreeze:1.2.7 作為映像名稱。

在 Caddy 後方部署 LinkBreeze 並自動啟用 TLS

Caddy 會自行向 Let's Encrypt 申請及續期憑證,因此不需要另外執行 TLS(傳輸層安全性)憑證設定。整個部署只需要同一個目錄中的 3 個檔案。

先產生 secret:

mkdir -p ~/linkbreeze && cd ~/linkbreeze
printf 'SECRET_KEY=%s\n' "$(openssl rand -hex 32)" > .env
chmod 600 .env

SECRET_KEY 用來簽署管理員工作階段 cookie,並為分析訪客雜湊值加入 salt。Repository 發布的 compose 檔案預設將它設為 ${SECRET_KEY:-changeme-in-production}。如果略過此步驟,執行個體會使用公開顯示在 GitHub 上的工作階段簽署金鑰。請在首次啟動前設定,因為之後變更會使你登出,並重設分析 salt。

建立 docker-compose.yml

services:
  linkbreeze:
    image: ghcr.io/manak-hash/linkbreeze:1.2.7
    restart: unless-stopped
    volumes:
      - linkbreeze-data:/app/data
    environment:
      - DATABASE_PATH=/app/data/linkbreeze.db
      - SECRET_KEY=${SECRET_KEY}
      - BASE_URL=https://links.example.com
    networks:
      - linkbreeze-net

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
      - caddy-config:/config
    networks:
      - linkbreeze-net

networks:
  linkbreeze-net:

volumes:
  linkbreeze-data:
  caddy-data:
  caddy-config:

BASE_URL 為選用設定,但建議設定。它會告知應用程式實際的公開位址,避免應用程式因收到偽造的 Host 標頭,而產生指向他人網域的連結。

接著在同一目錄建立 Caddyfile,填入你自己的網域:

links.example.com {
    encode zstd gzip
    reverse_proxy linkbreeze:3000
}

Caddy 預設會在代理請求上設定 X-Forwarded-ForX-Forwarded-Proto,分析功能依賴這些標頭。啟動服務:

docker compose up -d
docker compose ps
docker compose logs -f caddy

docker compose ps 應顯示 LinkBreeze 容器的狀態為 healthy。映像檔內建 healthcheck:wget --spider -q http://127.0.0.1:3000/api/health,因此不需要自行新增。不要直接複製 repository 自帶的 Caddy 範例中的 healthcheck。該檢查會呼叫 curl,而此映像檔是以 node:22-alpine 為基礎建置,內含 busybox wget,但沒有 curl。因此,即使容器能正常提供頁面,也會回報 unhealthy

在瀏覽器中開啟 https://links.example.com。首次造訪會進入 /setup 的設定精靈,用來建立唯一的管理員帳號。完成後,儀表板位於 /dashboard,登入表單位於 /login。此帳號僅存在於這個執行個體中,應用程式也沒有 single sign-on 整合點。如果希望儀表板使用與其他自架服務相同的登入方式,必須在前方加入 forward auth proxy,例如 自架的 Authentik

請注意 compose 檔案沒有執行的事項:它完全不會發布 3000 埠。只有 Caddy 會監聽公開介面。如果不熟悉 Compose 檔案語法,VPS 的 Docker Compose 基礎說明了此檔案所假設的相關內容;如果前方已經執行其他代理,Nginx、Caddy 與 Traefik 比較則說明需要進行哪些變更。Repository 也提供 Nginx 搭配 Certbot、Traefik 及 Cloudflare tunnel 的可用範例。

資料存放位置,以及備份必須包含的內容

DATABASE_PATH 指向 /app/data/linkbreeze.db。上傳的頭像與連結縮圖會寫入旁邊的 /app/data/uploads。兩者都位於名為 linkbreeze-data 的 volume 中,因此備份單位是整個 volume,而不是單獨的資料庫檔案。若還原檔案時未包含 uploads 目錄,頁面上的每張圖片都會變成 404。

其餘資料確實都在同一個資料庫中,包括頁面、連結、設定、佈景主題、電子郵件訂閱者與分析資料列。

請在容器停止後建立副本:

docker compose stop linkbreeze
docker compose cp linkbreeze:/app/data ./backup-$(date +%F)
docker compose start linkbreeze

必須先停止服務,因為程序寫入 SQLite 資料庫時進行複製,可能會取得尚未完成的交易,導致副本開啟時被判定為損毀檔案。複製期間頁面會離線。還原時則反向執行相同步驟:

docker compose stop linkbreeze
docker compose cp ./backup-2026-08-14/. linkbreeze:/app/data
docker compose start linkbreeze
docker compose logs -f linkbreeze

儀表板也提供 JSON 匯出功能,從 /api/backup 提供為 linkbreeze-backup-YYYY-MM-DD.json。其中包含個人檔案、連結、設定與已儲存的佈景主題,但不包含分析歷史、電子郵件訂閱者或上傳的圖片。還原時會先刪除目前這 4 個資料表中的資料列,再插入匯出檔案中的資料列。請將它視為用於搬移主機或復原編輯錯誤的設定快照。volume 副本才是備份。

這裡適用兩項與其他環境相同的儲存規則:在 VPS 上 於正式環境執行 SQLite 時,請將資料庫放在本機磁碟,因為 SQLite 在網路檔案系統上的鎖定機制並不可靠,而頁面損毀通常要等到發生問題時才會被發現。若要將 named volume 改為主機 bind mount,請先對主機目錄執行 chown:容器會以非 root 的 node 使用者執行,該使用者在 node:22-alpine 中的 uid 為 1000。由 root 建立的目錄無法讓它寫入,因此應用程式無法開啟資料庫,容器也會在啟動時結束。Compose 中以 bind mount 取代 named volume 詳細說明了這項取捨。

這項功能足以說明,為何要自行託管一個其他地方可以免費提供的頁面。

analytics 不使用 cookie。訪客不會被設定 cookie,公開頁面也不會載入第三方 script。系統會以 IP 位址、user agent 字串及 salt 的 SHA-256 hash 識別訪客,並截取前 16 個十六進位字元。salt 本身是目前 UTC 日期與您的 SECRET_KEY 的 hash,因此會在 UTC 午夜變更,昨天的 hash 無法與今天的 hash 比對。原始 IP 位址永遠不會寫入資料庫。

Clicks 由伺服器計算。公開頁面上的每個 http 連結都會指向您自己網域中的 /go/<id>,該端點會記錄 click,然後以 302 redirect 回應,導向實際目的地。因此,即使讀者停用 JavaScript,或使用會封鎖背景請求的 app 內建瀏覽器,仍可計算 clicks。Page views 則透過 /api/track 記錄。

有兩項排除條件值得注意。帶有有效 admin session 的請求會被略過,因此編輯自己的頁面不會灌高數字。已知的 crawler user agent 也會被略過。

關於 consent:讀者的裝置上不會儲存任何資料,而 cookie banner 要求授權的正是儲存在讀者裝置上的 cookie。您仍須依讀者所在地確認適用義務,但這裡沒有需要揭露的 tracking cookie,也沒有第三方接收這些資料。

有一項容易令人意外的限制:輪替 SECRET_KEY 後,每日 salt 也會隨之變更,因此從該時刻起,每位回訪訪客都會被計為新訪客。

分析資料中的國家欄位為何是空白?

因為你的整個架構沒有設定國家標頭。LinkBreeze 會從 cf-ipcountryx-vercel-ip-country 等 proxy header 解析國家。在自有 Caddy 或 Nginx 後方的 VPS 上,這些標頭都不存在,因此國家會記錄為 null,分類結果也會維持空白。容器內沒有 GeoIP database。

有兩種方式可以填入資料。將 Cloudflare 放在網域前方,Cloudflare 會在每個代理的請求中加入 cf-ipcountry。或者,在自有的 reverse proxy 中透過本機 GeoIP lookup 設定其中一個標頭。

另一個相關問題更嚴重,請一併確認。點擊與瀏覽處理常式會優先從 X-Forwarded-For 讀取 client address,接著讀取 X-Real-IP;兩者都不存在時,才會回退到 0.0.0.0。如果未設置 proxy,直接將 port 3000 發布到網際網路,每位訪客都會雜湊成相同的值。這表示 unique visitors 永遠顯示 1,而每個 IP 每分鐘 60 個事件的速率限制會同時套用到所有訪客。使用上方的 reverse_proxy directive 時,Caddy 會替你設定該標頭,這兩個問題也會同時消失。

從 Linktree 匯入,以及無法一併匯入的內容

控制面板中的遷移精靈接受公開個人檔案 URL 或匯出的檔案。它可辨識 linktr.ee、bento.me、lnk.bio、tap.link、hopp.bio、beacons.ai、solo.to、linkfly、mssg.me 和 LittleLink 頁面,也支援一般 HTML 與 JSON 匯出檔。對於 Linktree 或 Bento URL,它會讀取這些頁面嵌入的 __NEXT_DATA__ JSON。對於靜態頁面,它會讀取錨點標籤。

可匯入的內容包括每個連結的標題、URL、描述和圖片、連結是否為社群個人檔案,以及你的顯示名稱、個人簡介和頭像。在寫入資料庫前,你可以選擇要保留哪些找到的連結。

無法匯入的內容包括分析歷史記錄、佈景主題與版面配置、電子郵件訂閱者、排程發布日期,以及舊平台透過自身登入機制限制存取的任何內容。請預期需要手動重建頁面外觀,也要接受舊的點擊歷史記錄會留在舊服務上。

匯入程式會從你的伺服器擷取 URL,而不是從瀏覽器擷取,因此會拒絕非公開的位址。Private/local URLs are not allowed 表示你提供了自己網路內部的位址;這項拒絕是刻意設計的。若沒有這項限制,任何具備控制面板存取權限的人都能利用你的伺服器,探測只有你的伺服器可以連線的機器。你可能看到的其他訊息包括 Only http and https URLs are allowedRequest timed outResponse too large

網頁擷取功能依賴他人的標記。若精靈在明確包含連結的頁面上找不到任何內容,表示該平台在解析器撰寫後變更了 HTML。請手動加入連結,不要等待修正版本。如果你真正需要的是可衡量成效的短連結,而不是個人檔案頁面,像 Shlink 這類自架 URL 縮短器即可完成這項工作,也能在同一台伺服器上穩定執行。

固定版本部署的更新

# edit the image tag in docker-compose.yml, then
docker compose pull
docker compose up -d
docker compose logs -f linkbreeze

容器啟動時會自動執行 schema migration。目前沒有記錄說明如何反向執行,因此請先複製 volume。無法還原的升級,只有在能夠恢復升級前狀態時才安全。

有較新 release 時,dashboard 會顯示 banner。它每 24 小時從專案的 GitHub repository 取得一次小型版本檔案,以檢查是否有更新,不會傳送任何與您的 instance 有關的資訊。變更 tag 前,請先閱讀 release notes,因為在專案目前的開發階段,minor version 可能會變更您所依賴的預設值。

錯誤情況與您會看到的訊息

manifest unknown 拉取映像時。 標籤寫成了 :v1.2.7。Registry 標籤不包含 v,因此請使用 :1.2.7

no matching manifest for linux/arm64/v8 in the manifest list entries 發布的映像僅支援 amd64。請在 ARM 主機上,從具有該標籤的原始碼建置。

容器回報 unhealthy,但頁面可正常載入。 您的 compose 檔案中的 healthcheck 正在呼叫 curl,但該映像不包含此項目。請刪除它,讓映像內建的 wget healthcheck 執行。

Caddy 提供憑證錯誤,或完全沒有回應。 請檢查 docker compose logs caddy。常見原因是 A record 尚未指向此 VPS,或防火牆關閉了 80 埠,導致 Caddy 用來證明網域控制權的 ACME(automatic certificate management environment)HTTP challenge 無法通過。

Unique visitors 停留在 1。 沒有 proxy 設定 X-Forwarded-For,因此每位訪客產生的雜湊值都相同。

容器啟動後立即結束,但昨天仍能正常運作。 如果您從 named volume 改用 host bind mount,資料目錄會由 root 擁有,而應用程式以 uid 1000 執行,因此無法開啟資料庫檔案。請對主機目錄執行 sudo chown -R 1000:1000

Tracking requests 回應 HTTP 429。 /api/track/go/<id> 上的每個 IP 節流限制已達上限。訪客仍會重新導向至目的地,但該次點擊不會被計算。

FAQ

LinkBreeze 是否已準備好用於公開的 bio 連結頁?

這是一個仍在早期階段的專案。截至 2026 年 8 月,repository 有 178 顆 stars、17 個 forks,且只有 1 名 maintainer;第一個 release 的日期是 2026 年 7 月 1 日。平均每週 release 超過 2 次,因此 bug 修復得很快,行為變更也很頻繁。MIT license 與本機 SQLite 檔案表示,即使停止開發,你仍能保留可運作的頁面;但缺乏 security fixes 的公開 web app 會變成負擔,因此應將它視為需要持續更新的軟體,而不是安裝 1 次後就不再處理。

我應該執行哪個 LinkBreeze image tag?

請執行 version tag,例如 ghcr.io/manak-hash/linkbreeze:1.2.7,並刻意控管版本變更。release workflow 只會推送 latest 與不含前綴的版本號,因此搭配 v:v1.2.7 並不存在,Docker 會回覆 manifest unknown。此 image 只支援 linux/amd64,因此在 arm64 VPS 上必須 clone 該 tag 並在本機建置。

為什麼 LinkBreeze analytics 中的國家分布維持空白?

LinkBreeze 會從 proxy header(例如 cf-ipcountryx-vercel-ip-country)讀取訪客國家,且本身不附帶 GeoIP database。位於自有 Caddy 或 Nginx 後方的 VPS 不會設定這些 header,因此國家會儲存為 null。請在網域前方加入 Cloudflare,或讓 reverse proxy 使用本機 GeoIP lookup 設定其中一個 header。

我確切需要備份哪些內容?要如何還原?

請備份整個 linkbreeze-data volume,而不只是 database file。/app/data/linkbreeze.db 儲存所有 link、page、setting、subscriber 與 analytics 資料列;/app/data/uploads 儲存頁面所引用的 avatar 與 thumbnail image。停止 container,執行 docker compose cp linkbreeze:/app/data ./backup-$(date +%F),然後再次啟動。還原時,請將該目錄複製回已停止的 container,再啟動 container。dashboard 的 JSON export 是 profile、link、setting 與 theme 的設定快照,不包含 analytics 或 image。

從 Linktree 匯入時,analytics 和 theme 也會一併匯入嗎?

不會。migration wizard 會從舊的公開 profile 讀取 link title、URL、description 與 image,以及 display name、bio 和 avatar。Analytics history、theme、email subscriber 與排程發布日期不會匯入。匯入後,請在 theme editor 中重新建立頁面外觀;click history 預期仍會留在舊平台。

#linkbreeze#linktree-alternative#docker-compose#sqlite#self-hosting#analytics