Forgejo is a lightweight, self-hosted Git forge: repositories, pull requests, issues, wikis, a package and container registry, and Forgejo Actions for CI/CD. It’s a community-governed fork of Gitea, stewarded by the non-profit Codeberg e.V., and it powers Codeberg.org. It runs comfortably on a small VPS.
Choosing between options? See self-hosted Git compared.
Requirements
- 1–2 vCPU and 1–2 GB RAM for a small team (more if CI runners run on the same host, which isn’t recommended).
- Docker and Docker Compose. See install Docker.
- A domain, e.g.
git.example.com.
Docker Compose with PostgreSQL
# /opt/forgejo/compose.yaml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:11 # pin a major version
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: change-me-db
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
FORGEJO__service__DISABLE_REGISTRATION: "true"
ports:
- "127.0.0.1:3000:3000"
- "2222:22"
volumes:
- ./data:/data
depends_on: [db]
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: forgejo
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: change-me-db
volumes:
- ./postgres:/var/lib/postgresql/data
cd /opt/forgejo && docker compose up -d
Use the current major version tag. Environment variables in the form FORGEJO__section__KEY map to app.ini settings.
SSH for Git
The compose file exposes Forgejo’s built-in SSH on port 2222, so the host’s own SSH stays on 22. Clone URLs look like ssh://[email protected]:2222/team/app.git. Open the port:
sudo ufw allow 2222/tcp
Want [email protected]:team/app.git on port 22? Either move the host’s admin SSH to another port, or set up SSH passthrough (the host’s sshd forwards the git user to the container), as described in the Forgejo docs. See change the SSH port.
HTTPS with Nginx
server {
server_name git.example.com;
client_max_body_size 512M; # large pushes, LFS, packages
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;
}
# listen 443 ssl; certbot --nginx -d git.example.com
}
See Let’s Encrypt.
First setup
Open https://git.example.com. The installation page shows the pre-filled settings. Create the administrator account there. Registration is disabled via the environment, so create other users as admin, or enable OAuth or OIDC login. Turn on 2FA for admins.
Forgejo Actions (CI/CD)
Actions use GitHub Actions-compatible workflows in .forgejo/workflows/. Run the Forgejo Runner on a separate VPS:
- In Site Administration → Actions → Runners, create a registration token.
- On the runner host:
docker run --rm -it -v $PWD/runner:/data code.forgejo.org/forgejo/runner:6 \
forgejo-runner register --instance https://git.example.com --token <token> --name runner-1 --no-interactive
- Run the runner as a service (Docker Compose), with Docker access for jobs.
Workflow syntax and security considerations are the same as Gitea’s. See Gitea Actions.
Migrate repositories
New Migration imports from GitHub, GitLab, Gitea and others, including issues, pull requests, releases and labels with a token. Pull mirrors keep a repository synced from elsewhere. Moving from Gitea: Forgejo supports upgrading from compatible Gitea versions. Check the version compatibility notes first.
Backups and upgrades
- Backup: stop or quiesce the container, then back up
./data(repositories, LFS, attachments, config) and the PostgreSQL database:
docker compose exec -T db pg_dump -U forgejo forgejo > /backups/forgejo-$(date +%F).sql
tar -czf /backups/forgejo-data-$(date +%F).tar.gz -C /opt/forgejo data
forgejo dump inside the container also creates a full archive. Copy everything off-server. See backup strategy.
- Upgrades: read the release notes, back up, change the image tag, then
docker compose pull && docker compose up -d. See updating self-hosted apps.
Quick reference
| Task | Command / setting |
|---|---|
| Start | docker compose up -d |
| Web | port 3000 behind Nginx |
| Git SSH | port 2222 (or passthrough on 22) |
| Settings via env | FORGEJO__section__KEY |
| Disable signups | FORGEJO__service__DISABLE_REGISTRATION=true |
| Workflows | .forgejo/workflows/*.yml |
Related: Gitea and CI/CD explained.