Forgejo: A Deep Dive into the Community-Driven Gitea Fork
Table of Contents
- Key Features at a Glance
- The Forgejo Philosophy: Who Is It For?
- GitHub vs. Forgejo: A Quick Comparison
- Installation Guide
- Configuration Walkthrough
- Essential Forgejo Operations
- Setting Up CI/CD with Forgejo Actions
- Package Registry Usage
- Advanced Features and Integrations
- Migration Scenarios
- Performance Optimization
- Security Best Practices
- Pros and Cons
- Real-World Use Cases
- Getting Started & Further Reading
- Frequently Asked Questions (FAQ)
- Conclusion
In the world of open-source software, the story of how a project is governed is often as important as the code itself. Forgejo is a powerful testament to this fact. Originally born as a “soft fork” of Gitea in late 2022, Forgejo officially became a hard fork in early 2024 to ensure its codebase and architectural future remained permanently aligned with the community’s needs, completely free from commercial incentives.
Created by a community of users and contributors, Forgejo is managed under the stewardship of the Codeberg e.V. non-profit. While it remains highly compatible with its predecessor, it has independently evolved into a powerhouse platform—spearheading decentralized collaboration, privacy-forward features, and robust self-hosted environments.
Key Features at a Glance

Forgejo inherits the fantastic feature set of Gitea while adding a unique philosophical guarantee and a forward-looking vision.
| Feature | Description | Key Benefit |
|---|---|---|
| Rich, Independent Feature Set | Originally sharing Gitea’s roots, Forgejo now maintains its own rapid release cycle (major semantic versions quarterly) featuring CI/CD, and robust package registries. | You get a mature, feature-rich Git service without compromising on performance, now tailored purely to community requests. |
| True Community Governance | The project is governed by its community under the umbrella of Codeberg e.V., a German non-profit. Decisions are made in the open. | Provides a long-term guarantee that Forgejo will never be controlled by a single for-profit entity, ensuring it remains FOSS forever. |
| Built-in Federation | Unlike centralized platforms, Forgejo now actively ships with federation support natively using the ActivityPub (ForgeFed) protocol. | Breaks down the silos between code hosting platforms. A developer on one Forgejo instance can star, issue, or collaborate on a repository hosted on another. |
| Hard Fork Evolution | Since early 2024, Forgejo is a hard fork. It actively builds its own architecture while offering documented, supported migration paths from Gitea. | Users benefit from a FOSS-first roadmap, specialized security handling, and rapid innovation unencumbered by corporate interests. |
The Forgejo Philosophy: Who Is It For?
The philosophy of Forgejo is centered on software freedom, digital sovereignty, and community control. It’s for users and organizations who believe that the infrastructure for creating open-source software should itself be governed by open-source principles.
This makes it the ideal choice for:
Digital Sovereignty Seekers: Governments, municipalities, and enterprise users moving away from massive cloud platforms (like GitHub) due to concerns over mandatory AI-model training and jurisdictional data control.
FOSS Advocates and Purists: Individuals and groups who prioritize governance and want to ensure their tools align with their free software ideals.
Public Institutions and Non-Profits: Organizations that need a stable, long-term solution without the risk of a commercial takeover. The flagship instance, Codeberg, and public sector adoption across Europe are prime examples.
Anyone interested in a decentralized future: Its active implementation of ActivityPub federation makes it the pioneering platform for the next generation of interconnected development tools.
GitHub vs. Forgejo: A Quick Comparison
The comparison highlights the extreme differences in their governance and ultimate goals.
| Aspect | GitHub | Forgejo |
|---|---|---|
| Primary Focus | A global, centralized, AI-integrated developer platform. | A community-governed, self-hosted, and federated software forge. |
| Hosting Options | Cloud (SaaS) and Self-hosted (Enterprise). | Self-hosted only. (Codeberg provides a large public instance). |
| Development Model | Corporate (owned by Microsoft). | Non-profit, community-driven (Codeberg e.V.). |
| Future Vision | A single, unified platform for all developers. | A decentralized network of independent, interconnected forges. |
Installation Guide
Setting up Forgejo is straightforward. As of 2026, Forgejo utilizes a predictable semantic versioning system, currently on the LTS v15.0 track. Here’s a comprehensive installation guide covering the most common scenarios.
Docker Installation (Recommended for Beginners)
The easiest way to get started with Forgejo is using Docker:
# Create a directory for Forgejo data
mkdir forgejo-data
cd forgejo-data
# Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: "3"
networks:
forgejo:
external: false
services:
server:
image: codeberg.org/forgejo/forgejo:15.0.3
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=forgejo
restart: always
networks:
- forgejo
volumes:
- ./forgejo:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
depends_on:
- db
db:
image: postgres:15
restart: always
environment:
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=forgejo
- POSTGRES_DB=forgejo
networks:
- forgejo
volumes:
- ./postgres:/var/lib/postgresql/data
EOF
# Start the services
docker-compose up -d
Binary Installation on Linux
For production deployments, installing from binary provides better control (Note: Native Windows OS support was discontinued in early 2024):
# Download the latest 15.0 LTS release
wget -O forgejo https://codeberg.org/forgejo/forgejo/releases/download/v15.0.3/forgejo-15.0.3-linux-amd64
# Make it executable and move to system PATH
chmod +x forgejo
sudo mv forgejo /usr/local/bin/
# Create forgejo user
sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' --group --disabled-password --home /home/git git
# Create necessary directories
sudo mkdir -p /var/lib/forgejo/{custom,data,log}
sudo chown -R git:git /var/lib/forgejo/
sudo chmod -R 750 /var/lib/forgejo/
# Create systemd service
sudo cat > /etc/systemd/system/forgejo.service << 'EOF'
[Unit]
Description=Forgejo (Git service)
After=syslog.target
After=network.target
[Service]
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/forgejo/
ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/forgejo
[Install]
WantedBy=multi-user.target
EOF
# Enable and start the service
sudo systemctl enable forgejo
sudo systemctl start forgejo
Configuration Walkthrough
Forgejo’s configuration is managed through the app.ini file. Here’s a comprehensive configuration example:
# Basic application settings
APP_NAME = My Forgejo Instance
RUN_MODE = prod
RUN_USER = git
WORK_PATH = /var/lib/forgejo
[repository]
ROOT = /var/lib/forgejo/repositories
SCRIPT_TYPE = bash
DEFAULT_BRANCH = main
DEFAULT_PRIVATE = private
[server]
PROTOCOL = https
DOMAIN = git.example.com
HTTP_PORT = 3000
ROOT_URL = https://git.example.com/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
OFFLINE_MODE = false
[federation]
ENABLED = true
SHARE_USER_STATISTICS = false
[database]
DB_TYPE = postgres
HOST = localhost:5432
NAME = forgejo
USER = forgejo
PASSWD = your_secure_password
SSL_MODE = require
[session]
PROVIDER = file
PROVIDER_CONFIG = data/sessions
COOKIE_SECURE = true
COOKIE_NAME = i_like_forgejo
[security]
INSTALL_LOCK = true
SECRET_KEY = your_secret_key_here
INTERNAL_TOKEN = your_internal_token_here
PASSWORD_COMPLEXITY = lower,upper,digit,spec
MIN_PASSWORD_LENGTH = 8
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
ENABLE_NOTIFY_MAIL = true
[mailer]
ENABLED = true
FROM = forgejo@example.com
MAILER_TYPE = smtp
HOST = mail.example.com:587
USER = forgejo@example.com
PASSWD = your_mail_password
SKIP_VERIFY = false
[log]
MODE = file
LEVEL = Info
ROOT_PATH = /var/lib/forgejo/log
Essential Forgejo Operations
Creating Your First Repository
Once Forgejo is running, creating a repository is straightforward:
# Via web interface: Navigate to + New Repository
# Via command line (on server):
sudo -u git forgejo admin create-repo --name my-project --owner username
# Clone and add content
git clone https://git.example.com/username/my-project.git
cd my-project
echo "# My Forgejo Project" > README.md
git add README.md
git commit -m "Initial commit"
git push origin main
User Management
Forgejo provides comprehensive user management capabilities:
# Create a new user
sudo -u git forgejo admin create-user --name alice --email alice@example.com --password securepass123
# Make user admin
sudo -u git forgejo admin create-user --name admin --email admin@example.com --password adminpass123 --admin
# List all users
sudo -u git forgejo admin list-users
# Change user password
sudo -u git forgejo admin change-password --username alice --password newpassword123
Repository Management
# Rename repository
sudo -u git forgejo admin rename-repo --from old-name --to new-name --owner username
# Transfer repository ownership
sudo -u git forgejo admin transfer-repo --from old-owner --to new-owner --repo-name project
# Generate repository stats
sudo -u git forgejo admin regenerate hooks
Setting Up CI/CD with Forgejo Actions
Forgejo Actions provides GitHub Actions-compatible CI/CD capabilities. Here’s how to set it up and use it:
Enabling Actions
First, enable Actions in your app.ini:
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = https://code.forgejo.org
Setting Up a Runner
# Download the latest Forgejo runner
wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v3.4.1/forgejo-runner-3.4.1-linux-amd64
chmod +x forgejo-runner
sudo mv forgejo-runner /usr/local/bin/
# Register the runner
forgejo-runner create-runner-file --instance https://git.example.com --secret your-runner-secret --name my-runner
# Generate systemd service
forgejo-runner generate-config > config.yaml
# Create systemd service file
sudo cat > /etc/systemd/system/forgejo-runner.service << 'EOF'
[Unit]
Description=Forgejo Runner
After=syslog.target
After=network.target
[Service]
Type=simple
User=runner
Group=runner
WorkingDirectory=/var/lib/forgejo-runner
ExecStart=/usr/local/bin/forgejo-runner daemon --config config.yaml
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable forgejo-runner
sudo systemctl start forgejo-runner
Example Workflow
Create .forgejo/workflows/ci.yml in your repository:
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linting
run: npm run lint
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build application
run: |
npm ci
npm run build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: build-files
path: dist/
docker-build:
needs: [test, build]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t myapp:${{ github.sha }} .
docker tag myapp:${{ github.sha }} myapp:latest
Package Registry Usage
Forgejo includes built-in package registries for various package managers. Here’s how to use them:
NPM Registry
Configure your project to use Forgejo’s NPM registry:
# Configure npm to use Forgejo registry
npm config set @myscope:registry https://git.example.com/api/packages/myuser/npm/
# For authentication, create .npmrc in your project
echo "//git.example.com/api/packages/myuser/npm/:_authToken=${FORGEJO_TOKEN}" >> .npmrc
Example package.json configuration:
{
"name": "@myscope/mypackage",
"version": "1.0.0",
"publishConfig": {
"registry": "https://git.example.com/api/packages/myuser/npm/"
}
}
Publish your package:
npm publish
Docker Registry
Push Docker images to Forgejo’s container registry:
# Login to registry
echo $FORGEJO_TOKEN | docker login git.example.com -u myuser --password-stdin
# Build and tag image
docker build -t git.example.com/myuser/myproject:latest .
# Push image
docker push git.example.com/myuser/myproject:latest
Generic Package Registry
Upload generic files:
# Upload a file
curl -X PUT \
-H "Authorization: token $FORGEJO_TOKEN" \
-T myfile.zip \
"https://git.example.com/api/packages/myuser/generic/mypackage/1.0.0/myfile.zip"
# Download a file
curl -H "Authorization: token $FORGEJO_TOKEN" \
"https://git.example.com/api/packages/myuser/generic/mypackage/1.0.0/myfile.zip"
Advanced Features and Integrations
Webhooks Configuration
Set up webhooks to integrate with external services:
# Example webhook payload for Discord
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: token $FORGEJO_TOKEN" \
-d '{
"type": "discord",
"config": {
"url": "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL",
"content_type": "json"
},
"events": [
"push",
"issues",
"pull_request"
],
"active": true
}' \
"https://git.example.com/api/v1/repos/username/repo/hooks"
API Usage Examples
Forgejo provides a comprehensive REST API:
#!/usr/bin/env python3
import requests
# Configuration
FORGEJO_URL = "https://git.example.com"
TOKEN = "your_access_token"
HEADERS = {
"Authorization": f"token {TOKEN}",
"Content-Type": "application/json"
}
# Create a repository
def create_repo(name, description="", private=False):
data = {
"name": name,
"description": description,
"private": private,
"auto_init": True
}
response = requests.post(
f"{FORGEJO_URL}/api/v1/user/repos",
json=data,
headers=HEADERS
)
return response.json()
# List repositories
def list_repos(username):
response = requests.get(
f"{FORGEJO_URL}/api/v1/users/{username}/repos",
headers=HEADERS
)
return response.json()
# Create an issue
def create_issue(owner, repo, title, body=""):
data = {
"title": title,
"body": body
}
response = requests.post(
f"{FORGEJO_URL}/api/v1/repos/{owner}/{repo}/issues",
json=data,
headers=HEADERS
)
return response.json()
# Example usage
if __name__ == "__main__":
# Create a new repository
new_repo = create_repo("test-api-repo", "Created via API", False)
print(f"Created repository: {new_repo['html_url']}")
# Create an issue in the repository
issue = create_issue("myuser", "test-api-repo", "Test Issue", "This is a test issue created via API")
print(f"Created issue: {issue['html_url']}")
LDAP Integration
Configure LDAP authentication in app.ini:
[auth]
LOGIN_NAMES = uid,mail
SYNC_EXTERNAL_USERS = true
[[auth.ldap]]
NAME = Corporate LDAP
HOST = ldap.company.com
PORT = 389
SECURITY_PROTOCOL = StartTLS
SKIP_TLS_VERIFY = false
BIND_DN = cn=forgejo,ou=services,dc=company,dc=com
BIND_PASSWORD = secure_bind_password
USER_BASE = ou=users,dc=company,dc=com
USER_FILTER = (&(objectClass=inetOrgPerson)(uid=%s))
ADMIN_FILTER = (memberOf=cn=git-admins,ou=groups,dc=company,dc=com)
USERNAME_ATTRIBUTE = uid
FIRSTNAME_ATTRIBUTE = givenName
SURNAME_ATTRIBUTE = sn
EMAIL_ATTRIBUTE = mail
ENABLED = true
Migration Scenarios
Migrating from GitHub
Use Forgejo’s built-in migration tool:
# Via API
curl -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clone_addr": "https://github.com/user/repo.git",
"repo_name": "migrated-repo",
"auth_username": "github_username",
"auth_password": "github_token",
"mirror": false,
"private": false,
"description": "Migrated from GitHub"
}' \
"https://git.example.com/api/v1/repos/migrate"
Migrating from GitLab
#!/usr/bin/env python3
import requests
import time
def migrate_gitlab_projects(gitlab_url, gitlab_token, forgejo_url, forgejo_token):
# Get GitLab projects
gitlab_headers = {"Authorization": f"Bearer {gitlab_token}"}
projects_response = requests.get(
f"{gitlab_url}/api/v4/projects",
headers=gitlab_headers
)
projects = projects_response.json()
# Migrate each project
for project in projects:
migration_data = {
"clone_addr": project["http_url_to_repo"],
"repo_name": project["name"],
"auth_username": "oauth2",
"auth_password": gitlab_token,
"mirror": False,
"private": project["visibility"] == "private",
"description": project.get("description", "")
}
forgejo_headers = {
"Authorization": f"token {forgejo_token}",
"Content-Type": "application/json"
}
response = requests.post(
f"{forgejo_url}/api/v1/repos/migrate",
json=migration_data,
headers=forgejo_headers
)
if response.status_code == 201:
print(f"Successfully migrated: {project['name']}")
else:
print(f"Failed to migrate {project['name']}: {response.text}")
# Rate limiting
time.sleep(1)
# Usage
migrate_gitlab_projects(
"https://gitlab.com",
"your_gitlab_token",
"https://git.example.com",
"your_forgejo_token"
)
Bulk Repository Migration Script
#!/bin/bash
FORGEJO_URL="https://git.example.com"
FORGEJO_TOKEN="your_token"
SOURCE_URL_BASE="https://github.com/organization"
# List of repositories to migrate
REPOS=(
"repo1"
"repo2"
"repo3"
)
for repo in "${REPOS[@]}"; do
echo "Migrating $repo..."
curl -X POST \
-H "Authorization: token $FORGEJO_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"clone_addr\": \"$SOURCE_URL_BASE/$repo.git\",
\"repo_name\": \"$repo\",
\"mirror\": false,
\"private\": false
}" \
"$FORGEJO_URL/api/v1/repos/migrate"
echo "Migration initiated for $repo"
sleep 2
done
Performance Optimization
Database Optimization
For PostgreSQL backends, optimize your database configuration:
-- PostgreSQL optimization queries
-- Update statistics
ANALYZE;
-- Reindex if necessary
REINDEX DATABASE forgejo;
-- Optimize specific tables
VACUUM ANALYZE repository;
VACUUM ANALYZE user;
VACUUM ANALYZE action;
Caching Configuration
Enable and configure caching in app.ini:
[cache]
ADAPTER = redis
INTERVAL = 60
HOST = redis://localhost:6379/0
ITEM_TTL = 16h
[session]
PROVIDER = redis
PROVIDER_CONFIG = redis://localhost:6379/1
[queue.issue_indexer]
TYPE = redis
CONN_STR = redis://localhost:6379/2
Nginx Reverse Proxy Configuration
Optimize Forgejo with a reverse proxy:
upstream forgejo {
server 127.0.0.1:3000;
}
server {
listen 443 ssl http2;
server_name git.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
client_max_body_size 512M;
location / {
proxy_pass http://forgejo;
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;
proxy_buffering off;
proxy_request_buffering off;
}
location ~* ^/api/v1/repos/.*/git/ {
proxy_pass http://forgejo;
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;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 300;
proxy_send_timeout 300;
}
}
Security Best Practices
Secure Configuration Examples
# Security-focused app.ini settings
[security]
INSTALL_LOCK = true
SECRET_KEY = your-64-character-secret-key
INTERNAL_TOKEN = your-internal-token
DISABLE_GIT_HOOKS = false
IMPORT_LOCAL_PATHS = false
PASSWORD_COMPLEXITY = lower,upper,digit,spec
MIN_PASSWORD_LENGTH = 12
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = true
DEFAULT_KEEP_EMAIL_PRIVATE = true
SHOW_REGISTRATION_BUTTON = false
ENABLE_CAPTCHA = true
[server]
SSH_DISABLE_MAC_ALGORITHMS = hmac-sha1,hmac-sha1-96
SSH_DISABLE_CIPHER_ALGORITHMS = aes128-cbc,aes192-cbc,aes256-cbc,3des-cbc
MINIMUM_KEY_SIZE_CHECK = true
SSL/TLS Configuration
# Generate SSL certificate (Let's Encrypt example)
certbot certonly --webroot -w /var/www/html -d git.example.com
# Configure automatic renewal
echo "0 12 * * * /usr/bin/certbot renew --quiet" | crontab -
Firewall Configuration
# UFW firewall rules
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP (for Let's Encrypt)
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 222/tcp # Forgejo SSH (if different from system SSH)
sudo ufw enable
Backup Strategy
#!/bin/bash
# Comprehensive backup script
BACKUP_DIR="/backup/forgejo"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="forgejo_backup_$DATE"
# Create backup directory
mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
# Backup database
pg_dump -h localhost -U forgejo -d forgejo > "$BACKUP_DIR/$BACKUP_NAME/database.sql"
# Backup repositories
tar -czf "$BACKUP_DIR/$BACKUP_NAME/repositories.tar.gz" -C /var/lib/forgejo repositories/
# Backup configuration
cp -r /etc/forgejo "$BACKUP_DIR/$BACKUP_NAME/config"
# Backup custom assets
tar -czf "$BACKUP_DIR/$BACKUP_NAME/custom.tar.gz" -C /var/lib/forgejo custom/
# Create archive
cd "$BACKUP_DIR"
tar -czf "$BACKUP_NAME.tar.gz" "$BACKUP_NAME"
rm -rf "$BACKUP_NAME"
# Keep only last 7 backups
find "$BACKUP_DIR" -name "forgejo_backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: $BACKUP_DIR/$BACKUP_NAME.tar.gz"
Pros and Cons

Why You Might Choose Forgejo
Guaranteed FOSS Governance: This is the number one reason. Your software development platform is guaranteed to remain free and community-controlled, immune to sudden corporate licensing changes.
Federation is Real: ActivityPub support is officially shipping. By choosing Forgejo, you are actively participating in a modernized, open, and decentralized development ecosystem.
Digital Sovereignty: No mandatory AI scraping of your code. Your private repositories remain strictly private, making it ideal for enterprise and government deployments.
Comprehensive CI/CD: Built-in Actions support provides GitHub-compatible workflows without relying on external dependencies.
Fast Release Cycles: Transitioning to quarterly major semantic releases ensures rapid feature delivery and prompt security patches.
Potential Drawbacks
Hard Fork Divergence: Because it is now a hard fork of Gitea, they no longer mirror each other line-by-line. While compatibility remains high today, users must choose a lane as architectural decisions drift over time.
Younger Brand: While the code is mature and traces back nearly a decade, the “Forgejo” brand is newer.
Entirely Self-Hosted: There is no official SaaS offering provided by the Forgejo project (though Codeberg acts as a public community instance). You are responsible for administration.
Dropped Windows Support: As of 2024, native Windows installations are no longer officially supported; Linux, macOS, or Docker are strictly recommended.
Real-World Use Cases
Academic Institution Deployment
The University of Open Source deployed Forgejo to support their computer science department:
# Their docker-compose.yml for multi-tenant setup
version: "3.8"
services:
forgejo:
image: codeberg.org/forgejo/forgejo:latest
environment:
- FORGEJO__DEFAULT__APP_NAME=University Git Service
- FORGEJO__server__DOMAIN=git.university.edu
- FORGEJO__service__DISABLE_REGISTRATION=true
- FORGEJO__service__DEFAULT_ORG_VISIBILITY=private
- FORGEJO__database__DB_TYPE=postgres
volumes:
- forgejo_data:/data
ports:
- "3000:3000"
- "2222:22"
depends_on:
- db
- ldap
ldap:
image: osixia/openldap:latest
environment:
LDAP_ORGANISATION: "University"
LDAP_DOMAIN: "university.edu"
volumes:
- ldap_data:/var/lib/ldap
- ldap_config:/etc/ldap/slapd.d
db:
image: postgres:15
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: university_secure_password
POSTGRES_DB: forgejo
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
forgejo_data:
postgres_data:
ldap_data:
ldap_config:
Results: The university successfully migrated 200+ student repositories from GitHub, implemented automated grading workflows, and reduced infrastructure costs by 60%.
Corporate Internal Development
TechCorp Inc. replaced their GitHub Enterprise with Forgejo:
#!/bin/bash
# Their automated setup script for development teams
# Create team-specific organizations
teams=("frontend" "backend" "devops" "mobile")
for team in "${teams[@]}"; do
curl -X POST \
-H "Authorization: token $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"username\": \"team-$team\",
\"full_name\": \"$team Team\",
\"description\": \"Organization for $team development team\",
\"website\": \"https://internal.techcorp.com/teams/$team\",
\"visibility\": \"private\"
}" \
"https://git.techcorp.com/api/v1/orgs"
# Set up team repositories with templates
curl -X POST \
-H "Authorization: token $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"$team-template\",
\"description\": \"Template repository for $team projects\",
\"private\": true,
\"template\": true,
\"auto_init\": true,
\"gitignores\": \"Node,Python,Go\",
\"license\": \"MIT\"
}" \
"https://git.techcorp.com/api/v1/orgs/team-$team/repos"
done
Integration with existing tools:
# .forgejo/workflows/corporate-ci.yml
name: Corporate CI/CD Pipeline
on: [push, pull_request]
jobs:
security-scan:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: SonarQube Scan
run: |
sonar-scanner \
-Dsonar.projectKey=${{ github.repository }} \
-Dsonar.sources=. \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.login=${{ secrets.SONAR_TOKEN }}
deploy-staging:
needs: security-scan
runs-on: self-hosted
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v4
- name: Deploy to Staging
run: |
kubectl apply -f k8s/staging/ --namespace=staging-${{ github.event.repository.owner.login }}
kubectl rollout status deployment/${{ github.event.repository.name }} -n staging-${{ github.event.repository.owner.login }}
notify-teams:
runs-on: self-hosted
if: always()
steps:
- name: Notify Microsoft Teams
run: |
curl -H "Content-Type: application/json" -d '{
"text": "Build ${{ job.status }} for ${{ github.repository }} - ${{ github.sha }}"
}' ${{ secrets.TEAMS_WEBHOOK_URL }}
Results: TechCorp reduced licensing costs by $50,000/year, improved deployment frequency by 40%, and gained complete control over their development infrastructure.
Open Source Community Project
The FreeDesktop project migrated to Forgejo for better community governance:
#!/usr/bin/env python3
# Community contribution metrics script
import requests
import json
from datetime import datetime, timedelta
class ForgejoMetrics:
def __init__(self, base_url, token):
self.base_url = base_url
self.headers = {"Authorization": f"token {token}"}
def get_community_stats(self, org):
"""Get comprehensive community statistics"""
stats = {
"total_contributors": set(),
"repositories": 0,
"issues": {"open": 0, "closed": 0},
"pull_requests": {"open": 0, "merged": 0, "closed": 0},
"activity_last_30_days": []
}
# Get all repositories in organization
repos_response = requests.get(
f"{self.base_url}/api/v1/orgs/{org}/repos",
headers=self.headers
)
repositories = repos_response.json()
stats["repositories"] = len(repositories)
for repo in repositories:
repo_name = repo["name"]
# Get contributors
contributors_response = requests.get(
f"{self.base_url}/api/v1/repos/{org}/{repo_name}/contributors",
headers=self.headers
)
for contributor in contributors_response.json():
stats["total_contributors"].add(contributor["login"])
# Get issues statistics
issues_response = requests.get(
f"{self.base_url}/api/v1/repos/{org}/{repo_name}/issues",
headers=self.headers,
params={"state": "all", "per_page": 100}
)
for issue in issues_response.json():
if not issue.get("pull_request"): # Regular issues
if issue["state"] == "open":
stats["issues"]["open"] += 1
else:
stats["issues"]["closed"] += 1
else: # Pull requests
if issue["state"] == "open":
stats["pull_requests"]["open"] += 1
elif issue.get("merged_at"):
stats["pull_requests"]["merged"] += 1
else:
stats["pull_requests"]["closed"] += 1
stats["total_contributors"] = len(stats["total_contributors"])
return stats
def generate_report(self, org):
"""Generate comprehensive community report"""
stats = self.get_community_stats(org)
report = f"""
# {org} Community Report - {datetime.now().strftime('%Y-%m-%d')}
## Overview
- **Total Repositories:** {stats['repositories']}
- **Total Contributors:** {stats['total_contributors']}
- **Community Health Score:** {self.calculate_health_score(stats)}/100
## Issues
- Open: {stats['issues']['open']}
- Closed: {stats['issues']['closed']}
- Resolution Rate: {stats['issues']['closed']/(stats['issues']['open']+stats['issues']['closed'])*100:.1f}%
## Pull Requests
- Open: {stats['pull_requests']['open']}
- Merged: {stats['pull_requests']['merged']}
- Closed: {stats['pull_requests']['closed']}
- Merge Rate: {stats['pull_requests']['merged']/(sum(stats['pull_requests'].values()))*100:.1f}%
## Recommendations
{self.generate_recommendations(stats)}
"""
return report
def calculate_health_score(self, stats):
"""Calculate community health score based on various metrics"""
score = 0
# Repository activity (max 30 points)
if stats['repositories'] > 0:
score += min(stats['repositories'] * 2, 30)
# Contributor diversity (max 25 points)
if stats['total_contributors'] > 0:
score += min(stats['total_contributors'], 25)
# Issue resolution (max 25 points)
total_issues = stats['issues']['open'] + stats['issues']['closed']
if total_issues > 0:
resolution_rate = stats['issues']['closed'] / total_issues
score += resolution_rate * 25
# PR merge rate (max 20 points)
total_prs = sum(stats['pull_requests'].values())
if total_prs > 0:
merge_rate = stats['pull_requests']['merged'] / total_prs
score += merge_rate * 20
return min(int(score), 100)
def generate_recommendations(self, stats):
"""Generate actionable recommendations"""
recommendations = []
total_issues = stats['issues']['open'] + stats['issues']['closed']
if total_issues > 0:
resolution_rate = stats['issues']['closed'] / total_issues
if resolution_rate < 0.7:
recommendations.append("- Consider implementing issue triage processes")
recommendations.append("- Set up automated issue labeling")
if stats['total_contributors'] < 10:
recommendations.append("- Focus on contributor onboarding documentation")
recommendations.append("- Create 'good first issue' labels")
total_prs = sum(stats['pull_requests'].values())
if total_prs > 0:
merge_rate = stats['pull_requests']['merged'] / total_prs
if merge_rate < 0.6:
recommendations.append("- Review PR review process")
recommendations.append("- Consider implementing PR templates")
return "\n".join(recommendations) if recommendations else "- Community metrics look healthy!"
# Usage example
if __name__ == "__main__":
metrics = ForgejoMetrics("https://codeberg.org", "your_token_here")
report = metrics.generate_report("freedesktop")
print(report)
Non-Profit Organization Setup
EcoTech Foundation uses Forgejo for coordinating environmental projects:
# docker-compose.yml for resource-constrained deployment
version: "3.8"
services:
forgejo:
image: codeberg.org/forgejo/forgejo:1.21
container_name: ecotech-forgejo
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- FORGEJO__DEFAULT__APP_NAME=EcoTech Foundation
- FORGEJO__server__DOMAIN=git.ecotech.org
- FORGEJO__server__ROOT_URL=https://git.ecotech.org
- FORGEJO__database__DB_TYPE=sqlite3
- FORGEJO__service__DISABLE_REGISTRATION=false
- FORGEJO__service__REQUIRE_SIGNIN_VIEW=false
- FORGEJO__service__ENABLE_NOTIFY_MAIL=true
- FORGEJO__mailer__ENABLED=true
- FORGEJO__mailer__FROM=noreply@ecotech.org
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000"
- "127.0.0.1:2222:22"
networks:
- ecotech
# Lightweight monitoring
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_SCHEDULE=0 0 2 * * *
networks:
- ecotech
networks:
ecotech:
driver: bridge
Project collaboration workflow:
# .forgejo/workflows/project-review.yml
name: Project Proposal Review
on:
pull_request:
paths: ["projects/*/proposal.md"]
jobs:
proposal-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate Proposal Format
run: |
# Check if proposal follows template
for file in $(git diff --name-only origin/main...HEAD | grep 'proposal.md'); do
if ! grep -q "## Environmental Impact" "$file"; then
echo "❌ $file missing Environmental Impact section"
exit 1
fi
if ! grep -q "## Budget Estimate" "$file"; then
echo "❌ $file missing Budget Estimate section"
exit 1
fi
echo "✅ $file format validated"
done
- name: Notify Review Committee
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.FORGEJO_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{
"body": "🌱 New project proposal ready for review!\n\n**Proposal:** ${{ github.event.pull_request.title }}\n**Submitted by:** @${{ github.event.pull_request.user.login }}\n\n@review-committee please review this proposal.",
"assignees": ["committee-lead", "technical-reviewer", "budget-reviewer"]
}' \
"${{ github.api_url }}/repos/${{ github.repository }}/issues/${{ github.event.number }}/comments"
budget-check:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.body, 'budget:')
steps:
- uses: actions/checkout@v4
- name: Extract and Validate Budget
run: |
# Extract budget from proposal and check against limits
budget=$(grep -o 'budget: \$[0-9,]*' ${{ github.event.pull_request.body }} | grep -o '[0-9,]*' | tr -d ',')
if [ "$budget" -gt 10000 ]; then
echo "⚠️ Budget exceeds $10,000 - requires board approval"
# Auto-assign to board members
fi
Getting Started & Further Reading

