Dein eigener Git-Server mit automatischem Deployment: Forgejo + Docker + Traefik

Wie man sein eigenes kleines Deployment System aufbauen kann ohne einem Konzern Geld in den Rachen zu werfen

Dein eigener Git-Server mit automatischem Deployment: Forgejo + Docker + Traefik

Du hostest deine Projekte bei GitHub oder GitLab – aber hast du dich schon mal gefragt, wie es wäre, deinen eigenen Git-Server zu betreiben? Mit automatischen Deployments, SSL-Zertifikaten und voller Kontrolle über deine Daten?

In diesem Artikel zeige ich dir, wie du mit Forgejo, Docker und Traefik eine komplette CI/CD-Pipeline auf deinem eigenen Server einrichtest. Kein GitHub Actions, keine externen Dienste – alles läuft auf deiner eigenen Infrastruktur.


Warum ein eigener Git-Server?

Bevor wir ins Technische einsteigen: Lohnt sich das überhaupt?

Die Vorteile:

  • Datensouveränität – Dein Code bleibt bei dir
  • Keine Limits – Keine Runner-Minuten, keine Storage-Grenzen
  • Lerneffekt – Du verstehst, was hinter den Kulissen passiert
  • Kosten – Ein kleiner VPS - kostet weniger als GitHub Enterprise

Die Nachteile:

  • Du bist für Wartung und Backups verantwortlich
  • Initiales Setup braucht Zeit

Wenn du einen VPS herumliegen hast (oder einen mieten willst), ist das ein ideales Wochenendprojekt.


Die Architektur

Unser Setup besteht aus drei Hauptkomponenten:

                    ┌─────────────────┐
                    │    Internet     │
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │     Traefik     │
                    │  (Reverse Proxy)│
                    │  SSL-Zertifikate│
                    └────────┬────────┘
                             │
          ┌──────────────────┼──────────────────┐
          │                  │                  │
   ┌──────▼──────┐    ┌──────▼──────┐    ┌──────▼──────┐
   │   Forgejo   │    │  Deine App  │    │  Weitere    │
   │  (Git+CI/CD)│    │  Container  │    │   Apps...   │
   └─────────────┘    └─────────────┘    └─────────────┘

