# Forgejo 16.0.3 self-hosting walkthrough pairs built-in SSH with Traefik TLS

A community tutorial documents running the Gitea fork as a single SQLite-backed container behind Traefik, including the SSH_PORT/SSH_LISTEN_PORT mismatch that causes restart loops and a dump-based backup routine.

Canonical URL: https://freelancenews.online/news/forgejo-16-0-3-self-hosting-walkthrough-pairs-built-in-ssh-with-9d6fe1b0
Published: 2026-09-25T19:18:20.791Z
Updated: 2026-09-25T19:18:20.791Z
Source published: 2026-09-23T18:31:49.000Z
Event date: Not established
Review status: source-reviewed
Review method: Automated comparison against retrieved source text; not independent fact-checking.

## Report

A community write-up on dev.to describes running Forgejo 16.0.3 as a self-hosted Git service in a single Docker container behind the Traefik reverse proxy, with SQLite as the database and Traefik handling HTTPS certificates. The post is a tutorial rather than a vendor announcement, so the version number, configuration values and failure modes below are the author's claims, not independently verified facts.

The stated goal is a Git platform that fits a small VPS while still offering issues, pull requests, a wiki and CI. The author positions Forgejo as the community fork of Gitea and says it is gaining ground in self-hosting circles, and frames the motivation as data sovereignty: repositories, issues and project history stay on the operator's own server rather than an external provider's terms, rate limits or acquisition risk.

The documented stack is deliberately minimal. Forgejo runs as one container, SQLite replaces a separate database container, and Traefik terminates TLS with a Let's Encrypt certificate. The author lists prerequisites as a Debian 13 server running Docker (tested on a netcup VPS), an existing Traefik proxy network and resolver, and a domain pointed at the server via A/AAAA records.

Configuration is done through environment variables using Forgejo's double-underscore convention, where the section and key are joined by two underscores. The author stresses that ROOT_URL must exactly match the public HTTPS address, otherwise clone links and redirects break. Registration is closed from the start with DISABLE_REGISTRATION set to true, and the admin account is created through the initial install wizard instead.

Git over SSH is handled by Forgejo's own built-in SSH server rather than the host's SSH daemon, so the host's port 22 configuration is left untouched. The container listens on 2222 and that port is published to the same host port. The author flags a specific trap: SSH_PORT, which appears in clone links, and SSH_LISTEN_PORT, which the server actually binds, must both be set to the same value. Setting only one causes a startup crash with an address-already-in-use error and a restart loop.

Web traffic never touches a published port. The author notes that Traefik is told the web interface listens internally on 3000 via the load balancer server port label, and that only SSH is exposed directly. The container's declared 22/tcp is described as a port the image declares rather than one that is published, so the only externally reachable port is the mapped 2222.

The post includes a healthcheck tuned for fast startup feedback. By default Docker waits a full interval before the first check, so the container can look unhealthy for 30 seconds or more even when the application is ready in about two seconds. The author sets a two-second start interval during the startup phase, which requires Docker 25 or newer and Compose v2.20 or newer, both of which the author says are available on Debian 13.

Verification is done by requesting the health endpoint over HTTPS. Before the install wizard is completed, the author says the endpoint returns a status of pass with the slogan "Forgejo: Beyond coding. We forge." After the wizard, the same URL lists individual cache and database checks and shows the instance name instead of the slogan.

The tutorial walks through creating a repository with an initialized README, then cloning over HTTPS and over SSH. For HTTPS the author recommends an access token from the applications settings instead of a password, noting it can be revoked individually and becomes mandatory once two-factor authentication is enabled. For SSH, the public key is added under the SSH/GPG keys settings and the clone URL uses port 2222, which must be opened in both the host firewall and any provider firewall.

Issues and pull requests, complete with labels, milestones and assignments, are among the features the author enumerates in addition to hosting. Forgejo Actions is described as a CI/CD engine that works with GitHub Actions workflows. The list continues with wikis and releases, a package registry covering container images, npm, Maven and PyPI, and organizations offering per-team access control. A migration path is also mentioned by the author: repositories can be imported not only as Git history but together with their issues, pull requests and releases, whether they come from GitHub, GitLab, or a different Forgejo/Gitea instance.

On Actions, the author is explicit about a limitation: jobs need a separate runner, and for security reasons that runner does not belong on the production host. The post points to a separate tutorial for that setup rather than covering it here, so the CI/CD capability is described but not demonstrated end to end in this piece.

The troubleshooting section reads as the most practically useful part. A 502 from Traefik is attributed almost always to the wrong internal port, since the web interface listens on 3000. Clone links showing localhost or the wrong port point to incorrect ROOT_URL, SSH_DOMAIN or SSH_PORT values. An SSH clone failing with a public-key permission error means the key is not stored on the account yet, or the port was omitted.

