SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

自架 Git 伺服器選擇:Forgejo、Gitea 與 cgit 比較

評估四種自架 Git 伺服器方案的記憶體需求。從 SSH 裸儲存庫到 GitLab,分析 1 GB RAM 的 VPS 能否穩定執行 Gitea 或 Forgejo,並提供效能調校建議。

您應該執行哪種自架 Git 伺服器

自架 Git 伺服器並非單一產品,您的 VPS 記憶體(RAM)容量決定了您能執行哪種版本。Git 本身不需要專屬 daemon:在您租用規格最小的機器上,一個裸儲存庫(bare repository)加上一個 SSH 帳號,就已經是一個可運作的伺服器。在此之上的所有選擇,都是您決定要額外執行的網頁應用程式,每提升一個層級,都會消耗小型 VPS 可能無法負荷的記憶體。

共有四個層級。透過 SSH 存取的裸儲存庫,無需額外監聽任何連接埠。cgit,一個快速、唯讀且無需資料庫的網頁檢視介面。Forgejo 或 Gitea,一個包含帳號、議題追蹤與合併請求功能的完整開發平台,僅需數百 MB 記憶體。GitLab,其資源需求遠高於其他選項,需要規格大上許多的伺服器。

請根據您的工作需求進行選擇,並將記憶體需求與您購買的方案進行比對。

各選項實際所需的 RAM 容量

僅有兩個專案公開了硬體需求數據。請將公開數據視為最低門檻而非保證,並在執行個體運作後,使用 systemd-cgtop 或 ps -o rss= -C forgejo 進行實際測量。

ChartRAM the projects document, official docs, October 2026
The data behind this chart
[
  {
    "label": "Gitea, small team",
    "ram_gb": 1
  },
  {
    "label": "GitLab, memory constrained",
    "ram_gb": 8
  },
  {
    "label": "GitLab, single node baseline",
    "ram_gb": 16
  }
]

Gitea 文件指出,對於小型團隊與專案,1 GB 的 RAM 與 2 核心 CPU 通常已足夠,並提到 Raspberry Pi 3 即可應付小型工作負載。GitLab 文件將 16 GB 列為單節點安裝的基準,並將 8 GB 視為其頁面所稱「記憶體受限環境」的低標。Forgejo 未發布任何硬體需求。由於它是 Gitea 的分支且運作方式相似,Gitea 的數據是您目前最接近的參考依據。

這對 1 GB 的 VPS 意味著什麼?純 Git 儲存庫與 cgit 可以順利執行並留有餘裕,因為兩者皆無常駐服務。Forgejo 或 Gitea 在使用 SQLite 時可以啟動並服務小型團隊,但您已處於文件規定的最低門檻,因此請勿在該機器上執行 PostgreSQL 與 CI (持續整合) runner。若網頁介面無預警消失,請執行 sudo dmesg -T | grep -i oom 並搜尋類似 Out of memory: Killed process 1181 (forgejo) 的訊息,這代表核心的 OOM (Out of Memory) killer 已將其終止。在 1 GB 的機器上執行 GitLab 並非調校問題,而是它根本無法執行。

層級 0:透過 SSH 的裸儲存庫

Git 不需要啟動任何網路 daemon。透過 SSH 使用 git push 時,遠端會以一般 Unix 程序執行 git-receive-pack,因此任何能透過金鑰存取的帳號,本身即為 Git 遠端。請為儲存庫建立一個專用帳號,並將儲存庫存放在該帳號的家目錄之外;因為在 Ubuntu 24.04 中,新建立的家目錄權限預設為 0750,後續若要加入網頁檢視功能,將無法讀取其中的內容。

sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' \
  --group --disabled-password --home /home/git git
sudo install -d -m 0755 -o git -g git /srv/git
sudo -u git git init --bare /srv/git/project.git

--bare 可建立一個不含工作目錄的儲存庫,這正是伺服器端應有的形式。若嘗試推送至含有工作目錄的儲存庫,系統會以 refusing to update checked out branch: refs/heads/main 拒絕請求,這是此層級最常見的錯誤。

