How to Use Forgejo: The Beginner's Guide (First Compose, the ROOT_URL Rule, First Repo, HTTPS and SSH, and the Disable-Registration Rule)

CodexFuse Team · 2026-10-31T12:08:15.499Z · 15 min read

Learn how to use Forgejo: install your own Git server on Docker, set the ROOT_URL rule, close open registration, and push over HTTPS and SSH.

Forgejo is a community-governed, self-hosted Git platform that puts your code, issues, and pull requests on your own server instead of someone else's cloud. Learning how to use Forgejo means you can run a lean, fully open-source Git service — forked from Gitea in 2022 — that handles repositories, code review, wikis, releases, a package registry, and even CI/CD through Forgejo Actions. This Forgejo tutorial is the complete beginner arc: the first Docker Compose file, the ROOT_URL rule that keeps clone links working, the setup wizard and first admin account, the disable-registration rule, your first repository, pushing over HTTPS and SSH, and the backup habit that saves you when something breaks. It assumes you can follow a Docker beginner guide and have a small VPS with a domain pointed at it.

What Is Forgejo, and Why Would You Self-Host Your Git?

Forgejo is a self-hosted Git forge: a web application you run on your own server that gives you Git hosting plus the workbench around it — issues, pull requests, code review, wikis, releases, and a package registry for container images, npm, Maven, and PyPI packages. It is the community fork of Gitea, created in 2022 when the community had governance concerns, and it stays truly free and open (selfhosting.sh). The practical reason to self-host is data sovereignty: your code, issues, and project history live exclusively on your hardware, with no dependence on another provider's terms, rate limits, or acquisitions. Forgejo is deliberately frugal — it runs comfortably on the smallest VPS — yet does almost everything a solo developer or small team uses GitHub for.

The reason Forgejo keeps gaining ground as the community default is the direction it is heading: its own release line on codeberg.org (v17.0.1, released 2026-10-26 per the Codeberg releases API), Forgejo Actions for CI/CD, and federation support through ActivityPub. If you want Git without the subscription and without the lock-in, Forgejo is the tool built for exactly that job.

How Do I Use Forgejo?

Using Forgejo follows one straight arc from an empty VPS to your first push. The seven steps are:

  1. Start one container with Docker Compose — the image is codeberg.org/forgejo/forgejo:17.0 (pinned minor line, SQLite default).
  2. Set the ROOT_URL rule: FORGEJO__server__ROOT_URL must be exactly your public HTTPS address, or clone links and redirects break.
  3. Finish the setup wizard at your domain and create the first admin account.
  4. Close registration with FORGEJO__service__DISABLE_REGISTRATION=true so strangers cannot self-register.
  5. Create your first repository from the + menu with "Initialize repository" checked.
  6. Push over HTTPS with an access token from Settings → Applications (not your password).
  7. Back up /data — the whole instance lives there.

What Do You Need Before Installing Forgejo?

You need three things: a server, a domain, and Docker. The server can be the smallest VPS you can rent — Forgejo is famously light — and if you do not have one yet, the what is VPS hosting explainer covers how VPS hosting works. The domain (or subdomain like git.example.com) needs an A or AAAA record pointing at the server's IP, because the ROOT_URL rule in step 2 of the arc depends on it. Finally, Docker and the Docker Compose plugin must be installed on the server — follow the Docker beginner guide if you have not set Docker up yet. On your own machine you need a Git client and, for the SSH path, an SSH key pair (ssh-keygen -t ed25519 generates one if you have none).

How to Install Forgejo with Docker Compose

