How-to guide Developer Tools 4 min read

How to Self-Host Gitea or Forgejo with Docker

The environment variable that closes public registration was renamed, so copying an older tutorial leaves your git host open to sign-ups while the compose file appears to say otherwise. Then SSH needs a port that the host is already using.

Kenji Tanaka
Developer Tools & Cloud Analyst
Published 31 Aug 2026, 4:13 PM (SGT)
Share:
A laptop screen showing two panes of source code side by side A laptop screen showing two panes of source code side by side Photo by markusspiske on Pixabay
Advertisement

Gitea and Forgejo are lightweight, self-hosted git services with issues, pull requests, a package registry and a web interface. They are the same idea and, for a while, were the same code, small enough to run comfortably on hardware you would not put a build server on.

Forgejo is a fork of Gitea under community governance. For a first installation the practical difference is small and the setup is close to identical; pick either, and know that instructions for one usually work for the other with the configuration prefix changed.

Close registration, and check you closed it

Out of the box, anyone who can reach the page can create an account. On a server exposed to the internet that is not a feature.

The setting to turn it off exists, and this is where copying an older tutorial bites. The short environment variable that once did the job was removed. The current form is a longer prefixed name, and the old one now does nothing at all. It does not error. It is simply ignored, and the server carries on accepting sign-ups while your compose file appears to say otherwise.

Create your admin account first, then set the current variable, restart, and then actually check by opening the sign-up page in a browser where you are not logged in. This is a case where verifying takes ten seconds and assuming has a long tail.

Take this one seriously. A self-hosted git host holds source code and often deployment credentials, and vulnerabilities in this class of software have been actively exploited.

SSH is the awkward part

Cloning over HTTPS works immediately. SSH is where the setup stops being a copy-paste job, because the server runs its own SSH daemon inside the container and your host is almost certainly already using port 22 for its own.

Two ports, one number. Something has to move, and there are three usual answers.

Publish the container's SSH on a non-standard port, and accept that clone URLs carry a port number. Move the host's own SSH to a different port and give the container 22, which is tidy for users and a decision to make carefully on a machine you administer remotely. Or forward from the host into the container, which keeps both on familiar ports at the cost of a piece of configuration that is easy to forget exists.

Advertisement

None is wrong. The confusion comes from guides that pick one method silently. A compose file from one article and an SSH instruction from another will conflict.

The user ID advice is contradictory because the answer depends

Some guides insist you set the user and group IDs to match your own; others say it is unnecessary. Both are right about different setups.

If the data directory is a path on the host, the container's user must be able to write to it, and the IDs have to match the owner. If the data lives in a named volume, Docker handles ownership and the setting is irrelevant.

Decide which you are using first, and the advice stops contradicting itself.

The rootless image is a different shape

There is a variant that runs without root inside the container. It is the better choice on security grounds, but it is not a drop-in substitute. The rootless image runs as a different user and listens on different ports.

A compose file written for the standard image will not work unchanged against the rootless one. If you are following a guide, check which image it uses before wondering why nothing binds.

What to do on the first day

Close registration and verify it. Take a backup of both the data directory and the database, because a git host with no backup is a single disk away from being the reason nobody has the code. And decide the SSH arrangement before anyone clones anything, since changing it later means every existing clone has a stale remote.

Where this comes from, and what will date

Composed from both projects' container documentation together with independent published setups and issue reports about the renamed configuration, cross-checked against each other on 31 August 2026. Configuration variable names often change between major versions. That is the point of the section, not an aside.

The two projects are diverging. They remain close enough that one guide covers both, and that will be less true over time. Check which one a set of instructions was written for, especially where it names a configuration variable.

Advertisement
Kenji Tanaka
Developer Tools & Cloud Analyst

Kenji Tanaka covers developer tools, cloud platforms, DevOps, CI/CD, and software supply-chain topics for RECATOOLS.

View author profile → · Editorial policy

About this byline Kenji Tanaka is a RECATOOLS editorial persona for developer tools, cloud, DevOps, and software supply-chain coverage. Articles are produced and reviewed under RECATOOLS editorial supervision.

Corrections policy

Advertisement