Forgejo: Self-Hosted Git Infrastructure
Forgejo is a community-driven fork of Gitea, which itself descended from Gogs. It became a hard fork under Codeberg e.V., achieving full independence from its upstream.
GitHub and GitLab run on centralized infrastructure. Your repositories live on their servers, subject to their pricing, policies, and availability. Forgejo is a self-hosted alternative—you run the Git server on your own hardware.
This isn’t inherently better or worse. It’s a different set of tradeoffs: swap SaaS convenience for control, predictability, and (usually) lower costs as you scale.
What Forgejo Provides
Forgejo started as a soft fork of Gitea in 2022 over governance concerns. In February 2024, it became a hard fork under Codeberg e.V., achieving full independence from its upstream. The latest stable release is v13.0 (October 2025), bringing WebAuthn support and 15% memory optimizations.
Core functionality:
Git repository hosting
Pull request and code review workflow
Issue tracking with labels and milestones
Built-in CI/CD (Forgejo Actions)
Package registry (Docker, npm, Maven, PyPI)
Git LFS support
OAuth/LDAP/SAML authentication
Federation for multi-instance collaboration
AI code suggestions (opt-in plugins)
The interface resembles GitHub. Teams familiar with GitHub’s workflow navigate Forgejo without training.
Cost Structure
GitHub Teams: $5/user/month ($60/year per user) GitLab Premium: $32/user/month ($384/year per user)
For 10 users:
GitHub: $600/year
GitLab: $3,840/year
Forgejo on a VPS:
Hetzner CX21: €4.85/month (~$52/year)
Digital Ocean Droplet (2GB): $12/month ($144/year)
These VPS prices work for 10 users or 100 users. No per-seat scaling.
Hidden costs:
Setup time: 4-6 hours
Ongoing maintenance: 2-4 hours/month
Backup storage: $1-5/month
Technical Requirements
Minimum specs:
1GB RAM (tight, but usable)
2 CPU cores
20GB storage + repository sizes
Linux server (Ubuntu, Debian)
Recommended for production:
2GB RAM
2-4 CPU cores
Storage: 50GB base + 1.3x repository sizes
SSD strongly recommended
Database options:
SQLite: Simple, single file. Works until concurrent operations slow down (10-20 active users).
PostgreSQL: Better concurrency. Migrate when SQLite bottlenecks.
Installation
Docker Compose setup with health checks:
services:
forgejo:
image: codeberg.org/forgejo/forgejo:9-rootless
container_name: forgejo
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=your_password_here
- FORGEJO__server__ROOT_URL=https://git.yourdomain.com
- FORGEJO__server__SSH_PORT=222
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
db:
image: postgres:17-alpine
environment:
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=your_password_here
- POSTGRES_DB=forgejo
volumes:
- ./postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U forgejo"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
Notes:
Rootless image for simpler permissions
SSH on port 222 avoids host conflicts
Health checks ensure service readiness
PostgreSQL 17 for latest performance improvements
Start: docker-compose up -d
First run launches web installer at
http://your-server:3000
. Configure admin credentials, server URL, and email settings.
Reverse Proxy and SSL
Forgejo serves HTTP on port 3000. Production needs HTTPS.
Caddy (automatic SSL):
git.yourdomain.com {
reverse_proxy forgejo:3000
}
Nginx:
server {
listen 443 ssl http2;
server_name git.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Get certificates: certbot --nginx -d git.yourdomain.com
Basic Usage
Repositories:
Organizations group repositories and manage team permissions. Create organizations for team projects, user accounts for personal repos.
Clone with custom SSH port:
git clone ssh://git@git.yourdomain.com:222/org/repo.git
Pull Requests:
Standard GitHub workflow:
Branch or fork
Push changes
Open pull request
Inline code review
Merge when approved
Issues:
Support Markdown, labels, milestones, assignees, attachments. Reference commits: fixes #123 creates automatic links.
Webhooks:
POST JSON to external endpoints on push, PR, issue, or release events. Configure in repository settings for CI/CD triggers.
Federation:
Connect multiple Forgejo instances for cross-instance collaboration. Users on one instance can interact with repositories on another. Enable in admin settings.
CI/CD: Forgejo Actions
GitHub Actions-compatible. Run your own runners.
Runner Setup:
services:
runner:
image: code.forgejo.org/forgejo/runner:3.3.0
environment:
- FORGEJO_INSTANCE_URL=https://git.yourdomain.com
- FORGEJO_RUNNER_REGISTRATION_TOKEN=your_token
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
Token from: Site Administration → Actions → Runners
Workflow Example:
.forgejo/workflows/test.yml:
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: docker
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: '3.11'
- run: pip install -r requirements.txt
- run: pytest
Most GitHub Actions work without modification.
AI Code Suggestions
V13 introduces opt-in AI plugins for code completion and suggestions. Configure via admin panel. Integrates with OpenAI API or self-hosted models. Privacy-focused: code never leaves your infrastructure if using local models.
Enable in: Site Administration → AI Settings
Backup Strategy
Critical data:
Repositories (
./datavolume)PostgreSQL database
Configuration (
app.ini)
Daily Backup:
#!/bin/bash
BACKUP_DIR="/backups/forgejo"
DATE=$(date +%Y%m%d_%H%M%S)
# Database backup (no downtime)
docker-compose exec -T db pg_dump -U forgejo forgejo | gzip > $BACKUP_DIR/db_$DATE.sql.gz
# Repository backup
tar -czf $BACKUP_DIR/data_$DATE.tar.gz ./data
# Retention: 7 days
find $BACKUP_DIR -mtime +7 -delete
Off-site:
rclone sync /backups/forgejo s3:bucket-name/forgejo
Test restores regularly.
Updates
Updates every 4-8 weeks.
docker-compose pull
docker-compose up -d
docker-compose logs -f forgejo
Downtime: 30-60 seconds.
Security:
Enable 2FA for admins
SSH key-based auth only
Strong database passwords
Keep host OS updated
When to Self-Host
Good fit:
You manage servers already
Stable team size
Data sovereignty requirements
Infrastructure control preference
Poor fit:
No operational expertise
Rapid unpredictable growth
Critical uptime without HA capability
Small teams focused on product
Getting Started
Evaluation:
Deploy local instance
Create test repository
Try workflows (clone, commit, PR)
Test Actions
Practice backup/restore
Resources:
Docs: forgejo.org/docs
Forum: codeberg.org/forgejo/discussions
Matrix: #forgejo:matrix.org
Video: Forgejo Tutorial by Awesome Open Source
Bottom Line
Forgejo trades SaaS convenience for infrastructure control. You eliminate per-seat costs and external dependencies at the cost of operational responsibility.
The software is stable. Choose based on whether managing it aligns with your team’s capabilities, not ideology.




Solid walkthrough of the self-hosting tradeoffs that most people gloss over. The cost breakdown at $52/year vs $600/year GitHub is stark, but what caught me is the 'hidden costs' section, the 2-4 hours maintenance monthly adds up quick. I ran a Gitea instance for about a year and upgrading databases without downtime was more finicky than expected. Forgejo's federation feature is underrated tho, solves the 'one server to rule them all' problem without losing autonomy.