Backups target the single data volume, which holds the SQLite database, repositories and the SSH host keys. Because SQLite is written during operation, the author recommends either briefly stopping the container or using Forgejo's built-in dump command. The dump must run as the git user, since Forgejo refuses it as root, and must write to a path outside the data directory because the git account cannot write to the volume root. The resulting zip is copied out of the container and contains the database dump, the app.ini and repository data.

Updates are handled by changing the image tag and pulling. The author advises pinning a fixed tag rather than latest, reading release notes before a major jump for migration hints, and taking a fresh backup before every update. The stated rationale is that data lives in the volume, so repositories and accounts survive the upgrade.

The security guidance is short and concrete: disable open registration, enable two-factor authentication for the admin account, use a normal account rather than the administrator for daily work, keep the software current because releases also carry security fixes, and publish only the ports actually needed, which the author narrows to 443 for Traefik and 2222 for Git over SSH.

For freelancers and small studios, the appeal is a single-container, single-volume service that can be backed up as one artifact and restored from a zip, with no separate database to administer. The tradeoff the author implies is operational ownership: certificate renewal, firewall rules, update cadence and backup verification all become the operator's responsibility, and the post's own troubleshooting list shows how configuration mismatches surface as outages rather than warnings.

Several things remain unverified or unstated. The version number, the claim that Forgejo is rising in the self-hosting scene, and the assertion that GitHub Actions workflow files often run unchanged are the author's assertions. No pricing, hardware sizing beyond "smallest VPS," performance measurements or independent security review are provided, and the Actions runner setup is deferred to a separate article. The post also does not test or claim multi-user load, migration fidelity at scale, or restore procedures beyond producing the dump archive.

The practical takeaway is that the configuration details matter more than the container itself. The two values that most often break a first deployment, according to the author, are the paired SSH port settings and the internal port Traefik must target, and the backup routine depends on running the dump as the correct user and writing outside the data volume. Treat the walkthrough as a starting configuration to test on a disposable host before trusting it with production repositories.

## Key points

- The author documents Forgejo 16.0.3 as a single Docker container with SQLite behind Traefik, which handles Let's Encrypt certificates, on a Debian 13 host.
- Forgejo's built-in SSH server listens on 2222; the author warns that SSH_PORT and SSH_LISTEN_PORT must match or the container crash-loops with an address-already-in-use error.
- Web access is routed only through Traefik to internal port 3000, while 2222 is the sole directly published port.
- Backups use Forgejo's dump command run as the git user and written outside the data volume, or a brief container stop, because SQLite is written during operation.
- Forgejo Actions requires a separate runner that the author says should not run on the production host, and that setup is deferred to another tutorial.

## Practical implications — editorial interpretation

For freelancers and small teams, this is a documented path to owning Git hosting with one container and one backup artifact, but it shifts certificate renewal, firewall configuration, update cadence and backup verification onto the operator. The author's troubleshooting list suggests the highest-risk steps are the paired SSH port settings and the internal port Traefik targets, so those should be validated on a test host before migrating real repositories.

## Limitations and unknowns

This is a community tutorial, so version numbers, the claim that Forgejo is rising in self-hosting circles, and the assertion that GitHub Actions workflows often run unchanged are author claims rather than verified facts. No pricing, hardware sizing beyond "smallest VPS," performance data or independent security review is supplied. The Actions runner setup is deferred to a separate article, and the post does not test multi-user load, large-scale migration fidelity or restore procedures beyond producing the dump archive.

## Sources

- [1] dev.to: Forgejo: your own Git server behind Traefik
  https://dev.to/serverkueche/forgejo-your-own-git-server-behind-traefik-31o0
  Retrieved: 2026-09-25T19:18:03.923Z

## Claim references

- The author documents Forgejo 16.0.3 running as a single container behind Traefik with an automatic Let's Encrypt certificate and SQLite as the database. [source 1]
- The author states that SSH_PORT and SSH_LISTEN_PORT must both be set to the same value, otherwise Forgejo crashes at startup with an address-already-in-use error and restart loop. [source 1]
- The author says the web interface listens internally on port 3000 and that only SSH port 2222 is published directly, with web access going exclusively through Traefik. [source 1]
- The author recommends backing up the data volume either by briefly stopping the container or by running Forgejo's dump command as the git user, writing outside the data directory. [source 1]
- The author states that Forgejo Actions needs a separate runner that, for security reasons, does not belong on the production host, and points to a separate tutorial. [source 1]
- The author advises pinning a fixed image tag instead of latest, reviewing release notes before a major jump, and taking a fresh backup before every update. [source 1]
