Forgejo: A Deep Dive into the Community-Driven Gitea Fork

Forgejo: A Deep Dive into the Community-Driven Gitea Fork

By Pashalis Laoutaris Category: GitHub Alternatives 21 min read

Editor’s Note: This article is part of our definitive guide to GitHub Alternatives. Click here to read the main overview.

Table of Contents

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 Maskot

Forgejo inherits the fantastic feature set of Gitea while adding a unique philosophical guarantee and a forward-looking vision.

FeatureDescriptionKey Benefit
Complete Gitea Feature SetForgejo 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 GovernanceThe 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 FederationA 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 CompatibilityForgejo 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.

AspectGitHubForgejo
Primary FocusA global, centralized developer platform.A community-governed, self-hosted, and federated software forge.
Hosting OptionsCloud (SaaS) and Self-hosted (Enterprise).Self-hosted only. (Codeberg provides a large public instance).
Development ModelCorporate (owned by Microsoft).Non-profit, community-driven.
Future VisionA 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.

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

Forgejo Pull Request

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

Forgejo Logo

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

  1. Start with Codeberg: Create an account on codeberg.org to experience Forgejo without setup
  2. Explore the Documentation: Read through installation and configuration guides
  3. Join the Community: Participate in discussions on the Forgejo Matrix channels
  4. Try Self-Hosting: Set up a local instance using Docker for testing
  5. Plan Migration: If coming from another platform, plan your migration strategy
  6. 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.


Back to All Posts
Share this post:
Share on Twitter
Share on LinkedIn
Share on Reddit
Share on Facebook
Copy Link
Copied!