Deployment and Scaling

Updated

October 30, 2026

Deployment and Scaling

After this page you can sketch a Forgejo or Gitea layout and name the first limit you will hit.

One directory is the whole server

The container’s data directory holds the bare repositories, the database, and the config. On the standard image that directory is /data. SQLite, when you leave the database unset, is a file in that tree (gitea.db, including on Forgejo: the filename stayed). Config is app.ini under the same tree.

./data/                      (bind mount → /data)
  git/repositories/          bare repos
  gitea/gitea.db             SQLite, until you move it
  gitea/conf/app.ini         generated on first start

Environment variables of the form FORGEJO__section__KEY write app.ini for you. Gitea uses the same pattern with the prefix GITEA__. A value already stored in app.ini is not cleared by unsetting the variable. Edit the file, or start from an empty data directory.

Two images ship:

Image Data mount SSH inside the container
codeberg.org/forgejo/forgejo:15 /data port 22
codeberg.org/forgejo/forgejo:15-rootless /var/lib/gitea port 2222

The lab uses the first. Rootless is the same product with a different mount and a different internal SSH port. Mirror data.forgejo.org when codeberg.org is not reachable from the build machine.

Where it stops being small

SQLite has one writer. A desk, a class, or a company of tens of people is the design center. A busy public forge is not. The move is mechanical:

  1. Keep Git bytes on local disk. NFS for the repository directory is a corruption risk unless you already know that filesystem’s locking story. Chapter 36 is the storage chapter.
  2. Move the database to Postgres when SQLite lock time shows up in the logs. Set FORGEJO__database__DB_TYPE=postgres and a host. Do this before the file is large, and take a backup first (chapter 37).
  3. Put LFS and package blobs on object storage when the data disk fills with artifacts, not when the Git packs do.
  4. Scale CI by adding runners, not by cloning the Forgejo process. Actions are optional. The server is still one writer of its own database.

There is no supported active-active pair of Forgejo nodes over one SQLite file. High availability here means backups, a second disk, and a practiced restore. GitLab’s multi-node shape is a different product.

Upgrades

Pin an image tag (:15 or :15.0), not latest. Moving from major N to N+1 is a manual upgrade with a backup of /data in front of it. Patch releases inside 15.0 replace the container and keep the directory.

Gitea’s image is docker.io/gitea/gitea:1.27. Same directory layout, same port story, GITEA__ environment names. Do not run both compose projects against one data directory.

The hands-on page brings Forgejo up, pushes desk-api, then shows the Gitea file as the same lab with the image line changed.