Forgejo: self-hosted Git forge with Docker Compose
Install Forgejo with Docker Compose, then see how this Git forge compares with Gitea, GitLab CE, Gogs and OneDev and which one fits a home server or a small team.
Forgejo is a lightweight, self-hosted software forge: Git repositories with a web interface, pull requests, issues, a wiki, packages and CI. It looks and feels like GitHub, runs from a single container and needs no external services in its simplest setup. The project is developed by a community under Codeberg e.V., a non-profit, and describes itself as exclusively free software.
Forgejo started in October 2022 as a fork of Gitea. Since early 2024 it is a hard fork with its own codebase. If you only want a place for your own repositories, the two are close in daily use, and the comparison below is meant to help you pick.
What you need
- A server with Docker and the Compose plugin
- A domain or subdomain and a reverse proxy with HTTPS if the forge will be reachable from the internet
- Free ports 3000 for the web interface and 222 for Git over SSH, or other ports of your choice
If you do not want to host it yourself, Codeberg runs a public Forgejo instance where anyone can create an account.
Install
Create a folder and give the container user ownership of the data directory. The Forgejo documentation warns that the container may not start otherwise:
mkdir -p forgejo-app/forgejo
cd forgejo-app
sudo chown 1000:1000 forgejo
Create docker-compose.yml in that folder. This is the example from the Forgejo documentation for the current stable release, which stores data in SQLite:
networks:
forgejo:
external: false
services:
server:
image: codeberg.org/forgejo/forgejo:17
container_name: forgejo
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- forgejo
volumes:
- ./forgejo:/data
- /etc/localtime:/etc/localtime:ro
ports:
- '3000:3000'
- '222:22'
USER_UIDandUSER_GIDmust match the owner of the./forgejofolder.222:22publishes Git over SSH on port 222, so it does not clash with the SSH daemon of the host.- If a reverse proxy runs on the same server, change the first port line to
'127.0.0.1:3000:3000'so that the web interface is reachable only through the proxy. - If
codeberg.orgis not reachable from your server, use the project’s mirror: replacecodeberg.orgwithdata.forgejo.orgin the image name.
Start it:
docker compose up -d
Set your address and SSH port
Forgejo builds the clone links from its own settings. Behind a domain and with SSH on port 222, tell it so by adding three variables to the environment block:
- FORGEJO__server__ROOT_URL=https://git.example.com/
- FORGEJO__server__SSH_DOMAIN=git.example.com
- FORGEJO__server__SSH_PORT=222
Any app.ini setting can be set this way. The pattern is FORGEJO__<section>__<KEY>, so [server] ROOT_URL becomes FORGEJO__server__ROOT_URL. Environment variables can add or change values, not remove them. After editing run docker compose up -d again.
First run
Open http://<host>:3000. With no database configured Forgejo uses SQLite, which the documentation recommends for an instance with low or moderate activity. Finish the onboarding page and register the first user: that account becomes the administrator.
Then close registration, otherwise anyone who finds the address can create an account. Add this variable and apply it with docker compose up -d:
- FORGEJO__service__DISABLE_REGISTRATION=true
After that only an administrator can create users. To test SSH, add your public key in the account settings and run ssh -F /dev/null git@git.example.com -p 222, the check the Forgejo documentation suggests. It should authenticate with your key and not ask for a password.
The project keeps a page of recommended settings for different instance sizes, for example a size-limited cache for small servers and committer as the signature trust model if people contribute to each other’s repositories.
Use PostgreSQL instead of SQLite
SQLite needs no maintenance and is fine for one person or a small team. For many users or heavy use the documentation recommends PostgreSQL or MySQL. Moving an existing instance to another database later is not trivial, so decide before you fill it with data.
The official example uses PostgreSQL 14. Forgejo needs 14 or newer, and 14 reaches the end of its community support in November 2026, so this version uses 17:
networks:
forgejo:
external: false
services:
server:
image: codeberg.org/forgejo/forgejo:17
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=change-me
restart: always
networks:
- forgejo
volumes:
- ./forgejo:/data
- /etc/localtime:/etc/localtime:ro
ports:
- '3000:3000'
- '222:22'
depends_on:
- db
db:
image: postgres:17
restart: always
environment:
- POSTGRES_USER=forgejo
- POSTGRES_PASSWORD=change-me
- POSTGRES_DB=forgejo
networks:
- forgejo
volumes:
- ./postgres:/var/lib/postgresql/data
Use the same password in both places. The database is not published outside the Docker network.
CI with Forgejo Actions
Forgejo Actions runs continuous integration from workflow files in the .forgejo/workflows folder of a repository, in a format close to GitHub Actions. It is enabled by default, but Forgejo itself does not run the jobs. A separate program, Forgejo Runner, fetches jobs from your instance and executes them. It can run as a binary, as a Docker container or from a distribution package, and one runner can serve several instances.
The steps are in the documentation: install the runner, register it with your instance, then choose its labels in the runner configuration. Read the page on securing Actions deployments first: a runner executes code from your repositories, so run it on a separate machine or in an isolated environment, not next to the forge and its data. Building container images in workflows needs extra Docker access configuration, described in the same documentation.
Backup
The documentation advises stopping Forgejo during a backup, because data lives in several places that cannot easily be copied consistently while it runs. With the SQLite setup above, everything is under ./forgejo:
docker compose stop
tar czf forgejo-backup.tar.gz forgejo
docker compose start
With PostgreSQL also dump the database (pg_dump, or the tool of your database engine). In either case keep the app.ini file: it contains SECRET_KEY and JWT_SECRET. Without them a restored instance cannot read some encrypted values and the integrations that use OAuth have to be set up again.
Updating
For minor and patch updates under the same tag:
docker compose pull && docker compose up -d
Moving to a new major version (for example from 17 to 18) is a separate task. The upgrade guide asks for a full backup first, reading the release notes for breaking changes, emptying the job queues with forgejo manager flush-queues and, after the update, running forgejo doctor check --all inside the container and checking the main workflows in the web interface. Then change the image tag in docker-compose.yml and run the commands above.
Forgejo follows semantic versioning since 7.0: breaking changes appear only when the first number changes. To hear about security releases in advance, watch the security announcements repository on Codeberg.
Moving from Gitea
Forgejo supports an in-place upgrade from Gitea only up to Gitea 1.22. The upgrade goes through Forgejo 10, and from there to any newer version. The project announced that newer Gitea versions can no longer be upgraded this way, because the two codebases have diverged. If you run a recent Gitea, move repositories one by one with the built-in migration tool, which keeps working as long as the Gitea API stays stable.
Forgejo compared with other self-hosted forges
All of these host Git repositories on your own server. They differ in weight, in how much of the DevOps cycle they cover and in who controls the project. Facts below come from each project’s own documentation and repository on 13 November 2026.
| License | Built-in CI | Footprint | Fits | |
|---|---|---|---|---|
| Forgejo | GPL-3.0-or-later (MIT up to v8) | Forgejo Actions with a separate runner | Light, single container | Home server, small team, free-software focus |
| Gitea | MIT | Gitea Actions | Light, single container | Same as Forgejo, company-backed project |
| GitLab CE | MIT for the Community Edition; the paid edition’s code in ee/ has its own licence |
GitLab CI/CD | Heavy: 8 vCPU and 16 GB RAM baseline, at least 8 GB | Larger organizations that want one platform |
| Gogs | MIT | Not in its feature list | Very light: 512 MB RAM and 2 cores for a team | A minimal Git service on a Raspberry Pi |
| OneDev | MIT | Built-in CI/CD with a GUI job editor | Java, at least 2 GB of memory | CI-centric teams, code search and navigation |
| Harness Open Source | Apache-2.0 | Pipelines, the successor of Drone | Its quick start mounts the Docker socket | Pipelines, dev environments and package registries in one tool |
Forgejo and Gitea
Both come from Gogs and share the same history, so the interface, the concepts and the Git behaviour are similar. The differences are mostly about governance and licence.
- Governance. Forgejo is under Codeberg e.V., a non-profit. By Forgejo’s account, Gitea is controlled by a for-profit company through its domain and trademark, and Forgejo was created in October 2022 after those were transferred to it.
- License. Forgejo has been GPL v3 or later since version 9. Gitea is MIT. For running your own instance the difference does not matter. It matters if you modify the code and distribute it, or if you want a permissive licence on principle.
- Free software only. Forgejo says it is developed and released with free software only, using Forgejo itself and Forgejo Actions. It describes Gitea as open core, with some software not published under a free licence, and developed on GitHub. This is Forgejo’s own account from its comparison page; Gitea’s side is not covered here.
- Releases. Forgejo publishes a stable release every three months and an LTS every year, with semantic versioning. Gitea has its own release cycle.
- Federation. Forgejo works on forge federation based on ForgeFed. The project calls it experimental and reserves the right to break it without notice, so do not build on it yet.
- Compatibility. The two are no longer interchangeable. A switch from a modern Gitea is a manual migration.
The others
- GitLab CE is a complete DevOps platform: CI/CD, container registry, issue boards and more in one product. The price is resources. The official requirements for a single node are 8 vCPU and 16 GB of RAM, with 8 GB as the floor for a memory-constrained installation. For one person or a handful of repositories it is far too much.
- Gogs is the project both Gitea and Forgejo descend from. It does one thing: Git hosting with issues, pull requests and a wiki, on very modest hardware. It has no CI in its feature list, so pair it with an external CI tool.
- OneDev is a Java application with CI/CD built in (jobs can be created in a GUI or as code), code search and navigation, issue boards and a service desk. It suits teams for whom the pipeline is the centre of the work. It asks for at least 2 GB of memory, more than Gogs.
- Harness Open Source combines code hosting, pipelines, hosted development environments and artifact registries. Its pipeline part continues Drone. It is more of a platform than a lightweight forge.
Which one to choose
- For a personal server or a small team, Forgejo or Gitea. There is no large technical gap in everyday use, so choose by principle: take Forgejo if non-profit governance, a copyleft licence and a free-software-only project matter to you, and Gitea if you prefer MIT and a company-backed roadmap or already run it.
- If memory is tight, for example a Raspberry Pi with little RAM, Gogs.
- If you want a large platform with built-in CI/CD and have the hardware, GitLab CE.
- If CI/CD is the main goal, look at OneDev, or run Forgejo with Forgejo Actions.
Whichever you pick, decide before you migrate everything: moving between these forges later means moving repositories one at a time.