How to Self-Host Forgejo: Community Git Forge 2026
TL;DR
Forgejo (GPL 3.0, ~11K GitHub stars, Go) is the community-driven fork of Gitea — created in 2022 when Gitea's core team moved to a for-profit model. Forgejo is 100% volunteer-run and a Codeberg project. It's functionally near-identical to Gitea (same Docker image, same API, same YAML CI syntax) but with a stronger governance model, more active federation work, and stricter open-source principles. If you want Gitea's features but prefer a community project with no corporate backing, Forgejo is the choice.
Key Takeaways
- Forgejo: GPL 3.0, ~11K stars — Gitea fork with community governance and no corporate backing
- Drop-in Gitea replacement: Same Docker config, same API, same CI YAML — migrates in minutes
- Forgejo Actions: GitHub Actions-compatible CI/CD (same as Gitea Actions)
- Federation: Actively working on ActivityPub federation (F3 protocol) for cross-forge PRs
- Codeberg hosted: codeberg.org runs Forgejo — the largest public instance
- Migration: Migrate from Gitea by just switching the Docker image tag
Forgejo vs Gitea
| Aspect | Forgejo | Gitea |
|---|---|---|
| License | GPL 3.0 | MIT |
| Governance | Nonprofit/community | Gitea Inc. (for-profit) |
| GitHub Stars | ~11K | ~43K |
| Gitea compatibility | Near 100% | — |
| CI/CD | Forgejo Actions (GH Actions compatible) | Gitea Actions (GH Actions compatible) |
| ActivityPub federation | Active development | Experimental |
| Public instance | codeberg.org | gitea.com |
| Release cadence | More frequent patches | Quarterly |
Part 1: Docker Setup
Forgejo uses the same configuration structure as Gitea — just swap the image:
# docker-compose.yml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:latest
container_name: forgejo
restart: unless-stopped
ports:
- "3000:3000"
- "2222:22"
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: postgres:5432
FORGEJO__database__NAME: forgejo
FORGEJO__database__USER: forgejo
FORGEJO__database__PASSWD: "${POSTGRES_PASSWORD}"
FORGEJO__server__DOMAIN: "git.yourdomain.com"
FORGEJO__server__ROOT_URL: "https://git.yourdomain.com"
FORGEJO__server__SSH_DOMAIN: "git.yourdomain.com"
FORGEJO__server__SSH_PORT: 2222
FORGEJO__server__HTTP_PORT: 3000
FORGEJO__service__DISABLE_REGISTRATION: "false" # Enable for initial setup
FORGEJO__federation__ENABLED: "true" # Enable ActivityPub federation
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
POSTGRES_DB: forgejo
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U forgejo"]
interval: 10s
start_period: 20s
volumes:
forgejo_data:
postgres_data:
# .env
POSTGRES_PASSWORD=your-secure-db-password
docker compose up -d
Visit http://your-server:3000 → complete the installation wizard.
Part 2: HTTPS with Caddy
git.yourdomain.com {
reverse_proxy localhost:3000
}
Part 3: Migrate from Gitea
If you're already running Gitea, migration is a single image swap:
# 1. Stop Gitea:
docker compose stop gitea
# 2. Backup data:
tar -czf gitea-backup-$(date +%Y%m%d).tar.gz \
$(docker volume inspect gitea_gitea_data --format '{{.Mountpoint}}')
# 3. Update docker-compose.yml:
# Change: image: gitea/gitea:latest
# To: image: codeberg.org/forgejo/forgejo:latest
# Change: GITEA__* env vars to FORGEJO__*
# 4. Start Forgejo with existing data volume:
docker compose up -d
# Forgejo auto-migrates Gitea database schema
Env var prefix change: Gitea uses GITEA__ prefix, Forgejo uses FORGEJO__ prefix. All other settings are identical.
Part 4: Forgejo Actions (CI/CD)
Enable Forgejo Actions (GitHub Actions-compatible):
environment:
FORGEJO__actions__ENABLED: "true"
Run a Forgejo Runner
# Add to docker-compose.yml:
services:
forgejo-runner:
image: code.forgejo.org/forgejo/runner:latest
container_name: forgejo_runner
restart: unless-stopped
volumes:
- ./runner-config.yaml:/config.yaml
- forgejo_runner_data:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
CONFIG_FILE: /config.yaml
FORGEJO_INSTANCE_URL: "https://git.yourdomain.com"
FORGEJO_RUNNER_REGISTRATION_TOKEN: "${RUNNER_TOKEN}"
FORGEJO_RUNNER_NAME: "docker-runner"
# Register runner (get token from Site Administration → Runners):
docker exec forgejo_runner forgejo-runner register \
--instance https://git.yourdomain.com \
--token YOUR_RUNNER_TOKEN \
--name docker-runner \
--labels ubuntu-latest:docker://node:20-bullseye
Workflow example (GitHub Actions compatible)
# .forgejo/workflows/ci.yml
# Identical syntax to GitHub Actions / Gitea Actions
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: |
npm ci
npm test
npm run build
release:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create release
uses: https://codeberg.org/forgejo/forgejo-action-release@v1
with:
direction: release
Part 5: ActivityPub Federation
Forgejo is building F3 (Federated Forge Format) — cross-forge pull requests and issues:
environment:
FORGEJO__federation__ENABLED: "true"
What federation enables (current state):
- Your Forgejo instance is discoverable via ActivityPub
- Users on other Forgejo/Gitea instances can be
@mentionedcross-instance - Cross-forge stars (experimental)
Roadmap: Full cross-forge pull requests (PR from alice@other-forge.com to a repo on your instance) — the ForgeFed protocol specification.
# Verify federation is working:
curl https://git.yourdomain.com/.well-known/webfinger?resource=acct:admin@git.yourdomain.com
# Returns: ActivityPub WebFinger response
Part 6: Packages and Registries
Forgejo includes multiple package registries:
# Container registry:
docker login git.yourdomain.com
docker push git.yourdomain.com/username/myimage:latest
# npm registry:
npm config set @myorg:registry https://git.yourdomain.com/api/packages/username/npm/
npm login --scope=@myorg --registry=https://git.yourdomain.com/api/packages/username/npm/
npm publish --scope=@myorg
# PyPI:
pip install --index-url https://username:token@git.yourdomain.com/api/packages/username/pypi/simple/ mypackage
# Maven (in pom.xml):
# <repository>
# <id>forgejo</id>
# <url>https://git.yourdomain.com/api/packages/username/maven</url>
# </repository>
Part 7: Migrate from GitHub
# Single repo via UI:
# + → New Migration → GitHub → enter URL + token
# Bulk migration script (same as Gitea):
#!/bin/bash
GITHUB_TOKEN="ghp_yourtoken"
FORGEJO_URL="https://git.yourdomain.com"
FORGEJO_TOKEN="your-forgejo-token"
REPOS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/user/repos?per_page=100" | jq -r '.[].full_name')
for REPO in $REPOS; do
curl -X POST "${FORGEJO_URL}/api/v1/repos/migrate" \
-H "Authorization: token ${FORGEJO_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"clone_addr\": \"https://github.com/${REPO}\",
\"auth_token\": \"${GITHUB_TOKEN}\",
\"repo_name\": \"$(echo $REPO | cut -d/ -f2)\",
\"mirror\": false,
\"issues\": true,
\"pull_requests\": true,
\"releases\": true
}"
done
Part 8: OAuth2 / SSO
Forgejo supports multiple OAuth2 providers:
# Via app.ini or env vars:
FORGEJO__oauth2__ENABLED: "true"
# Add provider via admin UI:
# Site Administration → Authentication Sources → Add Authentication Source
# → OAuth2 → GitHub / GitLab / Gitea / Keycloak / custom OIDC
# Example: Authentik as OIDC provider
environment:
FORGEJO__oauth2__ENABLED: "true"
In Authentik:
- Applications → Providers → Create → OAuth2/OpenID Provider
- Redirect URI:
https://git.yourdomain.com/user/oauth2/authentik/callback - Copy Client ID + Secret to Forgejo OAuth2 settings
Maintenance
# Update Forgejo:
docker compose pull
docker compose up -d
# Backup:
docker compose stop forgejo
tar -czf forgejo-backup-$(date +%Y%m%d).tar.gz \
$(docker volume inspect forgejo_forgejo_data --format '{{.Mountpoint}}')
docker exec forgejo-postgres-1 pg_dump -U forgejo forgejo \
| gzip > forgejo-db-$(date +%Y%m%d).sql.gz
docker compose start forgejo
# Logs:
docker compose logs -f forgejo
# Admin CLI:
docker exec -u git forgejo forgejo admin user create \
--username alice --email alice@yourdomain.com --password secret --admin
See all open source DevOps and Git hosting tools at OSSAlt.com/categories/devops.