Forgejo: The Community-Driven Self-Hosted Git Forge
Your source code is arguably the most valuable digital asset you own. When it lives on GitHub, you're trusting Microsoft with your intellectual property, your development workflows, and your access to your own work. For most people, that trade-off is fine. But if you want full ownership — or if compliance requirements demand it — self-hosting a Git forge puts you back in control.
Forgejo is a community-driven, self-hosted Git forge that gives you repositories, pull requests, issue tracking, CI/CD, and a package registry — all running on hardware you control, using roughly 100 MB of RAM. It's a fork of Gitea that exists because of a disagreement about how open source projects should be governed.
This guide covers what Forgejo is, why it split from Gitea, how it compares to the alternatives, and how to get it running with Docker Compose. If you've read our Gitea article, this picks up where that left off — with a deeper look at the fork that's charting its own course.
The Gitea-Forgejo Split: What Happened
To understand Forgejo, you need to understand the split.
Gitea started as a community fork of Gogs in 2016, aiming to be a lightweight, self-hosted Git service. It was community-governed and grew steadily. Then in October 2022, a for-profit company (Gitea Ltd.) took control of the project — owning the domain name, the trademark, and effectively the direction of development. Some community members saw this as a betrayal of the project's open source roots.
Forgejo was created in response, also in October 2022, as a "soft fork" — staying close to Gitea's codebase while establishing independent, community-first governance. By early 2024, Forgejo announced it was becoming a "hard fork," meaning it would increasingly diverge from Gitea with its own features and direction.
Forgejo operates under the umbrella of Codeberg e.V., a German non-profit organization. Its governance is community-driven: decisions are made in the interest of the general public, not to maximize profit. The project is developed entirely on Codeberg (itself powered by Forgejo), tested and released using Forgejo Actions, and localized through Weblate — all free/libre software tools.
The balanced take: Both projects are legitimate and well-maintained. Gitea has more commercial backing and a longer track record. Forgejo has stronger community governance guarantees and is pushing features like federation that Gitea isn't pursuing. For most users, either works. If you care about governance principles, Forgejo's non-profit structure is more aligned with traditional open source values. If you want a company standing behind the product, Gitea has that.
Forgejo vs Gitea vs GitLab vs Gogs
Here's how the self-hosted Git forges compare:
| Feature | Forgejo | Gitea | GitLab CE | Gogs |
|---|---|---|---|---|
| Idle RAM | ~100 MB | ~100 MB | 4-6 GB | ~50 MB |
| Setup time | 5 minutes | 5 minutes | 30-60 minutes | 5 minutes |
| Built-in CI/CD | Yes (Actions) | Yes (Actions) | Yes (advanced) | No |
| Package registry | Yes (15+ formats) | Yes (15+ formats) | Yes | No |
| Issue tracking | Good | Good | Advanced | Basic |
| Code review | Good | Good | Excellent | Basic |
| Container registry | Yes | Yes | Yes | No |
| Federation | In progress (ForgeFed) | No plans | No | No |
| License | GPL-3.0 | MIT | MIT (CE) | MIT |
| Governance | Non-profit community | Company-backed | Company-backed | Single maintainer |
| Developed on | Codeberg (Forgejo) | GitHub | GitLab | GitHub |
| Min server | 1 GB RAM VPS | 1 GB RAM VPS | 4 GB RAM VPS | 512 MB RAM VPS |
Forgejo vs Gitea: Functionally very similar today, since Forgejo hasn't fully diverged yet. The main differences are governance (non-profit vs company), licensing (GPL vs MIT), and roadmap (Forgejo is building federation; Gitea is not). Day-to-day usage is nearly identical.
Forgejo vs GitLab CE: GitLab is far more feature-rich — advanced CI/CD pipelines, security scanning, built-in container scanning, robust project management. But it requires 10-40x more resources. If your team needs GitLab-level features, use GitLab. If you want something lightweight that covers 80% of the use cases, Forgejo is the better fit.
Forgejo vs Gogs: Gogs is even lighter than Forgejo but lacks CI/CD, a package registry, and has limited development activity. Forgejo is the better choice for anything beyond basic Git hosting.
Installation with Docker Compose
Forgejo is distributed as a single container image. The quickest path to production is Docker Compose.
Minimal Setup with SQLite
For personal use or small teams (under 10 users), SQLite keeps things simple:
# docker-compose.yml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:14
restart: unless-stopped
ports:
- "3000:3000" # Web UI
- "2222:22" # SSH
volumes:
- forgejo_data:/data
environment:
USER_UID: 1000
USER_GID: 1000
FORGEJO__database__DB_TYPE: sqlite3
FORGEJO__server__ROOT_URL: https://git.example.com/
FORGEJO__server__SSH_DOMAIN: git.example.com
FORGEJO__server__SSH_PORT: 2222
FORGEJO__server__DOMAIN: git.example.com
volumes:
forgejo_data:
Run docker compose up -d, visit http://your-server:3000, and you'll see the setup wizard.
Production Setup with PostgreSQL
For teams or if you want better concurrent performance:
# docker-compose.yml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:14
restart: unless-stopped
ports:
- "3000:3000"
- "2222:22"
volumes:
- forgejo_data:/data
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__ROOT_URL: https://git.example.com/
FORGEJO__server__SSH_DOMAIN: git.example.com
FORGEJO__server__SSH_PORT: 2222
FORGEJO__server__DOMAIN: git.example.com
depends_on:
- db
db:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: forgejo
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
forgejo_data:
db_data:
Create a .env file with your database password:
echo "DB_PASSWORD=$(openssl rand -base64 32)" > .env
Then docker compose up -d.
Rootless Mode
Forgejo also offers a rootless container image for tighter security. Replace the image with codeberg.org/forgejo/forgejo:14-rootless and adjust the volume mount to /var/lib/forgejo instead of /data. The rootless image runs entirely as a non-root user inside the container.
Initial Setup and Configuration
The Setup Wizard
On first visit, Forgejo shows a setup wizard where you configure:
- Database settings (pre-filled if you used environment variables)
- Site title and base URL
- Admin account — create this immediately; don't leave it for later
Configuring app.ini
Forgejo's configuration lives in /data/gitea/conf/app.ini inside the container (yes, it still uses the gitea path for compatibility). You can set most options through environment variables using the FORGEJO__section__KEY format, but for fine-grained control, edit app.ini directly.
Key settings to consider:
[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com/
SSH_DOMAIN = git.example.com
SSH_PORT = 2222
[service]
DISABLE_REGISTRATION = true ; Disable public signup after creating your admin
REQUIRE_SIGNIN_VIEW = false ; Whether anonymous users can browse public repos
DEFAULT_KEEP_EMAIL_PRIVATE = true ; Privacy-friendly default
[repository]
DEFAULT_BRANCH = main
DEFAULT_PRIVATE = last ; Remember last visibility choice
[mailer]
ENABLED = true
PROTOCOL = smtps
SMTP_ADDR = smtp.example.com
SMTP_PORT = 465
USER = [email protected]
PASSWD = your-smtp-password
FROM = "Forgejo" <[email protected]>
[actions]
ENABLED = true ; Enable Forgejo Actions (CI/CD)
After editing, restart the container: docker compose restart forgejo.
Repository Management
Creating Repositories
The web UI is straightforward — click the + button, name your repository, choose visibility (public/private), optionally initialize with a README, license, and .gitignore. Clone over HTTPS or SSH and start pushing.
Organizations
Organizations group repositories and manage team permissions. Create an organization, add teams (Owners, Developers, Read-only), and assign repositories to teams. This mirrors GitHub's organization model closely.
Mirroring from GitHub
One of the most useful features for migration: you can set up a mirror that automatically syncs a GitHub repository to your Forgejo instance. Go to New Migration, select GitHub as the source, enter a personal access token, and enable mirroring. Forgejo will periodically pull changes from GitHub, keeping your local copy up to date.
This is ideal during a transition period — keep using GitHub for collaboration while maintaining an always-current backup on your own infrastructure.
Importing Repositories
Forgejo can import from GitHub, GitLab, Gitea, Gogs, and any plain Git URL. The import tool brings over:
- Code, branches, and tags
- Issues and comments
- Pull requests and reviews
- Labels, milestones, and releases
- Wiki content
For bulk imports via the API:
curl -X POST "https://git.example.com/api/v1/repos/migrate" \
-H "Authorization: token YOUR_FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clone_addr": "https://github.com/owner/repo",
"auth_token": "GITHUB_PAT",
"repo_name": "repo",
"repo_owner": "your-org",
"mirror": false,
"service": "github",
"issues": true,
"pull_requests": true,
"labels": true,
"milestones": true,
"releases": true,
"wiki": true
}'
CI/CD with Forgejo Actions
Forgejo Actions is compatible with GitHub Actions workflow syntax. If you have existing .github/workflows/ files, most of them will work with little or no modification.
Enabling Actions
Actions is enabled by default in Forgejo v14+. If you're on an older version, add this to app.ini:
[actions]
ENABLED = true
Setting Up a Runner
Forgejo Actions requires a runner — a separate process that picks up jobs and executes them. This is analogous to GitHub's self-hosted runners.
First, get a registration token from your Forgejo instance: go to Site Administration > Actions > Runners > Create New Runner.
Then deploy the runner with Docker Compose:
# runner-compose.yml
services:
forgejo-runner:
image: code.forgejo.org/forgejo/runner:6
restart: unless-stopped
volumes:
- runner_data:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
FORGEJO_URL: https://git.example.com
RUNNER_TOKEN: <your-registration-token>
RUNNER_NAME: my-runner
RUNNER_LABELS: ubuntu-latest:docker://node:22-bookworm,ubuntu-22.04:docker://ubuntu:22.04
volumes:
runner_data:
The RUNNER_LABELS map workflow runs-on values to actual Docker images. When a workflow specifies runs-on: ubuntu-latest, the runner spins up the corresponding container.
Security note: The runner needs access to the Docker socket to create containers for jobs. It's recommended to run the runner on a separate machine from your Forgejo instance, especially for public-facing installations.
Example Workflow
Create .forgejo/workflows/ci.yml in your repository (or .github/workflows/ci.yml — both paths work):
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npm run lint
Most GitHub Actions from the marketplace work on Forgejo, but there are exceptions. Actions that call GitHub-specific APIs (like actions/cache relying on GitHub's caching service, or github-script using the GitHub API) won't work without modification. Simple actions that just run commands, install tools, or check out code work fine.
As of Forgejo v14, Actions also supports matrix jobs and dynamic runs-on fields defined by earlier jobs in a workflow — letting you apply custom logic to determine where jobs execute.
Package Registry
Forgejo includes a built-in package registry supporting over 15 package formats. You don't need to run a separate Nexus, Artifactory, or Harbor — it's all integrated.
Supported formats include:
- Container images (OCI/Docker)
- npm packages
- PyPI packages
- Maven / Gradle
- NuGet (.NET)
- Cargo (Rust)
- Go modules
- Composer (PHP)
- Conan (C/C++)
- Conda
- Helm charts
- RubyGems
- Pub (Dart/Flutter)
- Vagrant
- Generic (arbitrary files)
Pushing a Container Image
# Log in to your Forgejo registry
docker login git.example.com
# Tag and push
docker tag myapp:latest git.example.com/myorg/myapp:latest
docker push git.example.com/myorg/myapp:latest
Publishing an npm Package
Configure your .npmrc:
@myorg:registry=https://git.example.com/api/packages/myorg/npm/
//git.example.com/api/packages/myorg/npm/:_authToken=${FORGEJO_TOKEN}
Then npm publish as usual.
Publishing a Python Package
pip install twine
twine upload \
--repository-url https://git.example.com/api/packages/myorg/pypi \
-u your-username -p your-token \
dist/*
The package registry supports deduplication — uploading identical files only stores one blob on disk. You can configure cleanup rules per user or organization to automatically prune old package versions.
Issue Tracking and Project Management
Forgejo's issue tracker covers the essentials:
- Issues with markdown support, file attachments, and threaded comments
- Labels with colors for categorization (bug, enhancement, priority, etc.)
- Milestones for grouping issues into releases or sprints
- Projects (kanban boards) for visual task management
- Assignees — assign issues to one or more team members
- Due dates for deadline tracking
- Templates — define issue and pull request templates in
.forgejo/issue_template/or.github/ISSUE_TEMPLATE/ - Reactions on issues and comments
It's not as feature-rich as GitLab's issue tracker (no epics, no time tracking, no burndown charts), but it covers what most small-to-medium teams need. If you need advanced project management, you're better off pairing Forgejo with a dedicated tool like Plane or Taiga.
User Management
Registration Settings
By default, Forgejo allows open registration. For private instances, disable it after creating your admin account:
[service]
DISABLE_REGISTRATION = true
ALLOW_ONLY_INTERNAL_REGISTRATION = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
You can also use allowlists to restrict registration to specific email domains:
[service]
EMAIL_DOMAIN_ALLOWLIST = example.com,yourcompany.com
External Authentication
Forgejo supports multiple authentication sources:
- OAuth2 — Connect with GitHub, GitLab, Google, Keycloak, Authentik, or any OIDC provider
- LDAP — Integrate with Active Directory or OpenLDAP for enterprise environments
- SMTP — Authenticate against an existing mail server
- PAM — Use system-level authentication
Configure these under Site Administration > Authentication Sources. You can have multiple sources active simultaneously.
Two-Factor Authentication
Users can enable TOTP-based 2FA from their security settings. Admins can require 2FA for all users by enabling it in the admin panel. WebAuthn/FIDO2 hardware keys (YubiKey, etc.) are also supported for passwordless authentication.
Webhooks and Integrations
Webhooks
Forgejo can send webhook notifications for repository events (push, pull request, issue, release, etc.) to any HTTP endpoint. Built-in webhook formats include:
- Forgejo (native format)
- Gitea (compatible)
- Slack
- Discord
- Microsoft Teams
- Telegram
- Matrix
- Feishu / DingTalk
- Generic (custom JSON to any URL)
Setting up a Discord notification for new pull requests takes about 30 seconds: go to the repository settings, add a Discord webhook, paste your webhook URL, select the events you care about, and save.
API
Forgejo exposes a comprehensive REST API (Swagger-documented) that covers everything the web UI can do: create repositories, manage issues, trigger actions, manage users and organizations, and more. API tokens can be created per-user with scoped permissions.
# List your repositories
curl -H "Authorization: token YOUR_TOKEN" \
https://git.example.com/api/v1/user/repos
# Create an issue
curl -X POST "https://git.example.com/api/v1/repos/owner/repo/issues" \
-H "Authorization: token YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Bug report", "body": "Description here", "labels": [1, 2]}'
Matrix and Chat Notifications
For self-hosters running Matrix (or using Discord/Slack), webhooks are the simplest integration. For more complex workflows — like posting CI/CD results to a channel or creating issues from chat commands — pair Forgejo's webhooks with a workflow tool like n8n or a simple bot.
Migration from GitHub or GitLab
Forgejo's built-in migration tool makes switching straightforward.
From GitHub
- Go to New Migration in the Forgejo web UI
- Select GitHub as the source
- Enter a GitHub personal access token (with
reposcope for private repositories) - Choose the repository to import
- Select what to bring over: issues, pull requests, labels, milestones, releases, wiki
- Click Migrate Repository
Forgejo preserves issue numbers, PR references, and cross-links as much as possible. Some GitHub-specific features (Discussions, Actions workflows that rely on GitHub APIs, GitHub Apps integrations) won't transfer, but the core data comes over cleanly.
From GitLab
The same migration UI supports GitLab. You'll need a GitLab personal access token with api scope. It imports repositories, issues, PRs (merge requests), labels, milestones, and releases.
From Gitea
Migration from Gitea is the smoothest, since the database schema is nearly identical. You can either use the migration tool or, for a full instance migration, copy the data directory directly and update the configuration.
Bulk Migration Script
For migrating many repositories at once:
#!/bin/bash
# Migrate all repos from a GitHub org to Forgejo
GITHUB_ORG="your-github-org"
FORGEJO_ORG="your-forgejo-org"
FORGEJO_URL="https://git.example.com"
GITHUB_TOKEN="ghp_..."
FORGEJO_TOKEN="your-forgejo-token"
# Get list of repos from GitHub
repos=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/orgs/$GITHUB_ORG/repos?per_page=100" \
| jq -r '.[].name')
for repo in $repos; do
echo "Migrating $repo..."
curl -s -X POST "$FORGEJO_URL/api/v1/repos/migrate" \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"clone_addr\": \"https://github.com/$GITHUB_ORG/$repo\",
\"auth_token\": \"$GITHUB_TOKEN\",
\"repo_name\": \"$repo\",
\"repo_owner\": \"$FORGEJO_ORG\",
\"mirror\": false,
\"service\": \"github\",
\"issues\": true,
\"pull_requests\": true,
\"labels\": true,
\"milestones\": true,
\"releases\": true
}"
echo " done."
sleep 2 # Be polite to the APIs
done
Resource Usage and Performance
This is where Forgejo (and Gitea) dominate. The resource footprint is a fraction of GitLab's:
| Metric | Forgejo | GitLab CE |
|---|---|---|
| Idle RAM | 80-150 MB | 3-6 GB |
| Disk (empty install) | ~100 MB | ~2.5 GB |
| Cold start | 2-3 seconds | 3-5 minutes |
| Minimum VPS | $5/month (1 GB RAM) | $20+/month (4 GB RAM) |
| Raspberry Pi viable | Yes | No |
| Database | SQLite or PostgreSQL | PostgreSQL (required) |
Forgejo runs comfortably on a Raspberry Pi 4 or a $5/month VPS. A $10/month VPS with 2 GB of RAM handles small teams of 20-30 users with room to spare. Contrast that with GitLab, where 4 GB is the minimum and 8 GB is recommended.
For a sense of scale: Codeberg, the largest public Forgejo instance, serves over 120,000 users and 350,000 repositories. It's proof that Forgejo scales well beyond personal use.
Backup
Forgejo includes a built-in dump command:
docker exec forgejo forgejo dump -c /data/gitea/conf/app.ini
This produces a zip file containing the database, repositories, configuration, and attachments. For automated backups, run this via cron and ship the archives off-site.
For more granular backups, the Git repositories live in /data/git/repositories/ and can be incrementally synced with rsync or restic.
Federation: Forgejo's Unique Bet
The most distinctive item on Forgejo's roadmap is forge federation using the ForgeFed protocol, an extension of ActivityPub (the same protocol that powers Mastodon and the fediverse).
The vision: different Forgejo instances could interact with each other without users needing accounts on every server. You could star a repository on another instance, open issues across instances, and eventually contribute pull requests — all federated.
As of early 2026, federation is still in active development. Federated repository starring works between Forgejo instances, and work on federated search using WebFinger is underway. Full cross-instance collaboration (federated pull requests) is further out. The NLnet Foundation has funded this work, so it has real resources behind it.
This is speculative and long-term, but it's the clearest differentiator between Forgejo and everything else. No other Git forge is seriously pursuing federation.
The Honest Take
Use Forgejo when:
- You need data sovereignty. Compliance requirements (HIPAA, SOC 2, government contracts) may mandate that source code stays on your infrastructure.
- You care about community governance. Forgejo's non-profit structure means the project can't be acquired or pivoted to maximize shareholder value.
- Cost at scale matters. GitHub Enterprise costs $21/user/month. Forgejo is free for unlimited users on a $5 VPS.
- You want a lightweight forge. If GitLab's 4+ GB RAM requirement is overkill for your needs, Forgejo gives you 80% of the features at 5% of the resource cost.
- You're building a private development environment. Behind a VPN or firewall, for internal tooling, without cloud dependencies.
Stick with GitHub when:
- You're an individual or small team. GitHub Free gives you unlimited private repos, Actions minutes, and access to the largest developer ecosystem on the planet.
- Discoverability matters. Open source projects get more contributors on GitHub than anywhere else. If you're building in public, GitHub is where developers look.
- You rely on the ecosystem. GitHub Copilot, GitHub Apps, Dependabot, CodeQL security scanning, Codespaces — there's nothing comparable in the self-hosted world.
- You don't want to maintain infrastructure. Self-hosting means you're responsible for updates, backups, uptime, and security. GitHub's team handles all of that.
The ecosystem gap is real
Forgejo has solid fundamentals — it's fast, lightweight, and covers the core Git forge features well. But the ecosystem around it is thin compared to GitHub. There are fewer integrations, fewer CI/CD marketplace actions, fewer tools that support Forgejo natively. You'll spend more time configuring things that "just work" on GitHub.
That gap is closing — Forgejo Actions' compatibility with GitHub Actions syntax helps enormously — but it's worth knowing going in.
Forgejo or Gitea?
If you're choosing between Forgejo and Gitea specifically, here's the pragmatic answer: both work. The day-to-day experience is nearly identical. Choose Forgejo if you value community governance and are excited about federation. Choose Gitea if you prefer MIT licensing or want the backing of a company. Either way, migration between them is trivial since they share a common heritage.