現在,請為該帳號設定金鑰並進行 clone。

sudo -u git install -d -m 700 /home/git/.ssh
sudo -u git tee -a /home/git/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptop
EOF
sudo -u git chmod 600 /home/git/.ssh/authorized_keys
git remote add origin git@vps.example.com:/srv/git/project.git
git push -u origin main

若首次推送成功,結尾會顯示 * [new branch] main -> main。若結尾顯示 git@vps.example.com: Permission denied (publickey),則代表未通過驗證,請使用 sudo journalctl -u ssh -n 20 讀取伺服器日誌。若日誌中出現 Authentication refused: bad ownership or modes for file /home/git/.ssh/authorized_keys,代表檔案權限設定錯誤,因為 sshd 會忽略任何其他使用者可寫入的金鑰檔案。

接著,移除該帳號的 shell 存取權。

command -v git-shell | sudo tee -a /etc/shells
sudo chsh -s "$(command -v git-shell)" git

git-shell 僅接受 Git 透過 SSH 發送的少數指令,因此現在進行互動式登入時,系統會顯示訊息並拒絕進入提示字元:

fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.

這就是伺服器的全部架構。沒有資料庫,也沒有需要升級的網頁程序。您放棄的是程式碼託管平台提供的所有功能:無法瀏覽、沒有問題追蹤、沒有合併請求,也沒有針對使用者的權限控管。該檔案中的每一把金鑰,皆可讀寫 git 使用者所擁有的所有儲存庫。

第一層:cgit 提供無資料庫的網頁檢視介面

cgit 是一個以 C 語言編寫的 CGI (common gateway interface) 程式。網頁伺服器會在每次請求時執行它,它會直接從磁碟讀取儲存庫,且本身不儲存任何狀態。Ubuntu 24.04 的 universe 組件中已包含此套件。

sudo apt update
sudo apt install -y cgit fcgiwrap nginx
sudo install -d -o www-data -g www-data /var/cache/cgit

將其指向 /etc/cgitrc 中的儲存庫目錄:

root-title=Git on example.com
css=/cgit.css
logo=/cgit.png
cache-size=1000
cache-root=/var/cache/cgit
snapshots=tar.gz zip
scan-path=/srv/git

scan-path 會遍歷該目錄並列出所有找到的儲存庫,因此新增一個 bare 儲存庫時無需額外設定。cache-size 是快取頁面的數量,若設為 0 則關閉快取功能。在新增設定行之前,請先閱讀套件預設放置於 /etc/cgitrc 的內容,因為 Debian 與 Ubuntu 套件本身已包含部分預設值。

每個項目會顯示儲存庫中 description 檔案的第一行,因此新建立的 bare 儲存庫會顯示為 Unnamed repository; edit this file 'description' to name the repository.。請針對每個儲存庫執行以下指令修正此問題:

echo 'Project X, internal tooling' | sudo -u git tee /srv/git/project.git/description
Nginx 站台設定檔與檢查方式
server {
    listen 80;
    server_name git.example.com;
    root /usr/share/cgit;

    try_files $uri @cgit;

    location @cgit {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
        fastcgi_param PATH_INFO $uri;
        fastcgi_param QUERY_STRING $args;
        fastcgi_param HTTP_HOST $server_name;
        fastcgi_pass unix:/run/fcgiwrap.socket;
    }
}
sudo systemctl enable --now fcgiwrap.socket
sudo nginx -t && sudo systemctl reload nginx
systemctl show fcgiwrap.socket -p Listen

root /usr/share/cgit 會將 cgit.css 與 cgit.png 作為靜態檔案提供服務,而 try_files 則將其餘請求交由 /usr/lib/cgit/cgit.cgi 的 CGI 處理。若出現 502 錯誤頁面,且 /var/log/nginx/error.log 中顯示 connect() to unix:/run/fcgiwrap.socket failed (2: No such file or directory),代表 socket 單元未執行或監聽於其他路徑。systemctl show 行會印出其實際使用的路徑。

