Skip to main content

Self-Host Forgejo: Open Source GitHub Alternative 2026

·OSSAlt Team
forgejogiteagithubself-hostingdockergitci-cd2026

TL;DR

Forgejo is a community-governed fork of Gitea — a self-hosted Git platform with repositories, issues, pull requests, Actions CI/CD, package registry, and Pages. GPL 3.0, ~10K stars. Forgejo was created in 2022 after Gitea's governance controversy (Gitea Ltd incorporated without community input). If you're choosing between Forgejo and Gitea in 2026: Forgejo has stronger community governance, Gitea has the more active codebase. Both run the same Docker image format and are 99% compatible.

Key Takeaways

  • Forgejo: GPL 3.0 (true open source), ~10K stars, Forgejo e.V. community governance
  • Forgejo vs Gitea: Same codebase origin; Forgejo focuses on community/open governance, Gitea on enterprise features
  • Actions: Full GitHub Actions-compatible CI/CD via forgejo-runner
  • Packages: Container registry, npm, PyPI, Maven packages
  • Resources: ~100MB RAM idle — runs comfortably on a $6/month VPS
  • Migration: Import repos from GitHub, GitLab, Bitbucket with one click

Forgejo vs Gitea vs GitLab

FeatureForgejoGiteaGitLab CE
LicenseGPL 3.0MITMIT
GovernanceCommunity (e.V.)Gitea LtdGitLab Inc
GitHub Stars~10K~46K~24K
RAM (idle)~100MB~100MB2–4GB
Actions CI/CD✅ (forgejo-runner)✅ (act_runner)✅ (GitLab Runner)
Container registry
Pages
Wiki
Issue tracker
Code review
Merge trains
RBACBasicBasicAdvanced

GitLab vs Forgejo: GitLab CE is full-featured but needs 4–8GB RAM minimum. For small teams and personal use, Forgejo is far more resource-efficient.


Part 1: Docker Compose Setup

# docker-compose.yml
version: '3.8'

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:9
    container_name: forgejo
    restart: unless-stopped
    ports:
      - "3000:3000"     # Web UI
      - "2222:22"       # SSH (map to non-standard host port to avoid conflict)
    environment:
      USER_UID: 1000
      USER_GID: 1000
      FORGEJO__database__DB_TYPE: postgres
      FORGEJO__database__HOST: db:5432
      FORGEJO__database__NAME: forgejo
      FORGEJO__database__USER: forgejo
      FORGEJO__database__PASSWD: "${DB_PASSWORD}"
      FORGEJO__server__DOMAIN: git.yourdomain.com
      FORGEJO__server__SSH_DOMAIN: git.yourdomain.com
      FORGEJO__server__ROOT_URL: https://git.yourdomain.com/
      FORGEJO__server__SSH_PORT: 2222
      FORGEJO__server__SSH_LISTEN_PORT: 22
    volumes:
      - forgejo_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: forgejo
      POSTGRES_USER: forgejo
      POSTGRES_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - forgejo_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U forgejo"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  forgejo_data:
  forgejo_db:
# .env
DB_PASSWORD=strong-database-password
docker compose up -d

Visit http://your-server:3000 → complete the setup wizard (admin account, instance settings).


Part 2: HTTPS with Caddy

git.yourdomain.com {
    reverse_proxy localhost:3000
}

After adding Caddy, update your Forgejo app.ini (or Docker env vars) to reflect the HTTPS URLs.


Part 3: SSH Configuration

SSH is how Git push/pull works over SSH protocol. With port mapping 2222:22:

# Clone via SSH:
git clone ssh://git@git.yourdomain.com:2222/username/repo.git

# Add SSH key to Forgejo:
# Profile → Settings → SSH / GPG Keys → Add Key
# Paste your ~/.ssh/id_ed25519.pub

SSH Config Shortcut

Add to ~/.ssh/config:

Host forgejo
    HostName git.yourdomain.com
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519

Then clone with:

git clone forgejo:username/repo.git

Part 4: Add forgejo-runner (Actions CI/CD)

Forgejo Actions is GitHub Actions-compatible — your existing .github/workflows/ files work with minor path adjustments.

Deploy forgejo-runner

# Add to docker-compose.yml:

  forgejo-runner:
    image: codeberg.org/forgejo/runner:6
    container_name: forgejo-runner
    restart: unless-stopped
    volumes:
      - ./runner:/data
      - /var/run/docker.sock:/var/run/docker.sock   # For Docker-in-Docker jobs
    environment:
      CONFIG_FILE: /data/config.yml
      FORGEJO_INSTANCE_URL: "https://git.yourdomain.com"
      FORGEJO_RUNNER_TOKEN: "${RUNNER_TOKEN}"
      FORGEJO_RUNNER_NAME: "docker-runner"
    depends_on:
      - forgejo

Get Runner Token

  1. In Forgejo: Admin Panel → Runners → Create new runner
  2. Copy the registration token
  3. Add to .env: RUNNER_TOKEN=your-registration-token

Workflow Example (GitHub Actions-compatible)

