
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. It is a “soft fork” of Gitea, created by a community of users and contributors to ensure that the project’s future remains in the hands of a non-profit, community-driven organization.
Born out of concerns following the creation of a for-profit company to manage Gitea, Forgejo’s mission is to be a truly free and open-source software (FOSS) forge, managed under the stewardship of the Codeberg e.V. non-profit. It is technically very similar to Gitea, but philosophically, it represents a commitment to community ownership and non-commercial governance.
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 |
---|---|---|
Complete Gitea Feature Set | Forgejo includes all the powerful features of Gitea: lightweight performance, easy installation, integrated CI/CD, package registries, and more. | You get a mature, feature-rich Git service without compromising on performance or ease of use. |
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. |
Focus on Federation | A key goal is to enable federation, allowing different Forgejo (and other forge) instances to interconnect using protocols like ActivityPub. | Aims to break down the silos between code hosting platforms, creating a future of decentralized, collaborative software development. |
Soft Fork Compatibility | Forgejo aims to stay close to Gitea’s upstream, merging features and security patches while maintaining its own governance and direction. | Users benefit from the rapid development of the larger Gitea ecosystem while being protected by Forgejo’s non-profit model. Migration is trivial. |
The Forgejo Philosophy: Who Is It For?
The philosophy of Forgejo is centered on software freedom 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:
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 or sudden licensing changes. The flagship instance, Codeberg, is a prime example.
Former Gitea Users: Those who were concerned by the commercialization of Gitea and want to move to a platform with the same features but a different governance model.
Anyone interested in a decentralized future: Its commitment to federation makes it a 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 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. |
Future Vision | A single, unified platform for all developers. | A decentralized network of independent, interconnected forges. |
Installation Guide
Setting up Forgejo is straightforward and can be accomplished through several methods. 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:1.21
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:14
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:
# Download the latest release
wget -O forgejo https://codeberg.org/forgejo/forgejo/releases/download/v1.21.0/forgejo-1.21.0-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
[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 = [email protected]
MAILER_TYPE = smtp
HOST = mail.example.com:587
USER = [email protected]
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 [email protected] --password securepass123
# Make user admin
sudo -u git forgejo admin create-user --name admin --email [email protected] --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 Forgejo runner
wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v3.3.0/forgejo-runner-3.3.0-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: [16, 18, 20]
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.
Inherits All of Gitea’s Strengths: You get the incredible performance, low resource usage, and rich feature set of one of the best lightweight Git servers available.
Easy Migration from Gitea: Because it is a soft fork, migrating an existing Gitea instance to Forgejo is a simple and well-documented process.
Pioneering Federation: By choosing Forgejo, you are supporting and participating in the movement towards a more open and decentralized development ecosystem.
Comprehensive CI/CD: Built-in Actions support provides GitHub-compatible workflows without external dependencies.
Enterprise-Ready: Supports LDAP, SAML, OAuth, and other enterprise authentication methods.
Active Development: Regular updates and security patches from a dedicated community.
Potential Drawbacks
Risk of Divergence: As with any fork, there is a long-term risk that it could diverge significantly from Gitea, potentially missing out on features or making future updates more complex.
Younger Brand: While the code is mature, the Forgejo brand is newer and has less recognition than Gitea, which might mean a slightly smaller pool of community support articles initially.
Entirely Self-Hosted: Like Gitea, there is no official SaaS offering. You are responsible for the administration, security, and maintenance of your instance.
Dependent on Community Health: Its success relies on maintaining a healthy, active, and well-organized community of contributors and maintainers.
Resource Requirements: While lightweight, running a full Git forge with CI/CD requires dedicated server resources.
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
- [email protected]
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 is a soft fork of Gitea, meaning it shares most of its codebase and features. The primary difference lies in governance: Forgejo is managed by a non-profit, community-driven organization (Codeberg e.V.), ensuring it remains free and open-source forever, while Gitea is controlled by a for-profit entity.
Q: Can I migrate my Gitea instance to Forgejo easily?
A: Yes, migration is straightforward due to Forgejo being a soft fork of Gitea. The process typically involves backing up your data, updating the binary, and potentially making minor configuration adjustments. The process is well-documented in the Forgejo documentation.
Q: Does Forgejo support federation yet?
A: Federation is a long-term goal for Forgejo, using protocols like ActivityPub to connect instances. As of now, it is under active development but not yet fully implemented. You can track progress on the federation roadmap in their development discussions.
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.