Traefik ist der Türsteher: Er empfängt alle Anfragen, kümmert sich um SSL-Zertifikate (automatisch via Let's Encrypt) und leitet die Anfragen an die richtigen Container weiter.

Forgejo ist dein Git-Server mit eingebautem CI/CD-Runner – quasi GitHub in einer einzigen Anwendung.

Deine Apps werden automatisch deployt, sobald du Code pushst.


Voraussetzungen

  • Ein VPS mit Linux (Ubuntu/Debian empfohlen), mindestens 2 GB RAM
  • Eine Domain mit Wildcard-DNS (z.B. *.deine-domain.de → VPS-IP)
  • Grundkenntnisse in Docker und der Kommandozeile
  • SSH-Zugang zum Server

Teil 1: Traefik einrichten

Traefik ist das Herzstück unseres Setups. Er übernimmt:

  • HTTPS-Terminierung mit automatischen Let's Encrypt-Zertifikaten
  • Routing zu verschiedenen Containern basierend auf der Domain
  • Load Balancing (falls du das mal brauchst)

Docker-Netzwerk erstellen

Zuerst brauchen wir ein Netzwerk, über das alle Container kommunizieren:

docker network create traefik-public

Traefik starten

Erstelle ein Verzeichnis und die Konfiguration:

mkdir -p /opt/traefik
cd /opt/traefik

docker-compose.yml:

services:
  traefik:
    image: traefik:v3
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=deine@email.de"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-certificates:/letsencrypt
    networks:
      - traefik-public
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.deine-domain.de`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
      - "traefik.http.routers.dashboard.service=api@internal"
      # Basic Auth für Dashboard (optional aber empfohlen)
      # - "traefik.http.routers.dashboard.middlewares=auth"
      # - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$..."

volumes:
  traefik-certificates:

networks:
  traefik-public:
    external: true

Starten mit:

docker compose up -d

Tipp: Ersetze deine@email.de und deine-domain.de mit deinen echten Werten. Die E-Mail wird nur für Let's Encrypt-Benachrichtigungen verwendet.


Teil 2: Forgejo installieren

Forgejo ist ein Fork von Gitea – leichtgewichtig, schnell und mit allem ausgestattet, was du brauchst.

Verzeichnis und Konfiguration

mkdir -p /opt/forgejo
cd /opt/forgejo

docker-compose.yml:

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:9
    container_name: forgejo
    restart: unless-stopped
    environment:
      - USER_UID=1000
      - USER_GID=1000
    volumes:
      - forgejo-data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    networks:
      - traefik-public
    ports:
      - "2222:22"  # SSH für Git
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.forgejo.rule=Host(`git.deine-domain.de`)"
      - "traefik.http.routers.forgejo.entrypoints=websecure"
      - "traefik.http.routers.forgejo.tls.certresolver=letsencrypt"
      - "traefik.http.services.forgejo.loadbalancer.server.port=3000"

volumes:
  forgejo-data:

networks:
  traefik-public:
    external: true

Starten:

docker compose up -d

Öffne https://git.deine-domain.de und folge dem Setup-Wizard. Die Standardwerte sind meist okay – achte nur darauf, dass du dir ein Admin-Konto erstellst.

SSH-Konfiguration

Damit git clone git@git.deine-domain.de:user/repo.git funktioniert, brauchst du einen Eintrag in ~/.ssh/config:

Host git.deine-domain.de
    HostName git.deine-domain.de
    Port 2222
    User git
    IdentityFile ~/.ssh/id_rsa

Teil 3: Den CI/CD-Runner einrichten

Forgejo kann selbst als Runner fungieren – das ist das Geniale daran.

Runner registrieren

  1. Gehe in Forgejo zu Site Administration → Actions → Runners
  2. Klicke auf Create new Runner und kopiere das Token

Runner starten

Füge den Runner zu deiner Forgejo docker-compose.yml hinzu:

  runner:
    image: code.forgejo.org/forgejo/runner:6
    container_name: forgejo-runner
    restart: unless-stopped
    depends_on:
      - forgejo
    volumes:
      - forgejo-runner-data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DOCKER_HOST=unix:///var/run/docker.sock
    command: >
      sh -c '
      forgejo-runner register --no-interactive
        --instance https://git.deine-domain.de
        --token DEIN_TOKEN
        --name docker-runner
        --labels docker:docker://node:20-alpine &&
      forgejo-runner daemon
      '

volumes:
  # ... bestehende Volumes
  forgejo-runner-data:

Ersetze DEIN_TOKEN mit dem kopierten Token und starte neu:

docker compose up -d

Der Runner sollte jetzt unter Site Administration → Actions → Runners als "Idle" erscheinen.


Teil 4: Deine erste automatische Deployment-Pipeline

Jetzt wird's spannend: Wir erstellen ein Projekt, das sich bei jedem Push automatisch deployt.

SSH-Key für Deployments

Der Runner braucht SSH-Zugang zum Server. Erstelle einen dedizierten Key:

ssh-keygen -t ed25519 -f ~/.ssh/deploy-key -N "" -C "ci-deploy"

Füge den Public Key zu den authorized_keys auf dem Server hinzu:

cat ~/.ssh/deploy-key.pub >> ~/.ssh/authorized_keys

Repository erstellen

  1. Erstelle ein neues Repository in Forgejo
  2. Gehe zu Settings → Actions → Secrets
  3. Füge SSH_PRIVATE_KEY hinzu (Inhalt von ~/.ssh/deploy-key)
  4. Gehe zu Settings → Actions → Variables
  5. Füge hinzu:
    • APP_NAME: Name deiner App (z.B. meine-app)
    • DOMAIN: Domain (z.B. meine-app.deine-domain.de)

Wichtig: Vermeide Unterstriche in Namen und Domains! Let's Encrypt mag die nicht, und du bekommst Zertifikatsfehler.

Die Pipeline

Erstelle im Repository die Datei .forgejo/workflows/deploy.yml:

name: Build and Deploy

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: docker
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H DEINE_SERVER_IP >> ~/.ssh/known_hosts

      - name: Deploy
        env:
          APP_NAME: ${{ vars.APP_NAME }}
          DOMAIN: ${{ vars.DOMAIN }}
        run: |
          SERVER="root@DEINE_SERVER_IP"
          DEPLOY_PATH="/opt/apps/${APP_NAME}"

          # Verzeichnis erstellen
          ssh $SERVER "mkdir -p ${DEPLOY_PATH}"

          # Dateien übertragen
          tar --exclude='.git' --exclude='.forgejo' -czf - . | \
            ssh $SERVER "cd ${DEPLOY_PATH} && tar -xzf -"

          # Umgebungsvariablen setzen
          ssh $SERVER "cat > ${DEPLOY_PATH}/.env << EOF
          APP_NAME=${APP_NAME}
          DOMAIN=${DOMAIN}
          EOF"

          # Container neu starten
          ssh $SERVER "cd ${DEPLOY_PATH} && docker compose up -d --build"

          echo "Deployed to https://${DOMAIN}"

Docker Compose für deine App

Die docker-compose.yml im Repository:

services:
  app:
    build: .
    container_name: ${APP_NAME}
    restart: unless-stopped
    networks:
      - traefik-public
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.${APP_NAME}.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.${APP_NAME}.entrypoints=websecure"
      - "traefik.http.routers.${APP_NAME}.tls.certresolver=letsencrypt"

networks:
  traefik-public:
    external: true

Dockerfile

Für eine einfache statische Seite:

FROM nginx:alpine
COPY public/ /usr/share/nginx/html/

Für eine Node.js-App:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Push und beobachten

git add .
git commit -m "Initial deployment setup"
git push -u origin main

Gehe in Forgejo zu Actions und beobachte, wie dein Code automatisch deployt wird. Nach ein paar Sekunden ist deine App unter https://meine-app.deine-domain.de erreichbar – mit gültigem SSL-Zertifikat.


Der Workflow im Alltag

Ab jetzt ist Deployen trivial:

# Änderungen machen
git add .
git commit -m "Neues Feature"
git push

Das war's. Die Pipeline baut und deployed automatisch. Du kannst den Fortschritt in Forgejo unter Actions verfolgen.


Troubleshooting

SSL-Zertifikat fehlt oder ungültig:

  • Prüfe, ob Unterstriche in der Domain sind (ersetze _ durch -)
  • Schau in die Traefik-Logs: docker logs traefik

Pipeline startet nicht:

  • Ist die Workflow-Datei in .forgejo/workflows/?
  • Pushst du auf den main-Branch?
  • Ist der Runner online? (Admin → Actions → Runners)

SSH-Fehler:

  • Ist der Private Key korrekt im Secret hinterlegt?
  • Ist der Public Key auf dem Server?
  • Stimmt die Server-IP in der Pipeline?

Container startet nicht:

  • Logs prüfen: docker logs CONTAINER_NAME
  • Ist das traefik-public Netzwerk vorhanden?

Fazit

Mit Forgejo, Traefik und Docker hast du eine komplette DevOps-Infrastruktur auf deinem eigenen Server. Kein Vendor Lock-in, volle Kontrolle, und du lernst dabei, wie die Tools funktionieren, die du sonst als Black Box nutzt.

Das Setup braucht initial etwas Zeit, aber danach ist es wartungsarm und skaliert gut. Du kannst beliebig viele Projekte hinzufügen – jedes mit eigener Domain und automatischem Deployment.

Nächste Schritte:

  • Richte automatische Backups für Forgejo ein
  • Füge ein Monitoring hinzu (z.B. Uptime Kuma)
  • Experimentiere mit verschiedenen App-Typen

Viel Spaß beim Selbsthosten!