# .forgejo/workflows/ci.yml  (or .github/workflows/ci.yml — both work)
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: docker
    container:
      image: node:20-alpine

    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build

  docker-build:
    runs-on: docker
    needs: test
    steps:
      - uses: actions/checkout@v4

      - name: Build and push Docker image
        run: |
          docker login git.yourdomain.com -u ${{ github.actor }} \
            -p ${{ secrets.FORGEJO_TOKEN }}
          docker build -t git.yourdomain.com/${{ github.repository }}:latest .
          docker push git.yourdomain.com/${{ github.repository }}:latest

Part 5: Container Registry

Forgejo includes a built-in container registry at git.yourdomain.com:

# Login to Forgejo registry:
docker login git.yourdomain.com \
  -u your-username \
  -p your-access-token    # Create in Profile → Settings → Applications

# Tag and push:
docker tag myapp:latest git.yourdomain.com/username/myapp:latest
docker push git.yourdomain.com/username/myapp:latest

# Pull:
docker pull git.yourdomain.com/username/myapp:latest

Enable in Forgejo admin: Administration → Configuration → Packages → Enable.


Part 6: Migrate from GitHub

Forgejo supports one-click migration from GitHub, GitLab, and Bitbucket:

  1. Explore → Create New Migration
  2. Select GitHub
  3. Enter GitHub personal access token (needed for private repos)
  4. Select repositories to migrate
  5. Options: migrate issues, pull requests, labels, milestones, wiki, releases

After migration, update your local git remotes:

# Update remote URL:
git remote set-url origin ssh://git@git.yourdomain.com:2222/username/repo.git

# Verify:
git remote -v

Mirror a GitHub Repository (keep in sync)

Explore → New Repository → Migration → Mirror

Forgejo pulls updates from GitHub on a schedule — useful for having a backup of public repositories you depend on.


Part 7: Webhooks and Integrations

Forgejo webhooks integrate with external services:

Repository → Settings → Webhooks → Add Webhook

Webhook to n8n/Slack on Push

Payload URL: https://n8n.yourdomain.com/webhook/forgejo-push Events: Push events, Pull request events

Deploy on Push (Watchtower-style)

# Simple deploy webhook:
curl -X POST 'https://your-app-server/deploy' \
  -H 'X-Forgejo-Signature: ...' \
  -d '{"ref": "refs/heads/main"}'

Part 8: Forgejo Pages

Host static sites directly from Forgejo repositories:

  1. Create a repo named username.yourdomain.com or enable Pages in repo settings
  2. Push a gh-pages branch or configure source branch
  3. Site available at https://pages.yourdomain.com/username/repo

Requires: Setting FORGEJO__server__PAGES_DOMAINS=pages.yourdomain.com in config.


App.ini Configuration

For full control, mount a custom app.ini:

[server]
DOMAIN           = git.yourdomain.com
HTTP_PORT        = 3000
ROOT_URL         = https://git.yourdomain.com/
SSH_DOMAIN       = git.yourdomain.com
SSH_PORT         = 2222
DISABLE_SSH      = false
OFFLINE_MODE     = false

[database]
DB_TYPE  = postgres
HOST     = db:5432
NAME     = forgejo
USER     = forgejo
PASSWD   = your-password

[service]
REGISTER_EMAIL_CONFIRM            = false
ENABLE_NOTIFY_MAIL                = false
DISABLE_REGISTRATION              = true     # Disable after setup!
REQUIRE_SIGNIN_VIEW               = true     # Private instance
DEFAULT_KEEP_EMAIL_PRIVATE        = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = true

[mailer]
ENABLED = true
FROM    = forgejo@yourdomain.com
PROTOCOL = smtp
SMTP_ADDR = smtp.yourdomain.com
SMTP_PORT = 587
USER     = forgejo@yourdomain.com
PASSWD   = smtp-password

[security]
INSTALL_LOCK = true
SECRET_KEY   = generate-with-openssl-rand-hex-32

Maintenance

# Update Forgejo:
docker compose pull
docker compose up -d

# Backup:
# Database:
docker exec forgejo-db pg_dump -U forgejo forgejo | \
  gzip > forgejo-db-$(date +%Y%m%d).sql.gz

# Repository data:
tar -czf forgejo-data-$(date +%Y%m%d).tar.gz \
  $(docker volume inspect forgejo_forgejo_data --format '{{.Mountpoint}}')

# Run admin tasks:
docker exec -u git forgejo forgejo admin user list
docker exec -u git forgejo forgejo admin generate-secret SECRET_KEY

Forgejo vs Gitea: Which to Choose?

Both are excellent. The practical differences in 2026:

Choose Forgejo if:

  • Open source governance matters to you (GPL 3.0, community non-profit)
  • You want to contribute and ensure no future corporate pivot
  • You prefer Codeberg.org as a codebase/community hub

Choose Gitea if:

  • You want the larger community and more third-party integrations
  • MIT license preferred
  • Enterprise support is on your roadmap

Either works. Both share the same original codebase, both run the same Docker workflow, and both fully replace GitHub for self-hosted use.


See all open source GitHub alternatives at OSSAlt.com/alternatives/github.

Comments