← All articles
White letters nfo on dark gray background

Forgejo: Self-Hosted Git Hosting Without the GitHub Lock-In

Development 2026-03-04 · 5 min read forgejo gitea git self-hosted docker ci-cd devops
By Selfhosted Guides Editorial TeamSelf-hosting practitioners covering open source software, home lab infrastructure, and data sovereignty.

Forgejo: Self-Hosted Git Hosting Without the GitHub Lock-In

Photo by Arno Senoner on Unsplash

If you want to own your code infrastructure without building it yourself, Forgejo is the answer. It's a fully-featured Git hosting platform — pull requests, issues, CI/CD pipelines, container registries, and more — that you can run on a $5 VPS or your homelab server. No per-seat pricing, no vendor lock-in.

Forgejo is a community-controlled fork of Gitea, created in 2022 when Gitea formed a commercial entity. Forgejo commits to staying open-source and community-governed.

What Forgejo Includes

Hardware Requirements

Forgejo is exceptionally lightweight:

It runs comfortably on a Raspberry Pi 4 or a small VPS.

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"   # HTTP
      - "22:22"       # SSH (or use 2222 and configure SSH client)
    volumes:
      - forgejo-data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    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: strongpassword
    depends_on:
      - db

  db:
    image: postgres:15
    container_name: forgejo-db
    restart: unless-stopped
    volumes:
      - forgejo-pg:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: forgejo
      POSTGRES_USER: forgejo
      POSTGRES_PASSWORD: strongpassword

volumes:
  forgejo-data:
  forgejo-pg:

Start it:

docker compose up -d

Then visit http://your-server:3000 to complete the setup wizard.

Setup Wizard Checklist

  1. Database: Should auto-populate from env vars — verify it shows PostgreSQL
  2. Server domain: Set to your actual domain (e.g., git.yourdomain.com)
  3. Application URL: Set to https://git.yourdomain.com
  4. Admin account: Create your admin user here
  5. Skip optional settings for now — you can configure them later

Like what you're reading? Subscribe to Self-Hosted Weekly — free weekly guides in your inbox.

HTTPS with Caddy

git.yourdomain.com {
    reverse_proxy localhost:3000
}

After setting up HTTPS, update your Forgejo config:

# Edit /path/to/forgejo-data/gitea/conf/app.ini
# (or set via environment variable)
FORGEJO__server__ROOT_URL = https://git.yourdomain.com
FORGEJO__server__DOMAIN = git.yourdomain.com
FORGEJO__server__SSH_DOMAIN = git.yourdomain.com

Then restart the container.

SSH Configuration

SSH on port 22 can conflict with your host's SSH daemon. Two options:

Option 1: Move Forgejo SSH to port 2222 (easier):

ports:
  - "3000:3000"
  - "2222:22"

Users then clone with: git clone ssh://[email protected]:2222/user/repo.git

Option 2: Use SSH passthrough (port 22, no conflict):

Create a git user on the host:

sudo adduser git --disabled-password --shell /bin/bash

Add to your host's ~/.ssh/authorized_keys:

command="ssh -p 22 -o StrictHostKeyChecking=no [email protected] 'SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $SSH_ORIGINAL_COMMAND'" ssh-rsa AAAA...

This is more complex to set up but lets users use the standard port 22.

Forgejo Actions (CI/CD)

Forgejo Actions is compatible with GitHub Actions workflow syntax. Workflows run on self-hosted runners called "act_runner".

Installing the Runner

# On the machine that will run CI jobs
docker run -d \
  --name forgejo-runner \
  --restart unless-stopped \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v forgejo-runner:/data \
  code.forgejo.org/forgejo/runner:latest \
  forgejo-runner daemon

Register the runner with your Forgejo instance:

docker exec -it forgejo-runner \
  forgejo-runner register \
  --no-interactive \
  --token YOUR_RUNNER_TOKEN \
  --name my-runner \
  --instance https://git.yourdomain.com \
  --labels docker,linux,amd64

Get the runner token from Site Administration → Actions → Runners.

Example Workflow

Create .forgejo/workflows/ci.yml in your repository:

name: CI

on:
  push:
    branches: [main]
  pull_request:

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

    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build

