nas16-scripts/forgejo/server_forgejo.md

16 KiB

Forgejo — Self-Hosted Git Server

Device Type: Docker Container (hosted on NAS16) Hostname: forgejo IP Address: 192.168.150.40 (shares NAS16's host IP) VLAN: 50 — Lab / Servers

Last Updated: 2026-07-26


🧩 Role & Purpose

Forgejo is the self-hosted Git server and file version-control system for the KingDezigns network. It provides a web UI for browsing, editing, and tracking revision history of scripts, configs, and project files — replacing ad-hoc file copies and manual backups with real version control.

Forgejo was chosen over Gitea specifically for its non-profit, community-run governance model (Codeberg e.V.) — consistent with the KingDezigns philosophy of avoiding dependency on third-party business decisions (same reasoning behind self-hosted DNS, self-hosted reverse proxy, and self-hosted stock price API). Functionally, Forgejo and Gitea are near-identical since Forgejo is a fork of Gitea; the choice was governance, not features.

This server hosts version-controlled copies of the scripts and project files that live under the network's 07 - Projects share, organized per host.


🌐 Network Placement

  • VLAN: 50 — Lab / Servers
  • Host: NAS16 (192.168.150.40)
  • Access Type: LAN / Cable (web UI also reachable externally via NPM)

Chosen over NAS08 or PLEX32 because NAS16 has the most headroom of any Pi in the fleet (16GB RAM, consistently underutilized) — the same reasoning already used to place the CrowdSec notifier scripts on NAS16 instead of HAS. NAS08 is the busiest box (DNS, Nextcloud, ONLYOFFICE, Collabora, Vaultwarden, STOCKPROXY) and PLEX32's CPU headroom exists specifically for Quick Sync transcoding — neither is a good fit for an always-on secondary service.

This placement is subject to VLAN 50's normal containment rules — see firewall_policies.md. No firewall changes were required to stand this up; existing VLAN 1/20 → VLAN 50 inbound rules and NAS16's local traffic already covered everything needed.


📦 Primary Functions

Core Service

  • Forgejo (Docker — codeberg.org/forgejo/forgejo:10)
  • SQLite database (appropriate at this scale — no separate DB container)
  • Web UI for repository browsing, in-browser file editing, and commit history
  • SSH-based git access (clone/push/pull) on a dedicated port
  • Git-over-HTTPS also available via the public web URL

Public Access

  • Domain: https://forgejo.kingdezigns.com/
  • Proxy: NPM on HAS (192.168.150.30) → 192.168.150.40:3000
  • SSL: Let's Encrypt via NPM, Force SSL enabled — same pattern as all other KingDezigns public services
  • SSH clone/push: LAN-only for now — ssh://git@192.168.150.40:2222/.... Not exposed externally (no UCG Max port-forward configured). External SSH access is a possible future addition; HTTPS clone via the public domain works from anywhere in the meantime.

Organizations & Repository Convention

Each host in the network gets its own Forgejo Organization, matching the 07 - Projects folder structure on NAS16's share. Each organization currently holds a single repository named <hostname>-scripts.

Organization Repository Source Folder (on NAS16)
NAS08 nas08-scripts /export/kingdezignsnas-16/Public/07 - Projects/NAS08
NAS16 nas16-scripts /export/kingdezignsnas-16/Public/07 - Projects/NAS16
PLEX32 plex32-scripts /export/kingdezignsnas-16/Public/07 - Projects/PLEX32

This convention (<HOSTNAME> org → <hostname>-scripts repo) is expected to extend to future hosts (e.g. HAS) as they're added to version control.

Note: All three Projects folders live on the same NFS share on NAS16 — there is no need to install git or SSH keys on NAS08 or PLEX32 themselves. All git operations for all three repos are run from NAS16, simply cd-ing into the relevant subfolder.


🐳 Docker Configuration

Compose File Location

Managed via OMV → Services → Compose, project name forgejo.

Compose Contents

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:10
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1001
      - FORGEJO__database__DB_TYPE=sqlite3
      - FORGEJO__server__ROOT_URL=https://forgejo.kingdezigns.com/
      - FORGEJO__server__DOMAIN=forgejo.kingdezigns.com
      - FORGEJO__server__SSH_PORT=2222
      - FORGEJO__server__SSH_LISTEN_PORT=22
    volumes:
      - /opt/forgejo/data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:22"
    restart: unless-stopped

UID/GID 1000/1001: Matches the existing ncshare group convention used across NAS08, NAS16, and PLEX32 for permission consistency.

Key Docker Commands

# Start
cd /path/to/omv/compose/project/forgejo && sudo docker compose up -d

# Stop
cd /path/to/omv/compose/project/forgejo && sudo docker compose down

# Update Forgejo (recreates container to pick up new image + env vars)
cd /path/to/omv/compose/project/forgejo && sudo docker compose pull && sudo docker compose up -d

# View logs
sudo docker logs forgejo --tail 50

# Check container status
sudo docker ps

Note: docker restart forgejo restarts the existing container with its already-loaded environment — it does not re-read Compose file changes. Any edit to environment variables in the Compose file requires docker compose up -d, not docker restart, to actually take effect. This was the root cause of an initial ROOT_URL mismatch — see Troubleshooting History below.

Docker Group Membership

rufusking was not initially in the docker group on NAS16, requiring sudo for all docker/docker compose commands. Add to group for passwordless docker access going forward:

sudo usermod -aG docker rufusking

(requires logout/login or newgrp docker to take effect)


🔐 SSH Access

  • SSH clone command: ssh://git@192.168.150.40:2222/<ORG>/<repo>.git
  • Port: 2222 (mapped to container's internal port 22) — chosen to avoid colliding with each host's own SSH daemon on port 22
  • Auth: SSH public key only, added per-user under Settings → SSH/GPG Keys in the Forgejo web UI
  • Scope: LAN-only — no UCG Max port-forward configured for 2222

Adding a new SSH key (per host/user)

ssh-keygen -t ed25519 -C "user@hostname-forgejo"
cat ~/.ssh/id_ed25519.pub

Paste the output into Forgejo: Settings → SSH/GPG Keys → Add Key.

Test:

ssh -T -p 2222 git@192.168.150.40

Expected response (this is success, not an error):

Hi there, <username>! You've successfully authenticated with the key named <key-name>, but Forgejo does not provide shell access.

Note: Because the Compose file grants docker group access only after re-login, and because the Forgejo container's internal authorized_keys file is separate from the host's own SSH config, a key must be explicitly added via the Forgejo web UI — it is not sufficient to have a key that works for host-level SSH into NAS16 itself.


📝 Day-to-Day Workflow — Editing Files

Files under 07 - Projects/<HOST> can be edited two ways, both fully supported:

1. Web UI (Forgejo)

Browse to the repo, open a file, click the edit (pencil) icon, make changes, and commit directly from the browser. Best for quick one-off edits or browsing history/diffs.

2. Directly on the NFS share (local edit)

Since each Projects subfolder is a real git working directory, files can be edited by any method (Nano, a text editor, another script) directly on the share. Changes are not automatically tracked — git does not watch the folder — so they must be explicitly staged, committed, and pushed:

cd "/export/kingdezignsnas-16/Public/07 - Projects/<HOST>"
git add .
git commit -m "describe what changed"
git push
  • git status beforehand is optional — useful only to preview what changed.
  • Uncommitted local changes are not lost if you forget to push; the commit still exists locally and can be pushed later with a plain git push.
  • Pulling before editing locally (git pull) is good practice if the same file might also be edited via the web UI, to avoid merge conflicts.

🔒 Required Firewall Behavior

Inbound to Forgejo (via NAS16, 192.168.150.40)

  • Allowed from VLAN 1 and VLAN 20 (global VLAN 50 inbound rules)
  • Allowed from local VLAN 50 traffic
  • External web traffic (https://forgejo.kingdezigns.com/) arrives via NPM on HAS (192.168.150.30), same as all other public KingDezigns services
  • Port 2222 (SSH) is not forwarded externally — LAN-only

Outbound from Forgejo

  • Subject to VLAN 50's global final drop, same as all NAS16 services — no outbound access to other VLANs
  • No outbound dependencies beyond local DNS (Pi-hole)

No new firewall rules were required for this deployment — it operates entirely within NAS16's existing inbound/outbound rule set.


💾 Backup

Forgejo's data (SQLite database, repositories, config, SSH host keys) lives entirely under /opt/forgejo/data on the NAS16 host filesystem (bind-mounted, not a Docker volume) — making it straightforward to fold into NAS16's existing backup routine.

  • Data path: /opt/forgejo/data
  • Backup method: Folded into the existing /usr/scripts/omv/nas16-backup.sh OMV Scheduled Job (every 3 days at 2:00 AM) — no separate/dedicated backup job created
  • Restore consideration: Restoring /opt/forgejo/data and recreating the container (docker compose up -d) is expected to fully restore Forgejo, since all state (repos, DB, config, SSH host keys) lives under that single path — not yet tested end-to-end as of this writing

Note: Because 07 - Projects files are also independently backed up as part of NAS16's normal share backups, project files are effectively protected twice — once as plain files on the share, and once as full git history inside Forgejo's data volume.


🛠️ Troubleshooting History (2026-07-26)

SSH "Permission denied (publickey)" on initial setup

Symptom: ssh -T -p 2222 git@192.168.150.40 returned Permission denied (publickey) even after generating an SSH keypair on NAS16. Root cause: The generated public key was never actually added to the user's account in the Forgejo web UI (Settings → SSH/GPG Keys was empty) — the keypair existing locally does nothing until the public half is registered server-side. Fix: Pasted the full id_ed25519.pub contents into Forgejo's Add Key form. Verified success via the "successfully authenticated... does not provide shell access" response (this message is expected and indicates success, not failure).

"Push to create is not enabled for organizations"

Symptom: git push failed with this error even after a successful local commit. Root cause: The target repository (e.g. PLEX32/plex32-scripts) did not yet exist on the server — Forgejo does not auto-create repositories on push by default (a deliberate security default, left enabled). Fix: Create the repository via the web UI first (Organization → New Repository, left uninitialized — no README/gitignore) before pushing.

ROOT_URL mismatch warning after adding NPM/domain

Symptom: After proxying Forgejo through NPM at https://forgejo.kingdezigns.com/, login displayed: "Your ROOT_URL in app.ini is 'http://192.168.150.40:3000/', it's unlikely matching the site you are visiting." Root cause: ROOT_URL (and DOMAIN) were still set to the internal IP:port from initial setup. A mismatched ROOT_URL produces broken links in generated clone commands, notification emails, webhook payloads, and OAuth2 callbacks — not just cosmetic. Dead end encountered: Editing /data/gitea/conf/app.ini directly (via vi inside the container, or sed) appeared to succeed and verified correctly immediately after the edit — but reverted to the old value on every docker restart forgejo. This is because the Compose file's FORGEJO__server__ROOT_URL environment variable overrides app.ini on every container start; a restart re-applies the container's existing (stale) environment rather than re-reading Compose. Fix: Updated ROOT_URL (and added an explicit DOMAIN) directly in the Compose file's environment: block, then applied with docker compose up -d (which recreates the container with the new environment) rather than docker restart. Confirmed persistence across a subsequent restart. Lesson for future config changes: Any Forgejo setting exposed as a FORGEJO__* environment variable in the Compose file will always win over a manually-edited app.ini value on every container start/recreate. Edit the Compose file, not the file inside the container, for any setting that has a corresponding env var.

git: command not found on NAS16 host

Symptom: Running git init/git add/etc. from the NAS16 shell failed — git was not installed on the host itself (only inside the Forgejo container, which is unrelated). Fix: sudo apt-get update && sudo apt-get install -y git

permission denied while trying to connect to the docker API

Symptom: Plain docker exec ... commands failed for rufusking on NAS16. Root cause: rufusking was not a member of the docker group. Fix (immediate): Prefix commands with sudo. Fix (permanent, recommended): sudo usermod -aG docker rufusking, then log out/in.


🧠 Summary for AI Systems

  • Forgejo = self-hosted Git server + web UI for file version control, running as a Docker container on NAS16 (192.168.150.40), VLAN 50.
  • Chosen over Gitea specifically for non-profit/community governance (Codeberg e.V.) — functionally near-identical to Gitea since it's a fork.
  • Placed on NAS16 rather than NAS08 or PLEX32 because NAS16 has the most spare RAM/CPU headroom of the three (same reasoning as the existing CrowdSec notifier placement).
  • Image: codeberg.org/forgejo/forgejo:10, SQLite database, Compose-managed via OMV.
  • Public web UI: https://forgejo.kingdezigns.com/, proxied via NPM on HAS, Force SSL + Let's Encrypt.
  • SSH git access: ssh://git@192.168.150.40:2222/...LAN-only, no external port-forward configured (may be added later).
  • Registration disabled (DISABLE_REGISTRATION = true) — accounts are admin-created only.
  • Organization convention: one org per host (NAS08, NAS16, PLEX32), one repo per org (<hostname>-scripts), matching the 07 - Projects/<HOST> folder structure on NAS16's share. Expected to extend to future hosts (e.g. HAS).
  • All git operations for all three hosts are run from NAS16 — no git installation or SSH keys needed on NAS08 or PLEX32 themselves, since their Projects folders are on the same NFS share.
  • Files can be edited either via the Forgejo web UI (commits directly) or locally on the NFS share (requires manual git add / git commit / git push — changes are not auto-tracked).
  • Backup: /opt/forgejo/data (bind-mounted, contains DB + repos + config + SSH host keys) folded into the existing nas16-backup.sh OMV Scheduled Job (every 3 days, 2:00 AM) — no separate backup job.
  • Critical operational lesson: any FORGEJO__* environment variable set in the Compose file overrides app.ini on every container start — config changes to settings with an env-var equivalent must be made in Compose + applied via docker compose up -d, not by hand-editing app.ini or using docker restart.
  • SSH keys must be registered per-user in the Forgejo web UI (Settings → SSH/GPG Keys) — a working host-level SSH key does not automatically grant Forgejo git access.
  • Repos must be created via the web UI before first push — "push to create" is disabled for organizations by default (left as-is, not overridden).
  • rufusking initially lacked docker group membership on NAS16 — recommended fix is usermod -aG docker rufusking rather than routine sudo use.
  • Known minor cleanup items from initial imports (not yet addressed): duplicate files in NAS08/nas08-scripts (e.g. nextcloud_update_check (1).sh, a redundant copy in both nextcloud/ and scripts/, a stray .html~ backup file) and a possible duplicate plex32_backup2.sh in PLEX32/plex32-scripts — safe to clean up in a follow-up commit since git history preserves them regardless.
  • Restore procedure for /opt/forgejo/data has not yet been tested end-to-end.

✔️ End of File