Dealownia
Menu
sprawdzono – jak sprawdzamy oferty

Forgejo na VPS: własny serwer Git w Dockerze z SSH i kopiami

Forgejo w Dockerze za nginx z HTTPS, klonowanie przez SSH na osobnym porcie, wyłączona rejestracja, kopie danych, runner CI w skrócie i kiedy wystarczy GitHub.

Aplikacje na własnym serwerzezaktualizowano 7 min czytania

Redakcja serwisu Dealownia · ceny i fakty ze stron dostawców, jak sprawdzamy

Własny serwer Git na Forgejo pod adresem https://git.example.pl: z interfejsem jak w GitHubie (repozytoria, zgłoszenia, pull requesty), z klonowaniem przez HTTPS i przez SSH, z zamkniętą rejestracją i z nocną kopią, z której odtworzysz całą instancję. Forgejo to wolne oprogramowanie rozwijane przez społeczność, odgałęzienie Gitei. Na końcu krótko o runnerze CI i o tym, kiedy darmowy GitHub w zupełności wystarczy.

Czego potrzebujesz

  • VPS z Ubuntu 24.04 LTS albo Debianem 12 lub 13, przygotowany jak w poradniku od zera: użytkownik z sudo, ufw, nginx na hoście, certbot, Docker z wtyczką Compose.
  • Rozmiar serwera: dokumentacja Forgejo nie podaje minimalnych wymagań sprzętowych. Forgejo to jeden program w Go, a w tym poradniku używa wbudowanej bazy SQLite, więc dla jednej osoby lub małego zespołu zacznij od najmniejszego planu z dyskiem, który pomieści repozytoria, i obserwuj zużycie w docker stats. Więcej zasobów potrzeba przy dużych repozytoriach, wielu użytkownikach i własnym CI na tym samym serwerze.
  • Subdomenę git.example.pl z rekordem A wskazującym serwer.

Jeśli chcesz porównać plany pod kątem dysku, zajrzyj do rankingu VPS.

Krok 1. Katalog i compose.yaml

Instrukcja instalacji w Dockerze używa obrazu codeberg.org/forgejo/forgejo z tagiem wersji głównej (obecnie 17, który śledzi najnowsze wydanie poprawkowe). Katalog z danymi musi należeć do UID i GID podanych w USER_UID i USER_GID, inaczej kontener może nie wystartować:

bash
sudo mkdir -p /opt/apps/forgejo/forgejo
sudo chown deploy:deploy /opt/apps/forgejo
sudo chown 1000:1000 /opt/apps/forgejo/forgejo
cd /opt/apps/forgejo

Plik compose.yaml:

yaml
services:
  server:
    image: codeberg.org/forgejo/forgejo:17
    container_name: forgejo
    restart: always
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - FORGEJO__server__DOMAIN=git.example.pl
      - FORGEJO__server__ROOT_URL=https://git.example.pl/
      - FORGEJO__server__SSH_DOMAIN=git.example.pl
      - FORGEJO__server__SSH_PORT=222
      - FORGEJO__service__DISABLE_REGISTRATION=true
    volumes:
      - ./forgejo:/data
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "127.0.0.1:3002:3000"
      - "222:22"

Co tu jest ważne:

  • Zmienne w formacie FORGEJO__sekcja__KLUCZ nadpisują ustawienia z pliku app.ini. Pełną listę kluczy i ich wartości domyślnych znajdziesz w ściągawce konfiguracji.
  • ROOT_URL to publiczny adres. Bez niego Forgejo składa adres z protokołu, domeny i portu 3000, więc linki w mailach i adresy klonowania wskazywałyby http://...:3000.
  • SSH_PORT=222 zmienia tylko port pokazywany w adresie klonowania. Kontener nadal słucha na 22, a Docker przekierowuje na niego port 222 hosta. Port 22 hosta zostaje dla Twojego zwykłego SSH.
  • DISABLE_REGISTRATION=true sprawia, że konta zakłada tylko administrator. Na publicznym serwerze z otwartą rejestracją szybko pojawią się konta spamerów.
  • Interfejs WWW publikujemy tylko na 127.0.0.1, na porcie hosta 3002 (w kontenerze nadal 3000; 3000 na hoście zajmuje zwykle Umami) – na zewnątrz wystawi go nginx z HTTPS. Port 222 jest publiczny celowo; pamiętaj, że opublikowane porty Dockera omijają ufw, więc dla tego portu reguła w ufw niczego nie zmienia.