The syntax is nearly identical to GitHub Actions. Most actions/* steps work because Forgejo Actions can pull from GitHub-hosted actions.

Package Registry

Forgejo includes a built-in package registry. Enable it in your app.ini:

[packages]
ENABLED = true

Publishing an npm Package

npm config set registry https://git.yourdomain.com/api/packages/YOUR_ORG/npm/
npm config set //git.yourdomain.com/api/packages/YOUR_ORG/npm/:_authToken YOUR_TOKEN

npm publish

Publishing a Docker Image

docker login git.yourdomain.com
docker tag myimage git.yourdomain.com/youruser/myimage:latest
docker push git.yourdomain.com/youruser/myimage:latest

Migrating from GitHub

Forgejo can mirror entire GitHub repositories or do one-time migrations.

Mirror an existing repo (stays in sync):

  1. Go to + → New Migration
  2. Choose GitHub
  3. Enter your GitHub token and repository URL
  4. Enable Mirror and set the interval

One-time import:

  1. Go to + → New Migration
  2. Choose GitHub
  3. Uncheck Mirror
  4. Imports issues, PRs, labels, milestones, and releases

For bulk migration, use the Forgejo API:

# Migrate a repo via API
curl -X POST https://git.yourdomain.com/api/v1/repos/migrate \
  -H "Authorization: token YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "clone_addr": "https://github.com/org/repo",
    "auth_token": "GITHUB_TOKEN",
    "repo_name": "repo",
    "uid": 1,
    "mirror": false
  }'

OIDC / SSO Integration

Forgejo supports OAuth2 providers. To integrate Authentik:

  1. In Authentik, create an OAuth2/OpenID provider
  2. Set redirect URI to https://git.yourdomain.com/user/oauth2/authentik/callback
  3. In Forgejo: Site Administration → Authentication Sources → Add OAuth2
  4. Provider: OpenID Connect
  5. Client ID/Secret from Authentik
  6. OpenID Connect Auto Discovery URL: https://auth.yourdomain.com/application/o/forgejo/.well-known/openid-configuration

Users will see a "Sign in with Authentik" button on the login page.

Organization and Team Permissions

For team-based access control:

  1. Create an Organization for your team/company
  2. Create Teams with specific permissions (read, write, admin)
  3. Add Members to teams
  4. Add Repositories to teams

This lets you give a contractor read-only access to one repo while your full team has write access across all repos.

Backup and Restore

# Backup (stops Forgejo briefly)
docker exec forgejo forgejo admin graceful-restart
docker run --rm \
  -v forgejo-data:/data \
  -v /backup:/backup \
  alpine tar czf /backup/forgejo-$(date +%Y%m%d).tar.gz /data

# Database backup (separate)
docker exec forgejo-db pg_dump -U forgejo forgejo \
  > /backup/forgejo-db-$(date +%Y%m%d).sql

Set this up as a daily cron job. Store backups off-site (S3, Backblaze B2, or rsync to another host).

Forgejo vs Gitea vs GitLab

Feature Forgejo Gitea GitLab CE
Governance Community Commercial Commercial
Resource usage Very light Very light Heavy
CI/CD Actions Actions GitLab CI
Container registry
Kubernetes-native ✅ (via Helm)
Enterprise features Basic Basic Extensive

Choose Forgejo if you want a lightweight GitHub alternative with community governance. Choose GitLab CE if you need enterprise-grade features and have the hardware to run it (GitLab wants at least 4GB RAM and 4 CPU cores).

Troubleshooting

Push to SSH fails: Run ssh -v [email protected] to debug. Check that the git user's authorized keys are synced from Forgejo (they live in forgejo-data/gitea/.ssh/).

Actions runner not connecting: Check the runner token is correct and that the runner can reach your Forgejo instance on port 443/3000. Firewall issues are the most common cause.

Large repo clone is slow: Enable Git LFS for large files. Forgejo supports LFS natively — configure it in repo settings.

Wrapping Up

Forgejo gives you a GitHub-equivalent experience on hardware you control. The CI/CD system is genuinely capable, the package registry covers the common cases, and the migration tools make it straightforward to pull in repos from GitHub.

For a homelab or small team, Forgejo is hard to beat — it runs on minimal hardware, stays out of the way, and lets you own your entire development workflow.

Get free weekly tips in your inbox. Subscribe to Self-Hosted Weekly