Self-Host Forgejo: Open Source GitHub Alternative 2026
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
| Feature | Forgejo | Gitea | GitLab CE |
|---|---|---|---|
| License | GPL 3.0 | MIT | MIT |
| Governance | Community (e.V.) | Gitea Ltd | GitLab Inc |
| GitHub Stars | ~10K | ~46K | ~24K |
| RAM (idle) | ~100MB | ~100MB | 2–4GB |
| Actions CI/CD | ✅ (forgejo-runner) | ✅ (act_runner) | ✅ (GitLab Runner) |
| Container registry | ✅ | ✅ | ✅ |
| Pages | ✅ | ✅ | ✅ |
| Wiki | ✅ | ✅ | ✅ |
| Issue tracker | ✅ | ✅ | ✅ |
| Code review | ✅ | ✅ | ✅ |
| Merge trains | ❌ | ❌ | ✅ |
| RBAC | Basic | Basic | Advanced |
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
- In Forgejo: Admin Panel → Runners → Create new runner
- Copy the registration token
- 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:
- Explore → Create New Migration
- Select GitHub
- Enter GitHub personal access token (needed for private repos)
- Select repositories to migrate
- 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:
- Create a repo named
username.yourdomain.comor enable Pages in repo settings - Push a
gh-pagesbranch or configure source branch - 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.