Install via docker compose

# docker-compose.yml
services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:latest
    container_name: forgejo
    restart: unless-stopped
    user: "1000:1000"
    ports:
      - "127.0.0.1:3000:3000"
      - "127.0.0.1:2222:22"
    volumes:
      - ./data:/var/lib/gitea
      - ./config:/etc/gitea
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    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_PW}
      FORGEJO__server__DOMAIN: git.example.com
      FORGEJO__server__ROOT_URL: https://git.example.com
      FORGEJO__server__SSH_DOMAIN: git.example.com
      FORGEJO__server__SSH_PORT: 2222
    depends_on: [ db ]

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

Reverse proxy

# Caddy
git.example.com {
    reverse_proxy 127.0.0.1:3000
    request_body { max_size 500MB }
}

SSH for git: redirect port 22 on a public IP to the container's 2222 via your router / iptables, or front with a TCP-only proxy. Or skip SSH entirely and push over HTTPS with a deploy token.

First-run wizard

Browse to https://git.example.com. The install wizard runs once:

  • Database: select Postgres + provide the connection details (from env)
  • Server URL: confirm https://git.example.com
  • Disable open registration (registration only via admin invite is the safer default)
  • Create the admin user

After clicking Install, the wizard is gone forever; subsequent admin happens via the UI or the forgejo CLI inside the container.

What Forgejo gives you

  • Repos — Git over HTTPS + SSH, branches, tags, releases, mirroring (pull from / push to other Git remotes)
  • Issues + projects — per-repo and org-wide projects with kanban boards, labels, milestones
  • Pull requests — with review tooling, CODEOWNERS, branch protection
  • Wiki — per-repo wiki (also git-backed)
  • Package registry — npm, Maven, NuGet, PyPI, RubyGems, Helm, container (OCI), Composer, Vagrant, Cargo, Conan, Conda. The Forgejo registry replaces "I need a separate Verdaccio + Nexus" for small teams.
  • Forgejo Actions — GitHub Actions-compatible CI (runs act-runner agents).
  • OAuth provider — other apps can use Forgejo for SSO
  • Federation — experimental ActivityPub-based "follow repos / users across instances"

Forgejo Actions: CI built in

Install an act-runner alongside Forgejo:

services:
  runner:
    image: gitea/act_runner:latest
    container_name: forgejo-runner
    restart: unless-stopped
    environment:
      CONFIG_FILE: /config.yaml
      GITEA_INSTANCE_URL: http://forgejo:3000
      GITEA_RUNNER_REGISTRATION_TOKEN: ${RUNNER_TOKEN}
      GITEA_RUNNER_NAME: forgejo-runner-1
    volumes:
      - ./runner-data:/data
      - /var/run/docker.sock:/var/run/docker.sock

Get a runner registration token from Forgejo Admin Panel → Actions → Runners. The runner connects, registers itself, picks up jobs.

Drop a .forgejo/workflows/ci.yml in any repo (GitHub Actions syntax):

name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: docker
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npm test

Almost every GitHub Action works unchanged. Some marketplace actions that depend on GitHub API specifically need adjustment, but the common ones (checkout, setup-*, build-push-action) are fine.

OCI registry

# Push container images directly to Forgejo
docker login git.example.com -u amir -p <token>
docker tag myapp git.example.com/myorg/myapp:1.0
docker push git.example.com/myorg/myapp:1.0

# Pull
docker pull git.example.com/myorg/myapp:1.0

One registry for source + container images + Helm charts + other package types. Reduces the "I need Verdaccio for npm + Nexus for Maven + my own Docker registry" sprawl.

Mirroring

Per repo, configure pull mirror (Forgejo pulls from an upstream GitHub repo) or push mirror (Forgejo pushes to a downstream GitHub repo). Useful for:

  • Read-only copy of public GitHub repos for "this still works if GitHub is down" patterns
  • Bidirectional sync of internal-and-external repos
  • Migration from GitHub: pull-mirror, work in Forgejo, eventually flip the canonical

Auth

Beyond the built-in user database:

  • OAuth providers: GitHub, GitLab, Discord, etc.
  • OIDC: Authentik (see that tutorial), Keycloak, or anything OIDC-compliant
  • LDAP / Active Directory
  • SMTP-based (login via your mail server's credentials)

For team Forgejo, disable built-in registration + require SSO; new users land via SSO with auto-creation enabled.

Backups

# Built-in dump tool (database + all repos + LFS + attachments)
docker compose exec -u forgejo forgejo forgejo dump -c /etc/gitea/app.ini -f /var/lib/gitea/dump.zip

# Or pg_dump the database + restic the data directory
docker compose exec db pg_dump -U forgejo forgejo > backup-$(date +%F).sql
restic backup ./data

The data directory holds repos as bare Git repos — can be cloned with regular Git tooling if Forgejo is ever broken.

Forgejo vs Gitea vs GitLab vs GitHub Enterprise

  • Gitea — the original; Forgejo's parent. After Gitea Ltd's commercial restructuring, the community forked into Forgejo. Both are still maintained; Forgejo is community-governed (Codeberg e.V.).
  • GitLab — much bigger; CI built in (GitLab CI); also self-hostable. Significantly heavier resource use.
  • GitHub Enterprise Server — commercial; the canonical GitHub experience self-hosted; expensive.
  • Bitbucket Server — Atlassian; reaching end-of-life for self-hosted in many tiers.

For "small to medium team that wants a GitHub-shaped forge on hardware they own without GitLab's footprint," Forgejo is the right size in 2026.

When Forgejo isn't enough

  • If you need GitHub Marketplace-scale integrations / large team CI minutes / advanced security scanning, GitHub Enterprise has features Forgejo doesn't.
  • If you need GitLab-style merge trains, large-team CI workflows, etc., GitLab Self-Managed is the more featureful alternative.
  • For "I just want a git server, no UI," plain bare repos over SSH (or Gitolite for fine-grained access) is simpler.