Bez konfiguracji bazy Forgejo używa SQLite. Jeśli spodziewasz się wielu użytkowników, ta sama strona dokumentacji pokazuje wariant z PostgreSQL.

bash
docker compose up -d
docker compose logs -f server

Krok 2. nginx i HTTPS

Konfiguracja według dokumentacji reverse proxy Forgejo, plik /etc/nginx/sites-available/git.example.pl:

nginx
server {
    listen 80;
    listen [::]:80;
    server_name git.example.pl;
    merge_slashes off;

    location / {
        proxy_pass http://127.0.0.1:3002;
        proxy_set_header Connection $http_connection;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        client_max_body_size 512M;
    }
}
bash
sudo ln -s /etc/nginx/sites-available/git.example.pl /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d git.example.pl

merge_slashes off dokumentacja wymaga do obsługi adresów z zakodowanymi ukośnikami, a client_max_body_size 512M pozwala wypchnąć przez HTTPS duże commity i wydania z plikami. Ogólne zasady konfiguracji proxy opisuje poradnik o reverse proxy.

Krok 3. Instalacja i konto administratora

Przy pierwszym wejściu na https://git.example.pl Forgejo pokaże stronę instalacji z polami wypełnionymi wartościami ze zmiennych środowiskowych. Zrób to od razu po uruchomieniu – dopóki instalacja nie jest zakończona, ta strona jest dostępna dla każdego. Sprawdź, czy baza to SQLite, a adres bazowy to https://git.example.pl/, rozwiń sekcję ustawień konta administratora i załóż je tam. Po instalacji Forgejo zapisze INSTALL_LOCK = true w app.ini i strona instalacji przestanie być dostępna.

Kolejne konta założysz w panelu administratora albo z wiersza poleceń (proces w kontenerze działa jako użytkownik git):

bash
docker exec -u git forgejo forgejo admin user create \
  --username ania --email ania@example.pl --random-password

Wygenerowane hasło pojawi się w wyniku polecenia – przekaż je osobie bezpiecznym kanałem.

Krok 4. Repozytoria przez SSH

Dodaj swój klucz publiczny w ustawieniach użytkownika (Ustawienia → Klucze SSH/GPG) i sprawdź połączenie:

bash
ssh -T -p 222 git@git.example.pl

Forgejo odpowie powitaniem z Twoją nazwą użytkownika – nie da powłoki, i tak ma być. Adres klonowania ma postać:

bash
git clone ssh://git@git.example.pl:222/ania/projekt.git

Żeby nie podawać portu za każdym razem, dopisz na swoim komputerze do ~/.ssh/config:

tekst
Host git.example.pl
    Port 222
    User git

Wtedy działa też krótka forma git clone git@git.example.pl:ania/projekt.git.

Krok 5. Kopie

Dokumentacja kopii Forgejo zaleca zatrzymanie Forgejo na czas kopii, bo kopia działającej instancji może być niespójna i przy odtwarzaniu stracisz dane. Przy SQLite i domyślnej konfiguracji katalog danych zawiera bazę, repozytoria, załączniki i kolejki, a app.ini z niezastępowalnymi sekretami (SECRET_KEY, JWT_SECRET) leży w nim w gitea/conf/. Wystarczy więc spakować cały katalog ./forgejo. Skrypt /opt/apps/forgejo/backup.sh:

bash
#!/usr/bin/env bash
set -euo pipefail
umask 077
cd /opt/apps/forgejo
install -d -m 700 backup
D=$(date +%F)

docker compose stop server
trap 'docker compose start server' EXIT

tar -czf "backup/forgejo-$D.tar.gz.part" forgejo
mv "backup/forgejo-$D.tar.gz.part" "backup/forgejo-$D.tar.gz"
find backup -name 'forgejo-*.tar.gz' -mtime +14 -delete
bash
chmod +x backup.sh && sudo ./backup.sh && sudo ls -lh backup
echo "15 3 * * * root /opt/apps/forgejo/backup.sh >>/var/log/forgejo-kopia.log 2>&1" | sudo tee /etc/cron.d/forgejo-kopia