The beginner-friendly install is a single container with SQLite — no separate database container, which is easily enough for a personal or small-team Git server (serverkueche's Forgejo guide). Create the stack directory and a compose.yaml file:

mkdir -p /opt/forgejo && cd /opt/forgejo

Write compose.yaml, replacing git.example.com with your real domain:

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:17.0
    container_name: forgejo
    restart: unless-stopped
    environment:
      USER_UID: 1000
      USER_GID: 1000
      FORGEJO__server__DOMAIN: git.example.com
      FORGEJO__server__ROOT_URL: https://git.example.com/
      FORGEJO__server__SSH_DOMAIN: git.example.com
      FORGEJO__server__START_SSH_SERVER: "true"
      FORGEJO__server__SSH_PORT: "2222"
      FORGEJO__server__SSH_LISTEN_PORT: "2222"
      FORGEJO__service__DISABLE_REGISTRATION: "true"
      FORGEJO__database__DB_TYPE: sqlite3
    volumes:
      - forgejo_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "2222:2222"
volumes:
  forgejo_data: {}

Start it with docker compose up -d. The important choices here are all deliberate. The :17.0 tag pins the major line to 17 while still tracking patch releases (the official docs note the 17 tag tracks the latest 17.0.x, and moving from one major line to the next requires a manual, human-verified upgrade — so a pinned tag like 17.0 is the safe middle ground). The volume forgejo_data maps to /data inside the container, and the official docs warn that it must be owned by the UID/GID you declare — the container may refuse to start with wrong permissions. Forgejo configures itself entirely through environment variables in the FORGEJO__[SECTION]__[KEY] format (double underscores separate section from key), and SQLite is the default database, so a single container is a complete install.

The ROOT_URL Rule: Set It or Your Clone Links Break

The ROOT_URL rule is the one setting beginners get wrong most often: FORGEJO__server__ROOT_URL must be exactly the public HTTPS address of your instance — including the scheme and the trailing slash — or every clone link and redirect Forgejo generates will point somewhere useless. If the compose file sets it to https://git.example.com/, the repository page's HTTPS/SSH toggle shows real, working clone addresses. If you leave it at the default localhost, or set it to an HTTP address while you serve HTTPS, the clone URLs are wrong and pushes fail in confusing ways. The related SSH_DOMAIN and SSH_PORT variables control what shows up in the SSH clone addresses, so set those to your real domain and your real SSH port (2222 in the compose above) as well. When clone links ever show the wrong host, the fix is always the same: correct ROOT_URL, SSH_DOMAIN, and SSH_PORT, then restart with docker compose up -d.

First Login, First Admin, and the Disable-Registration Rule

The disable-registration rule is simple: close open self-registration from the start, and your Git server is never open to strangers. The compose above already sets FORGEJO__service__DISABLE_REGISTRATION=true, and the initial install wizard still lets you create the administrator account normally — registration is closed to the public, not to you (serverkueche's guide). Open https://git.example.com in your browser on first start; Forgejo shows the initial install page with the database and server settings already pre-filled from your environment variables. Expand the administrator section, enter a username, email, and strong password, click Install Forgejo, and after a brief restart you land on your empty dashboard.

Finish the wizard immediately after docker compose up -d: the install page stays open until it is completed once, and an unprotected install page is an open door. One alternative exists if you want to invite collaborators without a bot magnet: leave DISABLE_REGISTRATION=false but set REGISTER_MANUAL_CONFIRM=true, so registrations sit in a queue until you approve them manually (the torbjorn.dev Forgejo deployment notes describe this exact pattern).

How to Create Your First Repository

Creating your first repository takes ten seconds and proves the whole chain works. Click the + at the top right, choose New Repository, give it a name, and check Initialize repository so it starts with a README instead of being empty (serverkueche's guide). After creating it, the repository page shows the HTTPS/SSH toggle with the matching clone addresses — if those addresses look right, your ROOT_URL rule is holding. This is also where migrations live: + → Migration imports an existing repository from GitHub, GitLab, or another Forgejo/Gitea instance, including issues, pull requests, and releases — not just the Git history — so switching your projects over takes minutes without losing anything.

How to Push Over HTTPS with an Access Token

Pushing over HTTPS works immediately, with no key setup. Clone the repo, add a file, commit, and push:

git clone https://git.example.com/your-user/my-first-repo.git
cd my-first-repo
echo "My first note" >> notes.txt
git add notes.txt
git commit -m "First note"
git push

For a private repository, Git asks for a username and password right here — and the access-token rule says: use an access token instead of your account password. Generate one under Settings → Applications → Generate New Token. A token can be revoked individually, scoped narrowly, and it is mandatory once you enable two-factor authentication on your account, because basic-auth passwords stop working then (serverkueche's guide). If this is your first commit on a fresh client and Git complains "Please tell me who you are", set your identity once globally with git config --global user.name "Your Name" and git config --global user.email "your@email" before committing.

How to Push Over SSH

SSH is the more convenient daily driver because it skips the password prompt on every push. Forgejo runs its own built-in SSH server (START_SSH_SERVER=true in the compose), so you never touch your host's hardened SSH access on port 22 — the Forgejo SSH server listens on 2222 in the container and is published to the same host port. Store your public key under Settings → SSH / GPG Keys → Add Key (cat ~/.ssh/id_ed25519.pub shows it), open TCP port 2222 in your firewall, then clone over SSH with the port in the address:

git clone ssh://[email protected]:2222/your-user/my-first-repo.git

The most common SSH failure is the port pair: SSH_PORT (the port shown in clone links) and SSH_LISTEN_PORT (the port the server actually listens on) must both be set to the same value — setting only one of them crashes Forgejo at startup in a bind: address already in use restart loop (serverkueche's troubleshooting notes). Set both to 2222, as the compose above does, and the failure never appears.

Forgejo vs Gitea: Which One Should You Run?

Forgejo is a fork of Gitea, so the interfaces look familiar — but the projects have diverged in governance and release direction. For the full Gitea lane, the Gitea beginner guide covers that side. The short version:

QuestionForgejoGitea
GovernanceCommunity-governed, nonprofit-backedCommercial company behind it
Release lineOwn line on codeberg.org (v17.0.1 current)Own line (separate versioning)
CI/CDForgejo Actions, GitHub-Actions-compatibleGitea Actions, same lineage
Image registrycodeberg.org/forgejo/forgejodocker.io/gitea/gitea
DirectionFederation via ActivityPub on the roadmapFocused on the core forge

Run Forgejo if you want the community-governed option with its own release line and the federation direction; run Gitea if you prefer its ecosystem. They are the same product lane — never set up both for the same team.

How to Run Forgejo Actions

Forgejo Actions is the CI/CD engine that runs your pipelines — and it is compatible with GitHub Actions, so existing .github/workflows files often run unchanged (serverkueche's guide). Actions needs a separate runner that executes the jobs; for security reasons the runner does not belong on the production host. Plan a second small machine (or a second container on a different host) as the runner, register it with a token from Site Administration → Actions → Runners, and point it at your instance. Then a .forgejo/workflows/test.yaml or .github/workflows/test.yaml in any repository runs on every push. The Actions branch is optional for the beginner arc — get repositories, HTTPS, and SSH working first — but it is the reason many teams choose Forgejo over a bare Git server.

How to Put Caddy in Front of Forgejo

Forgejo serves plain HTTP on port 3000, so a reverse proxy gives you real HTTPS — which the ROOT_URL rule requires. The simplest front is Caddy: point your domain at the server, then one block does the rest:

git.example.com {
    reverse_proxy localhost:3000
}

Caddy fetches and renews the Let's Encrypt certificate automatically, so https://git.example.com just works. The Caddy beginner guide walks through the full reverse-proxy setup if you have not configured Caddy before. If you already run Traefik, the equivalent is a router rule with a Let's Encrypt resolver pointed at the container's port 3000, as the serverkueche arc demonstrates — Forgejo itself needs no HTTPS configuration either way, because the proxy terminates TLS and talks plain HTTP to the container.

How to Back Up Forgejo

Everything Forgejo owns lives in the forgejo_data volume (/data): the SQLite database, your repositories, and the SSH server's host keys — so backing up that one volume backs up the whole instance. The cleanest backup uses Forgejo's built-in dump, run as the git user (as root, Forgejo refuses the dump):

docker compose exec -u git forgejo forgejo dump -t /tmp -f /tmp/forgejo-dump.zip
docker compose cp forgejo:/tmp/forgejo-dump.zip ./forgejo-dump.zip

The dump-to-/tmp detail matters: the git account cannot write to /data itself, so dumping there would fail (serverkueche's maintenance notes). Then move the zip off the machine — the Restic beginner guide covers encrypted, automated off-site backups for exactly this job. One consistency warning: a SQLite file is written while the server runs, so either stop the container briefly (docker compose stop) or use the dump above — never copy the live database file raw and call it a backup.

Beginner Mistakes That Break a Forgejo Install

Most first installs fail for one of a handful of reasons. Check this table before debugging:

MistakeSymptomFix
ROOT_URL left as localhost or wrong schemeClone links show localhost or HTTP; pushes fail oddlySet FORGEJO__server__ROOT_URL to the exact public HTTPS URL + trailing slash
Only one of SSH_PORT / SSH_LISTEN_PORT setbind: address already in use restart loopSet both to 2222
Wrong volume ownershipContainer exits on startMatch USER_UID/USER_GID to the volume owner
Registration left openStrangers create accounts (and can run Actions)FORGEJO__service__DISABLE_REGISTRATION=true from the start
Using :latest with unattended pullsMajor-version upgrade breaks the instancePin a minor tag like :17.0; upgrade majors manually
Backing up the live SQLite file rawCorrupt or inconsistent restoresUse forgejo dump or stop the container first

Sources

  • Codeberg — forgejo/forgejo releases API (v17.0.1, v16.0.7, v15.0.11 all published 2026-10-26 — patch releases across three supported lines). Fetched 2026-10-31.
  • Forgejo official documentation — "Installation with Docker" (image codeberg.org/forgejo/forgejo:17, data.forgejo.org mirror, major-tag tracks latest 17.0.x, X→X+1 upgrades need manual human verification, FORGEJO__[SECTION]__[KEY] env format with FORGEJO____APP_NAME for DEFAULT, SQLite default, MySQL/Postgres examples, ./forgejo:/data ownership rule, rootless 17-rootless image with /var/lib/gitea and 222:2222 mapping). Fetched 2026-10-31.
  • serverkueche via DEV Community — "Forgejo: your own Git server behind Traefik" (Forgejo 16.0.3 compose arc: SQLite for small teams, ROOT_URL must be exactly the public HTTPS address, DISABLE_REGISTRATION from the start with admin via wizard, built-in SSH server with SSH_PORT + SSH_LISTEN_PORT both 2222, HTTPS clone + access token under Settings → Applications mandatory with 2FA, SSH key under Settings → SSH/GPG keys, ssh://git@domain:2222/user/repo.git, Actions compatible with GitHub Actions needing a separate runner, + → Migration imports GitHub/GitLab incl. issues/PRs/releases, forgejo dump as git user, /api/healthz check). Fetched 2026-10-31.
  • selfhosting.sh — "Self-Host Forgejo" (community-governed, forked from Gitea 2022, repos/PRs/issues/Actions/package registries/federation via ActivityPub, Postgres compose example, openssl rand -base64 32 passwords, FORGEJO__mailer__* envs). Crawled 2026-10-31.
  • Sliplane — "Self-hosting Forgejo the easy way" (one-click arc: codeberg.org/forgejo/forgejo:15, FORGEJO__service__DISABLE_REGISTRATION=true recommended after the admin exists, FORGEJO__[section]__[key] format, migrations automatic on startup). Crawled 2026-10-31.
  • torbjorn.dev — "Moving to Forgejo on Hetzner" (service-user arc: secrets via FORGEJO__security__SECRET_KEY/__security__INTERNAL_TOKEN/__oauth2__JWT_SECRET env vars, alternative DISABLE_REGISTRATION=false + REGISTER_MANUAL_CONFIRM=true admin-approval queue). Crawled 2026-10-31.
  • Vultr Docs — "How to Deploy Forgejo – Lightweight Git Server" (Ubuntu 24.04 + Compose + Traefik deploy guide). Crawled 2026-10-31.

Frequently Asked Questions

Is Forgejo free to use?

Yes — Forgejo is free and open-source, community-governed, with no paid tier or hosted upsell in the project itself. You pay only for the server you run it on, which can be the smallest VPS available since Forgejo is deliberately light.

What is the difference between Forgejo and Gitea?

Forgejo is the 2022 community fork of Gitea with its own governance and release line (currently v17.0.1 on codeberg.org). The interfaces are nearly identical, but the projects version and steer independently — pick one per team and never run both for the same repositories.

Can I run Forgejo without Docker?

Yes. The official docs cover binary, Podman (including quadlet units), and other installs. This guide uses Docker Compose because it is the shortest path to a working beginner install with a single command.

Why do my Forgejo clone links show the wrong address?

That is always the ROOT_URL rule: FORGEJO__server__ROOT_URL must be exactly your public HTTPS address with a trailing slash. Fix it, check SSH_DOMAIN and SSH_PORT too, and restart the container.

How do Forgejo Actions compare to GitHub Actions?

Forgejo Actions is compatible with GitHub Actions — existing .github/workflows files often run unchanged. The one architectural rule: the runner is a separate machine or container, never the production host.

Featured on Twelve Tools Launch Llama Tools Launch your MCP