Skip to content

Forgejo

Lifecycle: Current. Last verified: 2026-07-17.

Service record

Field Value
Purpose Git hosting and Forgejo Actions control plane
Owner IT Operations
Criticality / recovery priority High / P3
Host/runtime CT 101, rootless Podman as podsvc
Image codeberg.org/forgejo/forgejo:11
Web Caddy route git.kh3group.com; backend 192.168.2.20:30080
SSH Container 22, CT host 2222
Network kh3-backend
Data /opt/podman/volumes/forgejo/data
Env /opt/podman/env/forgejo.env, mode 0600; never print
Quadlet /home/podsvc/.config/containers/systemd/forgejo.container
Database PostgreSQL database/role forgejo

The runner is a separate rootless Quadlet with image code.forgejo.org/forgejo/runner:6.3.1, control plane http://forgejo:3000, data under /opt/podman/volumes/forgejo/runner-data, and allowed bind roots for runner-data and runner-ssh.

Dependencies and access

Forgejo depends on CT 101, rootless Podman, PostgreSQL, and kh3-backend. Web access depends on Technitium and CT 103. SSH clients inside the DMZ use [email protected] -p 2222; CT host port 22 is not Forgejo.

Forgejo repository runner list

Forgejo's repository runner list. The upstream example uses a docker label; the KH3 documentation runner must be online with the mkdocs label.

Reconstruct and configure

Use Rootless Podman Restore for the historical database/archive import, then reconcile the live-only runner and publishing Quadlets. Required secret names include database password and OAuth/OIDC client secret; values belong in restricted env/config storage.

Forgejo's advertised SSH port must match the externally reachable port. Container SSH remains 22, while clone URLs normally need 2222 unless an intermediate proxy supplies another port.

The runner daemon must start with forgejo-runner daemon --config /config.yml. Its redacted configuration shape is:

container:
  network: kh3-backend
  options: >-
    -v /opt/podman/volumes/forgejo/runner-ssh:/runner-ssh:ro
    --dns 192.168.2.2
    --add-host git.kh3group.com:192.168.2.3
  valid_volumes:
    - /opt/podman/volumes/forgejo/runner-data
    - /opt/podman/volumes/forgejo/runner-ssh

The corresponding host mounts are /opt/podman/volumes/forgejo/runner-data:/data for runner state, /opt/podman/volumes/forgejo/runner-data/config.yml:/config.yml:ro for configuration, and the two allow-listed roots for job containers. Never publish an unrestricted host path through valid_volumes.

Operate

ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "podman ps --filter name=forgejo --filter name=runner"'
ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "podman logs --tail=200 forgejo"'
ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "podman logs --tail=200 runner"'
ssh pvessh 'curl -sS -o /dev/null -w "%{http_code}\n" \
  http://192.168.2.20:30080/'

Expected: containers running and web returns 200, 30x, or an intentional authentication response.

Run Forgejo CLI commands in the container as user git, never root:

ssh pvessh 'pct exec 101 -- runuser -l podsvc -c \
  "podman exec -u git forgejo forgejo --config /data/gitea/conf/app.ini --version"'

Upgrade

  1. Read Forgejo release notes and database compatibility.
  2. Back up data, database, env/config, runner registration/config, and SSH host keys.
  3. Pin the tested image.
  4. Pull and restart only Forgejo in an approved window.
  5. Validate database migration, web, HTTPS Git, SSH on 2222, OAuth, and one Actions workflow.
  6. Roll back image and data/database together if migration is not reversible.

Backup and restore

Back up /data, PostgreSQL database/roles, runner config/registration, and deployment units. Restore database and data from the same recovery point. Forgejo's /data/git/.ssh/authorized_keys is generated; after restore, compare database key records and regenerate as git if stale.

Security and monitoring

  • Keep OAuth secrets and job tokens out of logs.
  • Restrict runner bind roots.
  • Review runner jobs before exposing new host mounts.
  • Monitor web health, database health, runner online state, disk usage, and a scheduled test workflow.

Troubleshooting

Symptom Diagnosis Remedy
Web 502 Test 30080, Forgejo unit, PostgreSQL Repair failed dependency
SSH permission denied Compare client fingerprint, DB ownership, generated authorized_keys Regenerate keys as container user git; do not duplicate a globally unique key
Clone URL lacks :2222 Check selected server SSH settings Correct advertised port in an approved change
Runner offline Unit/log/config path/control-plane URL Use http://forgejo:3000 and explicit --config /config.yml
Job bind ignored Check container.valid_volumes Add only reviewed bind root and restart runner with approval
Job stuck running without task container Check runner logs and task containers Cancel/re-run in UI; do not edit Actions DB

Official references