Every page from here on assumes "a Git repository" the way earlier tiers assumed "a Docker host" - Infrastructure as Code, Configuration Management, and GitOps for Homelab all say "commit it to your repo" without saying where that repo actually lives. This page is that missing piece: hosting Git yourself instead of defaulting to github.com.

Why self-host this at all

github.com works fine, and plenty of homelabbers use it for exactly this. The case for running your own:

  • Nothing about your infrastructure has to touch the public internet - your compose files, Terraform state references, and Ansible inventories describe your network layout even when secrets themselves are kept out (see Secrets Management). Keeping that off a third party's servers is a reasonable privacy default, not paranoia.
  • GitOps webhooks work without exposing anything - GitOps for Homelab noted that a webhook needs your Git host to reach your GitOps controller. A repo on your own LAN can reach Portainer directly; a repo on github.com needs your homelab reachable from the internet, or you fall back to polling.
  • It's the same skill as everything else in this tier - running one more small, well-documented service, which by this point is a Tuesday.

The honest counterpoint: it's one more thing to back up and keep patched, and if it goes down, so does your ability to deploy anything GitOps-driven until it's back. Mirroring to github.com as a secondary remote is a reasonable belt-and-suspenders option if that risk bothers you.

Gitea or Forgejo

  • Gitea - the longer-established project, single self-contained binary (or container), lightweight, huge community.
  • Forgejo - a community-governed fork of Gitea, created in 2022 over concerns about Gitea's governance moving toward a for-profit model. Functionally near-identical, and migration between the two is straightforward - the same relationship as Terraform/OpenTofu from Infrastructure as Code, down to "pick either, they both work, one has no corporate-license risk attached."

Either is a fine choice; this page uses Forgejo in examples for that reason, but everything below applies to Gitea with only the image name changed.

Running it

services:
  forgejo:
    image: codeberg.org/forgejo/forgejo:11
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - FORGEJO__database__DB_TYPE=postgres
      - FORGEJO__database__HOST=forgejo-db:5432
      - FORGEJO__database__NAME=forgejo
      - FORGEJO__database__PASSWD=${DB_PASSWORD}
    volumes:
      - ./data:/data
    ports:
      - "3006:3000"
      - "2222:22"
    depends_on:
      - forgejo-db
    restart: unless-stopped

  forgejo-db:
    image: postgres:16
    container_name: forgejo-db
    environment:
      - POSTGRES_USER=forgejo
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=forgejo
    volumes:
      - ./db-data:/var/lib/postgresql/data
    restart: unless-stopped
  • Forgejo doesn't publish a latest tag - pin at least the major version (:11 above) since upgrades across major versions need manual steps; check the current release on Forgejo's release page before deploying.
  • Port 2222:22 maps host port 2222 to the container's SSH server, since your Proxmox guest's own port 22 is presumably already your normal SSH login - git clone ssh://git@server:2222/you/repo.git from then on.
  • This runs comfortably in an LXC container (see Proxmox VMs and Containers) rather than a full VM - it has no special hardware needs, unlike Home Assistant's dongle passthrough.

Resource expectations: light for a solo homelab's worth of repos - well under 1GB RAM idle, similar to Vaultwarden's footprint.

Built-in CI: Actions runners

Both Gitea and Forgejo ship an Actions system compatible with a useful subset of GitHub Actions workflow syntax - a .forgejo/workflows/build.yml (or .gitea/workflows/) in a repo can build a container image on push, which is the natural upstream half of the GitOps pipeline from GitOps for Homelab: push code, Actions builds and pushes the image, GitOps notices the new tag and deploys it. A runner is a separate container you register against your instance - not detailed here, since whether you need it depends entirely on whether you're building your own images or just deploying pre-built ones from upstream projects (the more common homelab case, and one that doesn't need this at all).

Backing it up

Same shape as everything else: the Postgres database and the ./data folder (which holds repo contents, issues, and Actions run history). Treat this with the same seriousness as your IaC and compose repos generally, since by this tier it likely is the canonical copy of both - see Proxmox Backups.

⚠️ Risk: if you do open SSH or the web UI to the internet (for pushing from outside your LAN, say), put it behind the same VPN-first posture as everything else - see Remote Access - rather than forwarding either port directly.

Next: Infrastructure as Code.