在建置之前,有兩個限制需要注意。cgit 為唯讀且無登入機制,因此 scan-path 下的所有內容皆為公開:請勿將私人儲存庫放置於該目錄,或是在整個站台前方加上 HTTP basic authentication。此外,CGI 是以網頁伺服器使用者的身分執行,因此該使用者必須具備遍歷 /srv/git 以及讀取各儲存庫的權限。若該使用者無法進入某個目錄,該目錄將顯示為空索引,而不會產生錯誤。

第二層:使用 Forgejo 或 Gitea 管理 Issue 與 Pull Request

Forgejo 與 Gitea 的概念相同:皆為單一 Go 二進位執行檔,提供包含使用者、組織、Issue、Pull Request、Release、套件登錄檔與內建 CI 系統的網頁原始碼託管平台。整個安裝僅需二進位檔與 SQLite,因此能運行在 GitLab 無法負荷的硬體上。下方的 Compose 檔案採用 Forgejo 官方文件範例,並標註截至 2026 年 10 月所使用的映像檔標籤。

networks:
  forgejo:
    external: false

services:
  server:
    image: codeberg.org/forgejo/forgejo:16
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1000
    restart: always
    networks:
      - forgejo
    volumes:
      - ./forgejo:/data
      - /etc/localtime:/etc/localtime:ro
    ports:
      - '3000:3000'
      - '222:22'
docker compose up -d
docker compose ps
curl -sI http://127.0.0.1:3000 | head -1

curl 指令應輸出 HTTP 狀態行。在完成首次設定前,它可能會重新導向至 /install,這仍代表服務已啟動。若容器直接退出,通常是權限問題:./forgejo 目錄必須歸屬於 USER_UID 中的 UID(使用者 ID),否則處理程序無法寫入其資料目錄。VPS 上的 Docker Compose 詳細說明了該檔案配置與 Volume 權限規則。

設定頁面中的兩個選項決定了 Clone URL 是否有效。SSH 埠號必須設為 222,因為 Compose 檔案將主機的 222 埠對應至容器內的 22 埠;網域名稱則必須是使用者實際輸入的位址。若兩者設定錯誤,每個儲存庫頁面提供的 Clone 指令對使用者而言皆會失敗。設定完成後,這些參數位於 app.ini 的 [server] 區段中,分別對應 SSH_PORT、SSH_DOMAIN 與 ROOT_URL。

若為公開實例,請僅將網頁埠號發佈至 Loopback 位址('127.0.0.1:3000:3000'),並在前方部署 Nginx 以處理 TLS(傳輸層安全性協定)。Gitea 的安裝方式相同,使用其文件指定的 docker.gitea.com/gitea 映像檔,或作為單一二進位檔搭配一個 systemd 單元與一個 app.ini 執行。其目前穩定版本為 v28.1.0,發佈於 2026 年 10 月 6 日。隨著 9 月底 v28.0.0 的發佈,該專案已捨棄舊有的 1. 前綴,因此 1.27 之後的版本為 28,而非 1.28。

在能力範圍內請維持使用 SQLite。它能將實例維持在單一處理程序與單一檔案,且重啟後無需額外監控服務。當多人同時寫入時,PostgreSQL 的效益才會顯現,因為 SQLite 會序列化寫入操作,而長時間的 CI 執行會持續寫入資料。兩個專案皆支援日後將現有實例遷移至 PostgreSQL,因此這並非不可變更的決定。

Forgejo 與 Gitea:實際差異為何

兩者系出同源。Gitea 於 2016 年從 Gogs 分支而出。2022 年底,Gitea 的網域與商標控制權轉移至 Gitea Ltd 公司,隨後多位維護者與 Codeberg 共同發起了 Forgejo。Forgejo 由德國註冊的非營利組織 Codeberg e.V. 發布,並於 2024 年將授權條款從 MIT 變更為 GPLv3 (GNU General Public License version 3)。Gitea 則維持 MIT 授權,並由商業資金支持開發。

