Files
dev-notes/content/soloforge-docs.md
2025-12-05 15:32:53 -06:00

7.5 KiB
Raw Blame History

title, section, summary, tags, nav
title section summary tags nav
SoloForge Documentation docs SoloForge Infrastructure Documentation for Self-Hosted Gitea with Actions Runner on Hetzner
servers
infrasctructure
gitea
ci/cd
hetzner
docker
1

Self-Hosted Gitea + Actions Runner (Hetzner Deployment)

This document describes the current SoloForge setup — Gitea, Traefik routing, the Gitea Actions runner, backup routines, directory layout, and operational notes.

This repo exists so future-me (and actual-me) dont need to reverse-engineer anything when something eventually explodes.

1. Overview

SoloForge is a self-hosted Gitea instance running on a Hetzner VM.

It provides:

  • Private (but login-protected) Git hosting
  • Public-visible repositories (instance auth is required anyway)
  • Gitea Actions, backed by a Docker-based self-hosted runner
  • Automatic CI for TODO-to-issue sync and other workflows
  • Reverse-proxy via Traefik
  • Automated Gitea backups

The system replaces the original Proxmox-hosted Gitea instance lost due to disk failure.


2. System Layout

Hetzner VM

  • Debian 13
  • Docker + Docker Compose installed
  • Traefik reverse proxy (existing before SoloForge migration)
  • HTTPS termination handled by Traefik via Lets Encrypt

Directory structure

/gitea/
├── backups/               # Gitea nightly backups
├── gitea/                 # Gitea app + persistent data
├── postgres/              # PostgreSQL data directory (if using Postgres)
└── docker-compose.yml     # Main Gitea stack

Runner lives separately

/gitea/gitea-runner/
├── docker-compose.yml     # Actions runner stack
└── data/                  # Contains .runner registration + job cache

3. Gitea Deployment

3.1 Gitea Compose Service

Gitea is launched via Docker Compose and reverse-proxied through Traefik. Data lives under /gitea/gitea to ensure persistence.

3.2 Traefik Routing

Traefik handles:

  • HTTPS certificate generation
  • Routing git.keithsolomon.net → Gitea web UI
  • Exposing SSH port (222) for git-over-SSH

No YAML generator required anymore — everything is stable and hand-maintained.


4. Gitea Actions Runner

SoloForge uses a self-hosted Gitea Actions runner, running via Docker and capable of executing JavaScript (Node-based) GitHub-style actions.

4.1 Runner compose file

Located at:

/gitea/gitea-runner/docker-compose.yml

Core configuration:

environment:
  GITEA_INSTANCE_URL: "https://git.keithsolomon.net"
  GITEA_RUNNER_REGISTRATION_TOKEN: "<token>"
  GITEA_RUNNER_NAME: "hetzner-runner-1"
  GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:20-bullseye,self-hosted,linux,x86_64,docker"

By default, GitHub runners provide Node.js preinstalled. Self-hosted runners do NOT.

Mapping:

ubuntu-latest:docker://node:20-bullseye

ensures any workflow using:

runs-on: ubuntu-latest

runs inside a Node-enabled container, fixing "node: command not found" errors.

4.3 Re-registering the runner (important!)

If labels change or the runner breaks:

cd /gitea/gitea-runner
docker compose down
rm -f data/.runner     # Forces new registration
docker compose up -d   # Registers with current labels

Check runner status in Gitea:

Site Admin → Actions → Runners


5. Workflows

5.1 TODO-to-Issue Sync

Certain repos use a custom JavaScript action to:

  • Parse TODO comments
  • Generate/close GitHub-style issues inside Gitea

These workflows run cleanly now because:

  • The runner supports Node (ubuntu-latest → node:20 container)
  • Repository permissions allow issue writing

5.2 Secret tokens

Unlike GitHub, Gitea does not auto-inject GITHUB_TOKEN. Workflows requiring an auth token need one defined manually in:

Repo → Settings → Secrets

Example:

GITHUB_TOKEN = <personal access token>

(Or rename to something more Gitea-themed.)


6. Repository Management

6.1 Bulk import

All repos were migrated using a custom bulk-mirror script that:

  • Created missing repos via the Gitea API
  • Pushed full history via git push --all and --tags

6.2 Public visibility

All repos are public (since Gitea login protects everything). A bulk-update script is available to flip visibility via API if needed.


7. Backups

Gitea supports built-in dumps via:

gitea dump

A cronjob is installed to dump nightly at 3am:

/gitea/backups/
└── gitea-dump-YYYYMMDD.zip

Recommended: sync this folder offsite or back to home lab.


8. Restore Notes

If Gitea must be restored from dump:

docker compose down
rm -rf gitea/* postgres/*
unzip gitea-dump.zip into /gitea/gitea
docker compose up -d

If the runner needs re-registration, follow section 4.3.


9. Future Improvements (Optional)

  • Mirror “source of truth” repos between GitHub ↔ Gitea
  • Add automated org-level secrets
  • Configure multiple runners (home lab, Hetzner, etc.)
  • Add Prometheus metrics + Grafana board for CI activity
  • Set up Giteas dependency listing or vulnerability scanning

10. CLI Toolkit

forge Tool

A custom CLI tool to help manage common tasks:

Command Description
forge status Show status of Gitea and runner containers
forge ps Alias for status
forge gitea-logs Tail logs from Gitea container
forge runner-logs Tail logs from Actions runner container
forge backup Run a Gitea dump and move it into BACKUP_DIR
forge restore-test Run backup restore sanity checks (unzip + extract)
forge restart-gitea Restart Gitea stack
forge restart-runner Restart Actions runner stack
forge runner-reset Re-register runner with current labels (destroys .runner)
forge diag Quick diagnostic summary

forge-alert Tool

A companion script that serves as a basic reusable alert sender, capable of logging to syslog and sending notifications via Telegram. Telegram bot token and chat ID must be set in environment variables by editing /etc/environment in a root/sudo shell.

forge-b2-backup Tool

A backup script that uploads Gitea dumps to Backblaze B2 cloud storage. Utilizes rclone, so make sure to configure an appropriate remote (B2 by default) before use. Alerts via forge-alert.sh for backup status.

forge-restore-test Tool

A restore test script that performs basic integrity checks on the latest Gitea dump file, including unzip testing and extraction smoke test. Alerts via forge-alert.sh if any issues are detected.


TL;DR Cheat Sheet

Runner broke?

→ delete data/.runner, docker compose up -d

node not found?

→ ensure ubuntu-latest label is mapped to node:20-bullseye

Release workflows failing?

→ they're GitHub-only; they run on GitHub mirrors

Backup?

→ see /gitea/backups, nightly gitea dump

Repo not found?

→ bulk import script: auto-create + push mirror