Self-Host Forgejo
What Is Forgejo?
Forgejo is a community-governed, self-hosted Git platform forked from Gitea in 2022. It provides repository hosting, pull requests, issue tracking, CI/CD via Forgejo Actions, package registries, and federation support through ActivityPub — all running on your own hardware with minimal resources. If you want a lightweight GitHub/GitLab alternative that answers to its community rather than a corporate entity, Forgejo is the right choice.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 512 MB of free RAM (1 GB recommended)
- 2 GB of free disk space (plus storage for repositories)
- A domain name (recommended for SSH and HTTPS access)
Docker Compose Configuration
This setup uses PostgreSQL as the database backend. SQLite works for personal use, but PostgreSQL handles concurrent users and larger installations far better.
Create a project directory:
mkdir -p ~/forgejo && cd ~/forgejo
Create a .env file with your secrets:
# .env — Change ALL of these values before starting
FORGEJO_DB_PASSWORD=replace_with_strong_password_32chars
POSTGRES_PASSWORD=replace_with_strong_password_32chars
FORGEJO_DOMAIN=git.example.com
FORGEJO_SSH_PORT=2222
Both password variables must match. Use openssl rand -base64 32 to generate a strong password.
Create a docker-compose.yml file:
services:
forgejo:
image: codeberg.org/forgejo/forgejo:14.0.3
container_name: forgejo
restart: unless-stopped
ports:
- "3000:3000" # Web UI
- "${FORGEJO_SSH_PORT:-2222}:22" # SSH for Git operations
environment:
USER_UID: "1000"
USER_GID: "1000"
FORGEJO__database__DB_TYPE: "postgres"
FORGEJO__database__HOST: "forgejo_db:5432"
FORGEJO__database__NAME: "forgejo"
FORGEJO__database__USER: "forgejo"
FORGEJO__database__PASSWD: "${FORGEJO_DB_PASSWORD}"
FORGEJO__server__DOMAIN: "${FORGEJO_DOMAIN}"
FORGEJO__server__ROOT_URL: "https://${FORGEJO_DOMAIN}"
FORGEJO__server__SSH_DOMAIN: "${FORGEJO_DOMAIN}"
FORGEJO__server__SSH_PORT: "${FORGEJO_SSH_PORT:-2222}"
FORGEJO__server__LFS_START_SERVER: "true"
FORGEJO__service__DISABLE_REGISTRATION: "false" # Set to true after creating your account
FORGEJO__service__REQUIRE_SIGNIN_VIEW: "false" # Set to true for private instances
volumes:
- forgejo_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
forgejo_db:
condition: service_healthy
networks:
- forgejo
forgejo_db:
image: postgres:16-alpine
container_name: forgejo_db
restart: unless-stopped
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
POSTGRES_DB: forgejo
volumes:
- forgejo_pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U forgejo"]
interval: 10s
timeout: 5s
retries: 5
networks:
- forgejo
networks:
forgejo:
external: false
volumes:
forgejo_data:
forgejo_pgdata:
Start the stack:
docker compose up -d
Wait about 30 seconds for PostgreSQL to initialize and Forgejo to run its first-time database migrations.
Initial Setup
- Open
http://your-server-ip:3000in your browser. - Forgejo shows the installation page. Most fields are pre-filled from the environment variables:
- Database Settings — already configured via environment variables, no changes needed.
- General Settings — set Site Title to your preferred name (e.g., “My Git Server”).
- Administrator Account — create your admin account. Do this now; you can disable registration afterward.
- Click Install Forgejo.
- Log in with the admin account you just created.
- After setup, set
FORGEJO__service__DISABLE_REGISTRATIONto"true"in yourdocker-compose.ymland restart:
docker compose down && docker compose up -d
This prevents random users from creating accounts on your instance.
Configuration
SSH Access
The Docker setup maps port 2222 on the host to port 22 inside the container, avoiding conflicts with your server’s SSH daemon:
# Clone via SSH
git clone ssh://[email protected]:2222/username/repo.git
Add an SSH config entry for convenience (~/.ssh/config):
Host forgejo
HostName git.example.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
Then clone with:
git clone forgejo:username/repo.git
Add your public SSH key under Settings → SSH / GPG Keys in the Forgejo web UI.
Email Notifications
Add these environment variables to the forgejo service to enable outbound email:
environment:
FORGEJO__mailer__ENABLED: "true"
FORGEJO__mailer__PROTOCOL: "smtps"
FORGEJO__mailer__SMTP_ADDR: "smtp.example.com"
FORGEJO__mailer__SMTP_PORT: "465"
FORGEJO__mailer__USER: "[email protected]"
FORGEJO__mailer__PASSWD: "your-smtp-password"
FORGEJO__mailer__FROM: "[email protected]"
Adjust the protocol to smtp+starttls if your provider uses port 587 with STARTTLS instead of implicit TLS on 465.
Webhooks
Forgejo supports webhooks out of the box. Configure them per-repository under Settings → Webhooks. Supported targets include Forgejo (native), Gitea, Slack, Discord, Microsoft Teams, Telegram, and generic HTTP endpoints. No additional configuration is needed in Docker — webhooks work with the default setup.
Forgejo Actions (CI/CD)
Forgejo Actions is compatible with GitHub Actions workflow syntax. Enable it by adding this environment variable:
environment:
FORGEJO__actions__ENABLED: "true"
Then register a runner. Add this service to your docker-compose.yml:
forgejo_runner:
image: codeberg.org/forgejo/runner:6.3.1
container_name: forgejo_runner
restart: unless-stopped
environment:
FORGEJO_URL: "http://forgejo:3000"
FORGEJO_RUNNER_REGISTRATION_TOKEN: "your-registration-token"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- forgejo
networks:
- forgejo
Get the registration token from Site Administration → Actions → Runners in the Forgejo web UI. Replace your-registration-token with the actual token and restart the stack.
Federation (ActivityPub)
Forgejo is pioneering federation for code forges via ActivityPub. Enable it with:
environment:
FORGEJO__federation__ENABLED: "true"
Federation allows users on different Forgejo instances to interact with repositories, stars, and profiles across instances — similar to how Mastodon federates social posts. This feature is still maturing, but it is a key differentiator from Gitea and GitLab.
Migrating from Gitea
Forgejo is a direct fork of Gitea, so migration is straightforward. Forgejo supports transparent upgrades from Gitea versions up to v1.22.x.
Migration Steps
- Back up everything first:
# Dump Gitea data
docker compose exec -u git gitea /usr/local/bin/gitea dump -c /data/gitea/conf/app.ini
# Back up PostgreSQL
docker compose exec gitea_db pg_dump -U gitea gitea > gitea-backup-$(date +%Y%m%d).sql
- Stop Gitea:
docker compose down
- Update your
docker-compose.yml— change the image fromgitea/gitea:1.22.xtocodeberg.org/forgejo/forgejo:14.0.3. Rename environment variable prefixes fromGITEA__toFORGEJO__:
# Before (Gitea)
image: gitea/gitea:1.22.6
environment:
GITEA__database__DB_TYPE: "postgres"
# After (Forgejo)
image: codeberg.org/forgejo/forgejo:14.0.3
environment:
FORGEJO__database__DB_TYPE: "postgres"
- If migrating from Gitea 1.22.x, first upgrade to Forgejo v10.0, let it run and complete migrations, then upgrade to v14.0.3:
# Step 1: Upgrade to Forgejo v10 first
image: codeberg.org/forgejo/forgejo:10.0.2
docker compose up -d
# Wait for migrations to complete (check logs)
docker compose logs -f forgejo
# Once stable, update to v14
# Step 2: Now upgrade to v14
image: codeberg.org/forgejo/forgejo:14.0.3
docker compose up -d
- Verify the migration:
docker compose exec forgejo forgejo doctor check --all
Your repositories, users, issues, and settings carry over. Existing Git remotes continue to work without changes.
Reverse Proxy
Update your environment variables to reflect your public domain:
environment:
FORGEJO__server__DOMAIN: "git.example.com"
FORGEJO__server__ROOT_URL: "https://git.example.com"
FORGEJO__server__SSH_DOMAIN: "git.example.com"
For Nginx Proxy Manager, create a proxy host with:
- Scheme: http
- Forward Hostname: forgejo (or the container IP)
- Forward Port: 3000
- Enable WebSocket Support: Yes
- SSL: Request a Let’s Encrypt certificate
For Caddy, add to your Caddyfile:
git.example.com {
reverse_proxy forgejo:3000
}
See Reverse Proxy Setup for detailed configuration with Nginx Proxy Manager, Traefik, and Caddy.
Backup
Forgejo’s Built-in Dump
docker compose exec forgejo forgejo dump -c /data/gitea/conf/app.ini
This creates a ZIP archive containing repositories, the database, configuration, LFS objects, and attachments. The output file lands in the current working directory inside the container (typically /data).
PostgreSQL Backup
docker compose exec forgejo_db pg_dump -U forgejo forgejo > forgejo-db-$(date +%Y%m%d).sql
Volume Backup
# Stop services to ensure consistency
docker compose down
# Back up named volumes
docker run --rm -v forgejo_forgejo_data:/data -v $(pwd):/backup alpine \
tar czf /backup/forgejo-data-$(date +%Y%m%d).tar.gz -C /data .
docker run --rm -v forgejo_forgejo_pgdata:/data -v $(pwd):/backup alpine \
tar czf /backup/forgejo-pgdata-$(date +%Y%m%d).tar.gz -C /data .
# Restart
docker compose up -d
Schedule backups with cron and store copies off-server. See Backup Strategy for the 3-2-1 backup approach.
Troubleshooting
Database Connection Refused on First Start
Symptom: Forgejo logs show dial tcp: connect: connection refused for PostgreSQL.
Fix: PostgreSQL may not be ready yet. The healthcheck and depends_on condition in the Compose file handle this, but if you removed them, add them back. Alternatively, restart the stack: docker compose restart forgejo. Check that FORGEJO__database__PASSWD matches POSTGRES_PASSWORD exactly.
SSH Clone Returns “Permission Denied”
Symptom: git clone ssh://git@... permission denied (publickey)
Fix: Verify your SSH public key is added in Forgejo under Settings → SSH / GPG Keys. Confirm the SSH port mapping — if you mapped host port 2222, use ssh://git@host:2222/user/repo.git. Check that FORGEJO__server__SSH_PORT matches the host-side port in your Docker port mapping.
”404 Page Not Found” After Reverse Proxy Setup
Symptom: Forgejo loads on port 3000 directly but returns 404 through the reverse proxy.
Fix: Ensure FORGEJO__server__ROOT_URL is set to your full public URL including the scheme (e.g., https://git.example.com). Enable WebSocket support in your reverse proxy. Restart Forgejo after changing ROOT_URL — it does not pick up changes at runtime.
Actions Runner Shows “Offline”
Symptom: The runner appears as offline in Site Administration → Actions → Runners.
Fix: Verify the registration token matches what Forgejo generated. Ensure the runner container can reach http://forgejo:3000 on the Docker network. Check runner logs: docker compose logs forgejo_runner. If the token was regenerated, update the runner’s environment variable and restart it.
High Memory Usage with Large Repositories
Symptom: Forgejo uses significantly more RAM when hosting repositories with large files or long histories.
Fix: Enable Git LFS for large binary files instead of committing them directly. Set FORGEJO__server__LFS_START_SERVER to "true" (included in the default config above). For very large repositories, consider increasing server RAM to 2 GB or more.
Resource Requirements
- RAM: ~120 MB idle, ~350 MB under moderate load (with PostgreSQL)
- CPU: Low — Forgejo is written in Go and is efficient. Git push/pull and Actions workflows are the main CPU consumers.
- Disk: ~250 MB for the Forgejo application. Repository storage scales with your usage. PostgreSQL adds ~50 MB overhead.
Forgejo runs comfortably on a 1 vCPU / 1 GB RAM server for small teams (under 20 users). For larger teams or heavy CI/CD usage, allocate 2+ vCPUs and 2+ GB RAM.
Verdict
Forgejo is the self-hosted Git platform we recommend for most users. It gives you everything Gitea offers — lightweight Go binary, clean web UI, Actions-based CI/CD, package registries — plus community governance that ensures the project stays open and user-focused. The federation support via ActivityPub is a genuine differentiator that no other Git forge provides: your instance can interact with other Forgejo instances across the internet.
If you are currently on Gitea, migrating to Forgejo is painless — swap the Docker image, rename the environment variable prefix, and your data carries over. If you need a heavier platform with built-in DevOps pipelines, container scanning, and enterprise compliance features, look at GitLab CE. For everyone else, Forgejo is the right choice.
Frequently Asked Questions
What is the difference between Forgejo and Gitea?
Forgejo is a fork of Gitea created in 2022 when Gitea’s governance shifted toward a for-profit company. Functionally, they are nearly identical — both are lightweight Go-based Git forges with pull requests, issues, CI/CD, and package registries. The key differences: Forgejo is community-governed (no corporate entity can restrict features), and Forgejo is pioneering ActivityPub federation for code forges. Gitea has a larger user base and slightly faster feature release cycle.
Can I migrate from Gitea to Forgejo?
Yes, and it is straightforward. Forgejo supports transparent upgrades from Gitea up to version 1.22.x. Swap the Docker image from gitea/gitea to codeberg.org/forgejo/forgejo, rename environment variable prefixes from GITEA__ to FORGEJO__, and start the container. Your repositories, users, issues, and settings carry over. See the Migration section above for step-by-step instructions.
Is Forgejo compatible with GitHub Actions?
Yes. Forgejo Actions uses the same workflow syntax as GitHub Actions. Most GitHub Actions workflows work with minimal or no changes. You need to register a Forgejo Runner (codeberg.org/forgejo/runner) to execute workflows. The runner uses Docker to run jobs, just like GitHub’s hosted runners.
What is Forgejo federation?
Forgejo is implementing ActivityPub — the same protocol that powers Mastodon and the fediverse — for code forges. When enabled, users on different Forgejo instances can discover, star, and interact with repositories across instances without creating accounts on each one. This feature is still maturing but represents a unique direction no other Git forge is pursuing.
Can Forgejo run on a Raspberry Pi?
Yes. Forgejo publishes official ARM64 images and runs well on a Raspberry Pi 4 with 1+ GB RAM. The Go binary is efficient — expect ~120 MB idle memory with PostgreSQL. For a small team (under 10 users), a Pi 4 with 2 GB RAM is sufficient.
Is Forgejo free and open source?
Yes. Forgejo is licensed under the MIT license (inherited from Gitea) and is governed by the Codeberg e.V. nonprofit. There is no paid tier and no closed-source components. The project’s charter guarantees it will remain free and community-governed.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments