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 → 3000HTTP (local network access bypassing the tunnel)2222 → 2222SSH (also via the tunnel as:22)
What lives where
| URL | Service |
|---|---|
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>.git | SSH 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) → commentsAuth 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