Install via Docker / Podman

The official image is codeberg.org/forgejo/forgejo (Codeberg hosts the project). A minimal compose file:

# docker-compose.yml
services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:10
    container_name: forgejo
    restart: unless-stopped
    environment:
      USER_UID: 1000
      USER_GID: 1000
      FORGEJO__database__DB_TYPE: postgres
      FORGEJO__database__HOST: db:5432
      FORGEJO__database__NAME: forgejo
      FORGEJO__database__USER: forgejo
      FORGEJO__database__PASSWD: ${DB_PASSWORD}
    volumes:
      - ./data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "127.0.0.1:3000:3000"        # HTTP, behind reverse proxy
      - "2222:22"                    # SSH for git over ssh
    depends_on: [ db ]

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: forgejo
      POSTGRES_USER: forgejo
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./db:/var/lib/postgresql/data

For a single-user / homelab install, swap Postgres for SQLite by dropping the db service and setting FORGEJO__database__DB_TYPE: sqlite3. SQLite handles low-dozens-of-users projects without issue.

Binary install on Debian

For a non-container deploy:

FJV=10.0.0      # check codeberg.org/forgejo/forgejo/releases for current
sudo useradd -r -m -d /var/lib/forgejo -s /bin/bash forgejo

curl -L -o /usr/local/bin/forgejo \
    "https://codeberg.org/forgejo/forgejo/releases/download/v${FJV}/forgejo-${FJV}-linux-amd64"
sudo chmod +x /usr/local/bin/forgejo

sudo mkdir -p /etc/forgejo
sudo chown root:forgejo /etc/forgejo
sudo chmod 770 /etc/forgejo

# Sample systemd unit at /etc/systemd/system/forgejo.service
sudo tee /etc/systemd/system/forgejo.service <<'EOF'
[Unit]
Description=Forgejo
After=network.target postgresql.service

[Service]
RestartSec=2s
Type=simple
User=forgejo
Group=forgejo
WorkingDirectory=/var/lib/forgejo
ExecStart=/usr/local/bin/forgejo web --config /etc/forgejo/app.ini
Restart=always
Environment=USER=forgejo HOME=/var/lib/forgejo GITEA_WORK_DIR=/var/lib/forgejo

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload

First-run setup wizard at http://<host>:3000/ writes app.ini. After it, sudo systemctl enable --now forgejo.

Reverse proxy

# Caddy
git.example.com {
    reverse_proxy 127.0.0.1:3000
}
# nginx
server {
    listen 443 ssl http2;
    server_name git.example.com;
    # ssl_certificate / ssl_certificate_key ...

    client_max_body_size 1024M;
    proxy_request_buffering off;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Then in app.ini[server]: ROOT_URL = https://git.example.com/ and DOMAIN = git.example.com. Restart.

SSH access for git push/clone

Two options:

  • Built-in SSH server — the Forgejo binary listens on its own SSH port ([server] START_SSH_SERVER = true). Simplest: container exposes 2222, users do ssh -p 2222 git@git.example.com, or DNS+SRV trick to make port-22 connections find it.
  • System sshd integration — Forgejo writes user keys into /var/lib/forgejo/.ssh/authorized_keys with command= wrappers; system sshd on port 22 routes git users through. Set [server] START_SSH_SERVER = false.

The system-sshd path is the standard for production; built-in SSH is faster for a homelab.

Forgejo Actions (GitHub-Actions-compatible CI)

Forgejo Actions reuses the GitHub Actions YAML syntax and most existing action marketplace items. The runner is a separate process (similar to GitHub's actions-runner):

# On a runner host
RV=6.0.0
curl -L -o /usr/local/bin/forgejo-runner \
    "https://code.forgejo.org/forgejo/runner/releases/download/v${RV}/forgejo-runner-${RV}-linux-amd64"
sudo chmod +x /usr/local/bin/forgejo-runner

# Register with the Forgejo instance
# 1. On the Forgejo web UI: Site Admin → Runners → Create new runner → copy token
sudo forgejo-runner register \
    --instance https://git.example.com \
    --token <runner-token> \
    --name builder-01 \
    --labels docker:docker://node:20,ubuntu-22.04:docker://gitea/runner-images:ubuntu-22.04

# Run as a systemd service
sudo forgejo-runner daemon

Each repository can enable Actions in Settings → Actions, then drop a workflow file at .forgejo/workflows/ci.yaml:

name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm test

Most actions/* from the GitHub Actions marketplace work out of the box because Forgejo Actions reads the same workflow format and provides compatible system env vars. For actions that hit GitHub's REST API expecting github.com, there's a Forgejo-specific replacement or a tweak.

Container and package registries

Forgejo includes registries for:

  • OCI / Docker images (under git.example.com/<user>/-/packages/container/...)
  • npm, Maven, PyPI, RubyGems, Cargo, Composer, NuGet, Helm, Go modules

Authenticate by creating a personal access token (User Settings → Applications → Access Tokens) and use it as the password:

docker login git.example.com -u amir -p <pat>
docker tag my-app:latest git.example.com/amir/my-app:latest
docker push git.example.com/amir/my-app:latest

# npm
npm config set @amir:registry https://git.example.com/api/packages/amir/npm/
npm config set //git.example.com/api/packages/amir/npm/:_authToken <pat>
npm publish

Backups

Three sources:

  • The data directory (/data in containers, /var/lib/forgejo in binary installs) — repositories, attachments, LFS, avatars.
  • The databasepg_dump for Postgres, file-copy for SQLite.
  • app.ini — configuration, JWT secrets, SECRET_KEY.

Or use the built-in dumper, which produces a single tarball with all three:

sudo -u forgejo /usr/local/bin/forgejo dump \
    --config /etc/forgejo/app.ini \
    --file /var/backups/forgejo-$(date +%F).zip

The output is restorable on a different Forgejo instance via forgejo restore.

Migrating from GitHub / GitLab

For each repo to import: New Repository → Migration tab → paste the source URL and credentials. Forgejo mirrors the full Git history, issues, pull requests (with comments), labels, milestones, releases, and wiki. For bulk migration, the Forgejo CLI has forgejo migrate.