Forgejo & PR-Agent

Forgejo & PR-Agent

Forgejo is the self-hosted Git forge — same host as the Kubernetes cluster, but deliberately outside Kubernetes, running as a Docker Compose stack. Keeping it out-of-cluster means Forgejo is still reachable if the cluster is down (the bootstrap chicken-and-egg problem).

The stack

# ~/homelab-git/docker-compose.yml (abridged)
services:
  forgejo:       { image: codeberg.org/forgejo/forgejo:9-rootless }
  forgejo-db:    { image: postgres:16-alpine }
  forgejo-runner:{ image: code.forgejo.org/forgejo/runner:6.2.1 }
  cloudflared:   { image: cloudflare/cloudflared:latest }
  pr-agent:      { image: codiumai/pr-agent:latest }

All share the forgejo-net bridge. The host exposes:

  • 3030 → 3000 HTTP (local network access bypassing the tunnel)
  • 2222 → 2222 SSH (also via the tunnel as :22)

What lives where

URLService
https://git.skypalette.ai/Forgejo web UI
https://git.skypalette.ai/api/v1/...Forgejo REST API
https://git.skypalette.ai/v2/OCI container registry
https://git.skypalette.ai/api/packages/<owner>/{pypi,npm,generic,...}Package registries
[email protected]:<owner>/<repo>.gitSSH git access (port 2222 on host)

Actions runner

The runner is a separate container that mounts the Docker socket so workflows can build container images. Forgejo Actions reads .github/workflows/ directly (GitHub-compatible). Migration to native .forgejo/workflows/ is deferred until it stops being optional.

Workflows in this repo (the one serving this site) build the static site, build a container image, and push to git.skypalette.ai/v2/. See Hosting for the exact pipeline.

PR-Agent (Qodo Merge)

A Forgejo bot user named pr-agent posts AI-generated code reviews on new PRs. The pr-agent container listens on 127.0.0.1:3001:3000 and Forgejo webhooks the PR’s issue_comment events at it.

PR opened


Forgejo webhook → http://pr-agent:3000/webhook


pr-agent reads the diff via Forgejo API → Anthropic API (Opus) → comments

Auth uses ANTHROPIC_API_KEY from the compose env. The bot’s Forgejo token is scoped to comment-only on the relevant org’s PRs.

A small shim is needed at startup to handle Forgejo’s issue_comment payload shape — PR-Agent was originally written against GitHub’s schema and Forgejo’s differs in a few pull_request.head fields. The shim is checked into ~/homelab-git/pr-agent-shim.py.

Why Forgejo and not GitHub

  • Container registry colocated with the source (no Docker Hub rate limits, no GHCR auth juggling).
  • AI review bot runs in your network, with your prompt, against your Anthropic billing.
  • Webhook latency is local — PR comments appear in ~3 seconds vs ~15+ with external services.
  • It costs the watts of a Docker container.

Bootstrap caveat

If Forgejo is down, ArgoCD can’t pull. If ArgoCD can’t pull, Forgejo itself can’t be redeployed via GitOps. That’s why the Forgejo docker-compose lives in ~/homelab-git/ (with backups, see Backup & DR) and is never managed by ArgoCD.

The chain in order of dependency:

host OS → docker → forgejo-db + forgejo + cloudflared (Forgejo's)


                   kubeadm + kubelet + containerd


                   argocd (helm chart, manually applied once)


                   everything else