日常使用中,兩者的功能集相當接近,但轉換路徑並不相通。2025 年 1 月發布的 Forgejo v10.0 是最後一個能直接讀取 Gitea 資料庫的版本,且僅限於 Gitea v1.22 或更舊的版本。截至 2026 年 10 月,Gitea 已更新至 v28.1.0,因此目前的 Gitea 執行個體已無受支援的直接轉換至 Forgejo 的途徑。請在匯入資料前先行選擇,並將日後的任何遷移視為匯出與重新匯入的過程。

選擇的簡易準則如下:若您重視治理模式,或希望專案由非營利組織維護,請選擇 Forgejo。若您偏好較大的安裝基礎與商業支援選項,請選擇 Gitea。兩者皆以開放方式維護並頻繁發布:Forgejo 每三個月發布一次穩定版本,每年發布一個 LTS (Long Term Support) 版本;截至 2026 年 10 月,當前版本為 v16.0.5,LTS 版本為 v15.0.9。Forgejo 對 v16 的支援至 2026 年 10 月 29 日,對 v15 的支援則至 2027 年 7 月 15 日,因此在鎖定特定主要版本前,請務必查看其發布頁面。

第三層級:GitLab 在執行任何任務前的成本

GitLab CE 屬於不同類型的軟體。單一實例是由一組協作服務所組成:負責 Web 應用程式的 Puma、處理背景任務的 Sidekiq、PostgreSQL、Redis、用於儲存庫存取的 Gitaly,以及前端的 nginx。Omnibus 套件將這些服務整合安裝,雖然簡化了安裝程序,但也拉高了記憶體的基本需求。

GitLab 的需求頁面將 16 GB RAM 與 8 vCPU 列為單節點安裝的基準規格,並指出在記憶體受限的環境下,最低需求為 8 GB。該頁面同時建議停用 swap,因為在負載下使用 swap 會嚴重降低實例效能。上述為 2026 年 10 月發布的數據,且這些需求隨時間不斷攀升,因此在規劃伺服器規模前,請務必重新查閱該頁面。

投入這些資源可換取實際功能:容器登錄庫 (container registry)、套件登錄庫 (package registry)、細粒度權限管理、合規與稽核功能,以及經過大規模驗證的 CI 系統。若您的團隊成員在本季內無法指出上述任何一項需求,那麼您只是在為更昂貴的 VPS 付費,卻未獲得任何實質效益。

SSH 存取模型:單一 git 使用者與多組金鑰

此架構中的各層級皆採用相同的驗證方式。系統僅存在一個名為 git 的 Unix 帳號,所有公開金鑰皆存放於該帳號的 ~/.ssh/authorized_keys 檔案中。驗證機制依賴金鑰本身,而授權權限則取決於您在該金鑰行前方所設定的選項。

若僅放置純金鑰行,持有者將獲得該帳號的所有權限。若要限制僅能執行 Git,可使用強制指令(forced command):

restrict,command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptop

restrict 於 OpenSSH 7.2 版本引入,能一併關閉連接埠轉發、代理轉發、X11 及 PTY(虛擬終端)配置。command= 會將客戶端請求替換為您指定的指令,由於 Git 會透過 $SSH_ORIGINAL_COMMAND 發送請求,因此 Git 仍能正常運作。

程式碼託管平台(Forge)會自動為您寫入該檔案,這正是第 0 層與第 2 層架構的主要差異。Forgejo 與 Gitea 會重寫 authorized_keys,為每個已註冊的金鑰建立一行設定,並透過強制指令標註該金鑰對應的資料庫 ID:

command="/usr/local/bin/forgejo --config=/etc/forgejo/app.ini serv key-3",no-port-forwarding,no-x11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice

透過此強制指令,單一共享的 Unix 帳號即可實現個別使用者的權限控管:key-3 會告知託管平台是哪位使用者正在連線,平台會在傳輸任何物件前,比對該使用者對儲存庫的存取權限。請勿手動編輯託管平台管理伺服器上的該檔案,因為系統會從資料庫重寫該檔案,導致您的手動變更遺失。部署金鑰(deploy keys)採用相同機制:部署金鑰是註冊於單一儲存庫的普通 SSH 金鑰,通常為唯讀權限,且權限檢查由託管平台而非 sshd 執行。

以下兩個習慣比上述任何設定都重要。請務必為每個人或每台機器核發獨立金鑰,切勿共用金鑰,因為撤銷共用金鑰意味著必須同時更換所有人的金鑰。此外,請在人員離職當天移除其金鑰,否則檔案中殘留的舊金鑰將成為無人監控的永久登入後門。伺服器上的 SSH 金鑰管理實務 涵蓋了金鑰類型與密碼短語,所有原則皆適用於此。若伺服器為全新狀態,請先參考 新 VPS 的前十分鐘設定,再開始部署儲存庫。

我可以在自己的 Git 伺服器上執行 GitHub Actions 嗎?

您可以執行以 GitHub Actions 語法編寫的工作流程(workflows),但無法執行 GitHub 本身。自 Forgejo v1.21 版本起,Forgejo Actions 已預設啟用,並會讀取每個儲存庫中 .forgejo/workflows 的工作流程檔案。Gitea Actions 的運作方式相同,會讀取 .gitea/workflows。兩者皆需要安裝第二個程式(即 runner),並使用管理員設定中的權杖(token)向您的執行個體進行註冊。許多已發布的 actions 可直接執行,但任何呼叫 GitHub API 或預期使用 GitHub 託管基礎設施的內容則無法運作。

請預先規劃兩項後果。Runner 會為每個任務啟動一個容器,因此它需要容器引擎與獨立的記憶體預算,這也是為什麼它不適合與 forge 放在同一台 1 GB 的機器上。此外,runner 會執行工作流程檔案中定義的任何指令,Forgejo 的說明文件已明確指出:runner 會執行遠端程式碼(remote code execution)。請盡可能為其配置獨立的主機,若無法做到,至少應使用無特權的使用者帳號,並將註冊權杖限制在單一儲存庫範圍內。

如果您的儲存庫仍保留在 GitHub 上,且您只想在自行控管的硬體上執行運算,那是另一種設定,步驟也不同:自架 GitHub Actions runner 會連接至 GitHub 儲存庫,且不需要上述任何設定。如果您仍在評估遷移的成本,GitHub 實際提供的功能 一文將 Git 託管服務與其周邊網路功能進行了區分。

備份:儲存庫僅代表一半的狀態

裸儲存庫(bare repository)本質上是一個目錄,因此複製該目錄即代表備份所有內容。從另一台機器進行鏡像複製(mirror clone)是真正的備份方式,且可原地更新:

git clone --mirror git@vps.example.com:/srv/git/project.git
cd project.git && git remote update

此指令會拉取所有的參照(ref)與物件(object)。但它不會拉取伺服器端的掛鉤(hooks)或 description 檔案,因此若您有使用掛鉤,請務必同時保留該目錄的檔案級備份。

代碼託管平台(forge)會將議題(issues)、合併請求(pull requests)、使用者、金鑰與權限儲存在資料庫中,僅複製儲存庫會遺失上述所有資訊。各專案皆提供 dump 指令,可將資料庫、儲存庫、設定檔與附件封裝成單一壓縮檔:

sudo -u git forgejo dump -c /etc/forgejo/app.ini -f /var/backups/forgejo-dump.zip

若使用 Docker,則需在容器內執行相同指令,且設定檔路徑取決於映像檔,執行前請先確認:

docker compose exec server ls /data/gitea/conf
docker compose exec -u git server forgejo dump -c /data/gitea/conf/app.ini

請以擁有資料的使用者身分執行,並將壓縮檔寫入該使用者具備寫入權限的目錄。接著將壓縮檔複製到伺服器外部,因為僅存在於受備份機器上的備份並非真正的備份。還原是人們最常忽略的步驟:現在就將一份 dump 檔解壓縮到備用機器上,以便在平靜時刻熟悉程序,而非等到故障發生時才手忙腳亂。

