自架 Git 伺服器怎麼選?Forgejo、Gitea 或 cgit
比較 4 種自架 Git 伺服器:SSH 裸儲存庫、cgit、Forgejo 或 Gitea、GitLab,依 RAM 排名,並說明 1 GB VPS 能執行哪些方案。
應執行哪一種自架 Git 伺服器
自架 Git 伺服器不是單一產品。VPS 的 RAM(random access memory)會決定你能使用哪一種方案。Git 不需要專用 daemon:bare repository 加上 SSH(secure shell)帳號,在你能租用的最小型主機上就已經是可運作的伺服器。再往上的方案,都是你選擇與 Git 並行執行的 Web 應用程式,而每升級一個層級,就會增加小型 VPS 可能無法提供的記憶體需求。
共有四個層級。透過 SSH 使用 bare repository,不會新增原本未監聽的服務。cgit 是快速的唯讀 Web 檢視介面,不需要資料庫。Forgejo 或 Gitea 是完整的 forge,提供帳號、issues 與 pull requests,通常只需數百 MB。GitLab 則預期執行於規模比其他方案大許多的伺服器上。
請依照你需要執行的工作做決定,再將記憶體需求與你付費方案提供的數值比較。
每個選項實際需要多少 RAM
這些專案中只有 2 個公布硬體需求。請將已公布的數值視為最低門檻,而不是保證值。執行實例後,使用 systemd-cgtop 或 ps -o rss= -C forgejo 測量實際用量。
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 文件指出,2 個 CPU 核心搭配 1 GB RAM,通常足以支援小型團隊與專案;文件也指出 Raspberry Pi 3 足以應付小型工作負載。GitLab 文件將 16 GB 列為單節點安裝的基準,並將 8 GB 列為其頁面所稱記憶體受限環境的最低值。Forgejo 完全沒有公布硬體需求。Forgejo 是 Gitea 的分支,運作方式也相近,因此 Gitea 的數值是目前最接近的公開參考。
在 1 GB VPS 上,裸儲存庫與 cgit 可以順利運作,且仍有剩餘資源,因為兩者都不會執行常駐服務。Forgejo 或 Gitea 都能啟動,也能透過 SQLite 為小型團隊提供服務,但目前已處於文件列出的最低門檻,因此不要在該主機上執行 PostgreSQL 與 CI(continuous integration)runner。如果 Web 介面無預警消失,請執行 sudo dmesg -T | grep -i oom,並尋找類似 Out of memory: Killed process 1181 (forgejo) 的日誌行;這表示核心的 out of memory killer 終止了該程序。在 1 GB 主機上執行 GitLab 不是調校問題。它無法運作。
第 0 層:透過 SSH 使用裸儲存庫
Git 沒有需要啟動的網路 daemon。git push 會透過 SSH 在遠端以一般 Unix process 執行 git-receive-pack,因此,只要能使用 key 連線到某個帳戶,該帳戶就已經是 Git remote。為儲存庫建立專用帳戶,並將儲存庫放在其 home directory 之外,因為在 Ubuntu 24.04 中,新建的 home directory 權限模式為 0750,之後新增的 web view 將無法讀取其中的內容。
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 會建立沒有 working copy 的儲存庫,這就是伺服器所持有的形式。若將內容 push 到具有 working copy 的儲存庫,Git 會以 refusing to update checked out branch: refs/heads/main 拒絕操作,而這是此層級最常見的錯誤。
現在為該帳戶設定 key,並將儲存庫 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_keysgit remote add origin git@vps.example.com:/srv/git/project.git
git push -u origin main成功完成的第一次 push 會以 * [new branch] main -> main 結束。以 git@vps.example.com: Permission denied (publickey) 結束的操作代表尚未完成驗證,因此請使用 sudo journalctl -u ssh -n 20 讀取伺服器 log。若看到內容為 Authentication refused: bad ownership or modes for file /home/git/.ssh/authorized_keys 的行,表示檔案模式不正確,因為 sshd 會忽略其他使用者可寫入的 key file。
接著移除該帳戶的 shell。
command -v git-shell | sudo tee -a /etc/shells
sudo chsh -s "$(command -v git-shell)" gitgit-shell 只接受 Git 透過 SSH 傳送的少數幾個命令,因此互動式登入現在會顯示訊息後停止,不會出現 prompt:
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.這就是完整的伺服器。不需要 database,也沒有要升級的 web process。你放棄的是 forge 提供的所有功能:沒有瀏覽介面、issue tracker、pull request,也沒有個別使用者權限。該檔案中的每個 key 都能讀取及寫入 git 使用者擁有的所有儲存庫。
第1層:cgit 不需要資料庫即可提供 Web 檢視介面
cgit 是以 C 撰寫的 CGI(common gateway interface)程式。Web 伺服器會針對每個請求執行一次,直接從磁碟讀取 repository,且不會自行儲存狀態。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 中將它指向 repository 目錄:
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/gitscan-path 會巡覽該目錄,並列出找到的每個 repository,因此新的 bare repo 無須額外設定就會出現。cache-size 是快取頁面的數量;設為 0 時會停用快取。新增設定前,先查看套件已寫入 /etc/cgitrc 的內容,因為 Debian 和 Ubuntu 套件本身會提供一些預設值。
每個項目會顯示 repository 的 description 檔案第一行,因此新的 bare repo 會以 Unnamed repository; edit this file 'description' to name the repository. 顯示。請針對每個 repository 修正一次:
echo 'Project X, internal tooling' | sudo -u git tee /srv/git/project.git/descriptionnginx site 檔案,以及檢查方式
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 Listenroot /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 unit 尚未執行,或正在其他路徑監聽。systemctl show 行會列印它實際使用的路徑。
在以它為基礎建置前,有兩項限制需要注意。cgit 只能讀取,且沒有登入功能,因此 scan-path 下的所有內容都是公開的:請勿將 private repository 放在該目錄中,或在整個網站前方設定 HTTP basic authentication。CGI 會以 Web 伺服器使用者身分執行,因此該使用者必須能進入 /srv/git,並讀取每個 repository。若該使用者無法進入某個目錄,索引會顯示為空白,而不是回報錯誤。
Tier 2:使用 Forgejo 或 Gitea 管理 issue 與 pull request
Forgejo 與 Gitea 的概念相同:都是單一 Go binary,提供包含使用者、組織、issue、pull request、release、package registry 及內建 CI system 的 web forge。安裝內容只有 binary 加上 SQLite,因此可部署在 GitLab 無法負荷的硬體上。以下 Compose 檔案取自 Forgejo 文件,並使用截至 August 2026 文件中指定的 image tag。
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 -1curl 應輸出 HTTP status line。在完成首次設定前,它可能會重新導向至 /install,這仍表示服務已啟動。如果 container 直接結束,通常原因是 ownership 設定錯誤:./forgejo directory 必須屬於 USER_UID 中指定的 UID(user id),否則 process 無法寫入自己的 data directory。VPS 上的 Docker Compose 完整說明該檔案配置與 volume ownership 規則。
設定頁面上的兩個答案會決定 clone URL 是否能正常運作。SSH port 必須是 222,因為 Compose 檔案會將 host port 222 對應至 container 的 port 22;domain 則必須填入使用者實際輸入的名稱。任一項設定錯誤,所有 repository 頁面提供的 clone command 都會對複製該指令的使用者失效。之後兩者都位於 app.ini 的 [server] section,設定值分別為 SSH_PORT、SSH_DOMAIN 與 ROOT_URL。
對外公開的 instance 應只在 loopback address 上發布 web port('127.0.0.1:3000:3000'),再由 nginx 在前方處理 TLS(transport layer security)。Gitea 也可從 gitea/gitea image 以相同方式安裝,或以單一 binary、單一 systemd unit 及單一 app.ini 執行;截至 August 2026,其目前的 stable release 是 1.27.1。
可以使用 SQLite 時就繼續使用。這樣 instance 只需一個 process 和一個 file,重新開機後也不需要額外監控其他 service。當多人同時寫入時,PostgreSQL 才值得增加其成本,因為 SQLite 會將寫入序列化,而長時間執行的 CI 會持續寫入。兩個 project 都能在之後將既有 instance 遷移至 PostgreSQL,因此不必現在就做出無法更改的決定。
Forgejo 與 Gitea:實際差異
兩者源自同一脈絡。Gitea 在 2016 年從 Gogs 分支出來。2022 年底,Gitea 的網域與商標控制權轉移至 Gitea Ltd。幾位維護者與 Codeberg 隨後共同啟動 Forgejo。Forgejo 由 Codeberg e.V. 發布。這是一個在德國註冊的非營利協會。Forgejo 在 2024 年從 MIT 授權條款改用 GPLv3(GNU 通用公共授權條款第 3 版)。Gitea 維持 MIT 授權,並在商業資金支持下開發。
日常使用上,兩者的功能相近。但兩者之間的遷移路徑並不相同。2025 年 1 月發布的 Forgejo v10.0,是最後一個能直接使用 Gitea 資料庫的版本,而且僅支援 Gitea v1.22 或更舊版本。截至 2026 年 8 月,Gitea 已是 1.27.1,因此目前的 Gitea 執行個體沒有受支援的原地切換方式可轉為 Forgejo。請在填入資料前先選定其中一個,並將日後的遷移視為匯出後重新匯入。
選擇時可遵循以下簡短原則。如果你重視治理,或希望專案維持由非營利組織管理,請使用 Forgejo。如果你需要較大的安裝基礎與商業支援選項,請使用 Gitea。兩者都以開放方式維護,且經常發布版本:Forgejo 每 3 個月發布一個穩定版本,每年發布一個 LTS(長期支援)版本。截至 2026 年 8 月,目前版本為 v16.0.2,LTS 版本為 v15.0.6。
第 3 層:GitLab 在執行任何工作前的成本
GitLab CE 屬於不同類型的軟體。單一執行個體由多個協作服務組成:用於 Web 應用程式的 Puma、處理背景工作的 Sidekiq、PostgreSQL、Redis、用於存取儲存庫的 Gitaly,以及前端的 nginx。Omnibus 套件會一併安裝這些服務,因此安裝簡單,但記憶體最低需求很高。
GitLab 的需求頁面指出,單一節點安裝的基準需求為 16 GB RAM 和 8 vCPU;在記憶體受限的環境中,8 GB 是低限。該頁面也要求停用 swap,因為負載下發生交換會嚴重降低執行個體的效能。以上是截至 August 2026 公布的數值,而且需求多年來持續增加,因此在估算伺服器規格前,請再次查閱該頁面。
這項預算確實能換來實際功能:container registry、package registry、細緻的權限控制、合規與稽核功能,以及經過大規模環境驗證的 CI。如果團隊中沒有人能指出上述功能有哪一項是本季需要的,代表你只是多花錢租用更大型的 VPS,卻沒有得到實質效益。
SSH 存取模型:一個 git 使用者與多把金鑰
這裡的每一層都採用相同的驗證方式。系統中只有一個名為 git 的 Unix 帳號,所有公開金鑰都放在該帳號的 ~/.ssh/authorized_keys 中。驗證依據是金鑰;授權則由同一行中、金鑰前方所寫的選項決定。
單純的金鑰行會授予持有者該帳號可執行的所有操作。強制命令則可將權限限縮為 Git:
restrict,command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptoprestrict 自 OpenSSH 7.2 起提供,可用一個選項同時停用連接埠轉送、代理程式轉送、X11 及 PTY(pseudo terminal)配置。command= 會以你指定的命令取代用戶端要求的命令;Git 仍可正常運作,因為 Git 會在 $SSH_ORIGINAL_COMMAND 中傳送要求。
Forge 會替你寫入該檔案,這正是 tier 0 與 tier 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 會告知 forge 哪個使用者正在連線,forge 會在任何物件傳輸前,先檢查該使用者是否具備該 repository 的權限。請勿在由 forge 管理的主機上手動編輯該檔案,因為 forge 會從資料庫重新產生檔案,你加入的行會消失。Deploy key 也採用相同機制:deploy key 是註冊到單一 repository 的一般 SSH 金鑰,通常設定為唯讀,並由 forge 而非 sshd 執行權限檢查。
有兩個習慣比上述任何設定都重要。每個人或每台機器都應使用一把獨立金鑰,絕不要共用金鑰,因為撤銷共用金鑰時,所有人都必須同時更換金鑰。使用者離開的當天就應移除其金鑰,因為檔案中的舊金鑰會形成無人監控且永久有效的登入方式。伺服器上的良好 SSH 金鑰管理涵蓋金鑰類型與密碼片語,這些原則在此完全適用。如果主機是新的,請先參閱新 VPS 上線後的前 10 分鐘,再將 repository 放上去。
我可以在自己的 Git 伺服器上執行 GitHub Actions 嗎?
您可以執行使用 GitHub Actions 語法撰寫的工作流程,但無法執行 GitHub。Forgejo 自 Forgejo v1.21 起預設啟用 Forgejo Actions,並從每個儲存庫中的 .forgejo/workflows 讀取工作流程檔案。Gitea Actions 的運作方式相同,並讀取 .gitea/workflows。兩者都需要另外安裝 runner,並使用管理員設定中的 token 向您的執行個體註冊。許多已發布的 action 無須修改即可執行;但凡呼叫 GitHub API 或預期使用 GitHub 託管基礎架構的 action,則無法執行。
請預先規劃兩項影響。runner 會為每個工作啟動一個容器,因此需要容器引擎,也需要獨立的記憶體配額。這也是不應將 runner 與 forge 放在同一台 1 GB 伺服器上的原因。runner 會執行工作流程檔案指定的所有內容;Forgejo 文件明確指出,runner 會執行遠端程式碼。可行時,請為 runner 配置獨立主機;至少也應使用獨立的非特權使用者,並將 registration token 限定在單一儲存庫。
如果您的儲存庫仍保留在 GitHub,而您只想在自己管理的硬體上執行運算,這是不同的設定,步驟也不同:自託管的 GitHub Actions runner 會附加至 GitHub 儲存庫,無須進行上述設定。如果您仍在評估離開 GitHub 的成本,GitHub 實際提供的功能會將 Git 託管服務與其周邊網路服務分開說明。
備份:儲存庫只包含一半的狀態
裸儲存庫是一個目錄,因此複製該目錄就會複製其中的所有內容。從另一台機器建立 mirror clone 是真正的備份,且可在原位置更新:
git clone --mirror git@vps.example.com:/srv/git/project.git
cd project.git && git remote update這會取得每個 ref 與每個 object,但不會取得伺服器端 hooks 或 description 檔案。因此,如果使用 hooks,也應保留該目錄的檔案層級副本。
Forge 會在其資料庫中保存 issues、pull requests、使用者、keys 與 permissions。只備份儲存庫會遺失所有這些資料。兩個專案都提供 dump 命令,可將資料庫、儲存庫、設定與附件寫入單一 archive:
sudo -u git forgejo dump -c /etc/forgejo/app.ini -f /var/backups/forgejo-dump.zip在 Docker 中,必須在 container 內執行相同命令;設定路徑則取決於 image,因此請先確認路徑,再輸入命令:
docker compose exec server ls /data/gitea/conf
docker compose exec -u git server forgejo dump -c /data/gitea/conf/app.ini請以擁有資料的使用者執行,並將 archive 寫入該使用者具備寫入權限的目錄。接著將 archive 複製到伺服器之外,因為只存在於備份來源機器上的備份不算備份。還原是許多人會略過的步驟:現在就將一份 dump 解開到備用主機,讓自己在狀況平穩時熟悉程序,而不是等到服務中斷時才處理。
依情境選擇
只有一名使用者、一台筆電和一台 VPS,且不需要瀏覽介面:使用 SSH 提供 bare repository。無須執行額外服務,也沒有需要升級的元件。
條件相同,但希望在瀏覽器中閱讀程式碼並分享連結:加入 cgit。仍不需要資料庫,也沒有常駐服務。
團隊需要互相檢視程式碼並追蹤議題:使用 Forgejo 或 Gitea,並配置 2 GB 以上的 RAM。工作負載增加後,再將 CI runner 移到第二台主機。
組織需要 container registry 和稽核軌跡,且伺服器可配置 16 GB RAM:使用 GitLab。低於這項配置時,不要啟動 GitLab。
提升到前 3 個層級的成本不高,因為這些層級中的 repository 都是磁碟上的一般 Git 目錄。從能滿足需求的最低層級開始。如果你正在評估同一台伺服器上還有哪些服務值得配置,值得自行託管的服務清單會將 Git server 與其他爭用 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:若輸出中有一行列出被終止的程序,表示是 kernel 的 out of memory killer 終止了該程序。此時應升級方案,而不是調整某個 flag。
Forgejo 和 Gitea 有何差異?
兩者具有共同的程式碼歷史,且大多數功能相同。Gitea 於 2016 年從 Gogs 分支而出;2022 年底,Gitea 的商標控制權移轉至一家公司後,Forgejo 再從 Gitea 分支而出。Forgejo 由德國非營利組織 Codeberg e.V. 以 GPLv3 授權發布;Gitea 則維持 MIT 授權,並有商業支持。實際差異在於移轉路徑。2025 年 1 月發布的 Forgejo v10.0,是最後一個能直接使用 Gitea database 的版本,而且來源只能是 Gitea v1.22 或更舊版本。因此,目前的 Gitea instance 沒有受支援的原地切換方式。
可以在自行託管的 Git server 上執行 GitHub Actions workflows 嗎?
Forgejo Actions 和 Gitea Actions 都能執行使用 GitHub Actions YAML syntax 撰寫的 workflows,並從 .forgejo/workflows 和 .gitea/workflows 讀取。你需要安裝獨立的 runner program,並向自己的 instance 註冊。許多已發布的 actions 無須修改即可運作,但任何呼叫 GitHub API 的 action 都無法運作。runner 會執行 repositories 中的任意程式碼,並為每個 job 啟動一個 container。因此,請為 runner 使用獨立主機,或至少使用獨立的 unprivileged user;如果 forge 已在 1 GB server 上運作,也不要將 runner 部署在同一台伺服器上。
如何備份自行託管的 Git server?
對於 bare repositories,從另一台機器執行 git clone --mirror 可複製所有 refs 和 objects;在該 mirror 內執行 git remote update 可更新 mirror。對 Forgejo 或 Gitea 而言,repositories 只是一部分狀態,因為 issues、pull requests、users 和 keys 都儲存在 database 中。請使用內建的 dump 功能 sudo -u git forgejo dump -c /etc/forgejo/app.ini;如果是 Docker 安裝,也可以在 container 內執行相同的 command。將 archive 複製到伺服器之外,並在備用機器上還原一次,以確認程序確實可用。