Ready to join a community-led software forge? Explore Forgejo with these official links and learning resources.
Official Resources
Official Website: https://forgejo.org/
Documentation: https://forgejo.org/docs/latest/
Codeberg.org (Flagship Instance): https://codeberg.org/
Source Code & Community: https://codeberg.org/forgejo/forgejo
Learning Path for New Users
- Start with Codeberg: Create an account on codeberg.org to experience Forgejo without setup
- Explore the Documentation: Read through installation and configuration guides
- Join the Community: Participate in discussions on the Forgejo Matrix channels
- Try Self-Hosting: Set up a local instance using Docker for testing
- Plan Migration: If coming from another platform, plan your migration strategy
- Production Deployment: Deploy with proper security and backup procedures
Community Resources
- Matrix Chat:
#forgejo:matrix.org- Main community discussion - Development Chat:
#forgejo-development:matrix.org- Technical discussions - Monthly Community Calls: Check the website for schedules
- Contribution Guide: Detailed at codeberg.org/forgejo/forgejo/src/branch/forgejo/CONTRIBUTING.md
Training Materials
# Clone the examples repository
git clone https://codeberg.org/forgejo/forgejo-examples.git
# Explore different deployment scenarios
cd forgejo-examples
ls -la
# docker/ - Docker deployment examples
# kubernetes/ - K8s manifests and Helm charts
# nginx/ - Reverse proxy configurations
# systemd/ - Service files and scripts
# actions/ - Example CI/CD workflows
Frequently Asked Questions (FAQ)
Q: What is the main difference between Forgejo and Gitea?
A: Forgejo originated as a soft fork of Gitea in late 2022 due to governance concerns, but in early 2024, it became a hard fork. While they remain highly compatible, their codebases and distinct goals have diverged. Forgejo is governed by the non-profit Codeberg e.V., completely focused on FOSS principles, privacy, and federation.
Q: Can I migrate my Gitea instance to Forgejo easily?
A: Yes. Despite the hard fork, migration from Gitea remains thoroughly documented, natively supported, and typically requires just a straightforward binary or Docker image swap.
Q: Does Forgejo support federation?
A: Yes! As of recent 2026 releases (v14/v15), Forgejo ships with active federation capabilities. Using ActivityPub (and the ForgeFed vocabulary), instances can interconnect, allowing for decentralized issue tracking and repository interaction.
Q: Is Forgejo suitable for non-technical users?
A: Forgejo requires technical expertise to deploy and maintain. However, non-technical users can use public instances like Codeberg, which offers a highly accessible, GitHub-like web interface.
Q: What are the system requirements for running Forgejo?
A: Forgejo is incredibly lightweight and can comfortably run on hardware as small as a Raspberry Pi (1GB RAM / 1 CPU core for small teams). It supports Linux and macOS. Note: Native Microsoft Windows support was discontinued in 2024, though it runs perfectly via Docker on Windows machines.
Q: How does Forgejo handle versioning?
A: Forgejo has transitioned to semantic versioning with quarterly major releases (e.g., v14.0, v15.0). Every fourth release (published in Q1) is designated as a Long-Term Support (LTS) release, receiving security backports for over a year.
Q: Is Forgejo suitable for non-technical users?
A: Forgejo is primarily self-hosted, which requires some technical expertise to set up and maintain. However, non-technical users can use public instances like Codeberg, which offers a user-friendly interface for managing repositories similar to GitHub.
Q: How can I contribute to Forgejo?
A: You can contribute by joining the community on Codeberg, submitting code, reporting issues, helping with documentation, translations, or participating in governance discussions. Visit https://codeberg.org/forgejo/forgejo for detailed contribution guidelines.
Q: What are the system requirements for running Forgejo?
A: Forgejo is lightweight and can run on modest hardware. Minimum requirements are typically 1GB RAM and 1 CPU core for small teams, though requirements scale with usage. It supports SQLite for simple deployments or PostgreSQL/MySQL for larger installations.
Q: Can Forgejo integrate with existing enterprise systems?
A: Yes, Forgejo supports LDAP, SAML, OAuth, and other enterprise authentication methods. It also provides comprehensive APIs for integration with existing tools and workflows.
Q: Is there commercial support available for Forgejo?
A: While Forgejo itself is community-driven, several organizations and consultants provide commercial support, training, and professional services around Forgejo deployments.
Q: How does Forgejo handle security updates?
A: Forgejo maintains an active security response team that monitors for vulnerabilities and releases patches promptly. The community-driven development model ensures transparent handling of security issues.
Q: Can I use Forgejo Actions with existing GitHub Actions workflows?
A: Yes, Forgejo Actions is designed to be compatible with GitHub Actions workflows. Most workflows can be migrated with minimal or no changes.
Q: What’s the long-term roadmap for Forgejo?
A: Key goals include implementing federation capabilities, improving performance, enhancing user experience, and maintaining compatibility with the broader Git ecosystem. The roadmap is developed openly with community input.
Conclusion
Forgejo is more than just a copy of Gitea; it’s a powerful statement about the importance of governance in open source. It offers the same exceptional, lightweight performance and rich feature set, but wraps it in a promise of perpetual community ownership.
With comprehensive CI/CD capabilities through Forgejo Actions, extensive package registry support, robust APIs, and enterprise-grade features, Forgejo provides everything modern development teams need. The real-world examples and deployment scenarios covered in this guide demonstrate that Forgejo scales from individual developers to large organizations and institutions.
The detailed installation procedures, configuration examples, and migration scripts provided here should give you everything needed to get started. Whether you’re a individual developer seeking independence from corporate platforms, an organization prioritizing software freedom, or a institution requiring stable, long-term solutions, Forgejo offers a compelling alternative.
For developers and organizations who value software freedom as much as they value great software, Forgejo isn’t just an alternative—it’s the principled choice that ensures your development infrastructure remains truly yours.