依情境選擇

若僅有一台筆記型電腦與一台 VPS,且無需瀏覽器介面:請使用 SSH 協定下的 bare repositories。此方案無需額外執行服務,亦無須維護更新。

若上述需求外,還需透過瀏覽器閱讀程式碼並分享連結:請加入 cgit。此方案同樣無需資料庫,且無須常駐背景服務。

若團隊成員需進行程式碼審查與追蹤問題:請使用 Forgejo 或 Gitea,並確保伺服器具備 2 GB 以上的 RAM。當 CI 任務量增加時,請將 CI runner 移至另一台機器。

若組織需要容器登錄檔(container registry)與稽核軌跡,且伺服器擁有 16 GB RAM:請使用 GitLab。若資源低於此標準,請勿部署。

前三種方案的升級成本極低,因為其儲存庫本質上皆為磁碟上的標準 Git 目錄。請從符合需求的最低門檻開始。若您正在評估伺服器上還能部署哪些服務,值得自架的服務清單 提供了將 Git 伺服器與其他服務並存時,對 RAM 資源分配的參考建議。

FAQ

1 GB 的 VPS 能執行 Forgejo 或 Gitea 嗎?

可以。若僅供小型團隊使用、採用 SQLite,且伺服器上沒有其他高負載服務,則可行。Gitea 文件指出,1 GB RAM 與 2 核心 CPU 通常足以應付小型團隊與專案,而 Forgejo 是 Gitea 的分支,資源需求相同。請勿在該機器上安裝 PostgreSQL 或 CI runner。若服務無預警中斷且日誌中無錯誤訊息,請執行 sudo dmesg -T | grep -i oom:若輸出中出現該處理程序的名稱,代表核心的 OOM (Out of Memory) killer 已將其終止,此時的解決方案是升級方案規格,而非調整參數。

Forgejo 與 Gitea 有何差異?

兩者共享程式碼歷史與大部分功能。Gitea 於 2016 年從 Gogs 分支,Forgejo 則於 2022 年底在 Gitea 商標權移轉至營利公司後,從 Gitea 分支。Forgejo 由德國非營利組織 Codeberg e.V. 發布,採用 GPLv3 授權;Gitea 則維持 MIT 授權並由商業公司支持。實際差異在於遷移路徑。2025 年 1 月發布的 Forgejo v10.0 是最後一個能直接讀取 Gitea 資料庫的版本(僅限 Gitea v1.22 或更舊版本),因此目前的 Gitea 實例已無官方支援的直接原地轉換路徑。

我能在自架 Git 伺服器上執行 GitHub Actions 工作流程嗎?

Forgejo Actions 與 Gitea Actions 皆可執行以 GitHub Actions YAML 語法撰寫的工作流程,並讀取 .forgejo/workflows 與 .gitea/workflows。您需安裝獨立的 runner 程式並將其註冊至您的實例。許多已發布的 actions 可直接運作,但呼叫 GitHub API 的項目則無法使用。Runner 會執行儲存庫中的任意程式碼,並為每個任務啟動一個容器,因此請將其部署在獨立主機,或至少使用獨立的非特權使用者,且切勿將其放在已執行 Git 服務的 1 GB 伺服器上。

如何備份自架的 Git 伺服器?

對於裸儲存庫 (bare repositories),從另一台機器執行 git clone --mirror 可複製所有參照與物件,並在該鏡像內執行 git remote update 即可更新。對於 Forgejo 或 Gitea,儲存庫僅是狀態的一部分,因為 Issue、Pull Request、使用者與金鑰皆存於資料庫中。請使用內建的 dump 工具 sudo -u git forgejo dump -c /etc/forgejo/app.ini,若為 Docker 安裝,則在容器內執行相同指令。請將備份檔複製到伺服器外部,並務必在備用機器上執行一次還原程序,以確保流程正確無誤。

#git#self-hosting#forgejo#gitea#ssh