trap … EXIT uruchamia Forgejo z powrotem zawsze, gdy skrypt się kończy – także po błędzie. Stare archiwa są kasowane dopiero po udanym tar: przy błędzie (np. pełny dysk) set -e przerywa skrypt przed find, więc dotychczasowe kopie zostają, a w logu /var/log/forgejo-kopia.log widać błąd. Archiwum powstaje najpierw jako .part, więc niedokończony plik nigdy nie wygląda jak gotowa kopia. umask 077 i katalog 700 sprawiają, że archiwa (z app.ini i jego sekretami) czyta tylko root. Na wszelki wypadek sprawdzaj czasem rozmiar archiwów. Przerwa trwa tyle, ile pakowanie – przy kilku gigabajtach repozytoriów to zwykle chwila. Archiwa wysyłaj poza serwer, np. jak w poradniku o restic i rclone. Przed aktualizacją do nowej wersji głównej (zmiana tagu, np. z 17 na 18) dokumentacja aktualizacji wymaga pełnej kopii.

Runner CI w skrócie

Forgejo Actions jest domyślnie włączone i używa składni bardzo podobnej do GitHub Actions. Zadania nie wykonuje jednak sam serwer, tylko osobny program Forgejo Runner (obraz data.forgejo.org/forgejo/runner). Rejestrujesz go w panelu administratora pod /admin/actions/runners przyciskiem tworzenia nowego runnera, który pokaże jednorazowy sekret do pliku konfiguracyjnego runnera. Szczegóły instalacji są w dokumentacji Actions.

Dokumentacja ostrzega, że runner wykonuje zdalny kod, co jest poważnym zagrożeniem dla maszyny i sieci, na której działa. Dlatego nie stawiaj go na tym samym VPS co Forgejo, jeśli do repozytoriów mają dostęp inne osoby – osobny mały serwer albo maszyna wirtualna ograniczają skutki złośliwego workflowu.

Kiedy wystarczy darmowy GitHub

GitHub Free daje nielimitowane repozytoria publiczne i prywatne oraz 2000 minut CI miesięcznie (szczegóły na stronie darmowe). Jeśli pracujesz sam albo w małym zespole, nie masz wymogu trzymania kodu u siebie i mieścisz się w minutach CI, własny serwer doda Ci tylko obowiązków: aktualizacje, kopie, monitoring. Forgejo ma sens, gdy kod nie może opuścić Twojej infrastruktury, chcesz uniezależnić się od jednej firmy, potrzebujesz kopii lustrzanych wielu repozytoriów albo masz już VPS i chcesz CI bez limitu minut.

Najczęstsze problemy

  • Kontener restartuje się z błędem uprawnień do /data. Katalog ./forgejo nie należy do UID 1000. Popraw: sudo chown -R 1000:1000 /opt/apps/forgejo/forgejo.
  • Adres klonowania pokazuje localhost:3000 albo http://. Brak ROOT_URL w zmiennych albo wcześniej zapisana wartość w app.ini. Sprawdź docker exec forgejo grep -A3 '^\[server\]' /data/gitea/conf/app.ini i zrestartuj kontener.
  • git push przez SSH pyta o hasło albo zwraca Permission denied (publickey). Łączysz się na port 22 hosta zamiast 222. Sprawdź ssh -T -p 222 git@git.example.pl i wpis w ~/.ssh/config.
  • SSH na 222 nie działa, gdy domena stoi za Cloudflare. Proxy Cloudflare przepuszcza tylko HTTP i HTTPS. Dla SSH ustaw osobny rekord bez proxy, np. ssh.git.example.pl, i podaj go w SSH_DOMAIN – więcej w poradniku o Cloudflare.
  • Wypchnięcie dużego repozytorium przez HTTPS kończy się błędem 413. Za mały client_max_body_size w bloku HTTPS po certbocie – sprawdź sudo nginx -T | grep client_max_body_size.

Co dalej

Dodaj monitoring adresu w Uptime Kuma i przejdź listę kontrolną zabezpieczeń. Jeśli na tym samym serwerze stoją inne narzędzia, uporządkuj je jak w stacku w jednym pliku compose.

Powiązane poradniki