Self-Hosted Git
Hosting your own Git server with Gitea or Forgejo - the missing foundation under every "commit it to a repo" instruction from here on, plus CI runners for the GitOps page.
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
latesttag - pin at least the major version (:11above) since upgrades across major versions need manual steps; check the current release on Forgejo's release page before deploying. - Port
2222:22maps 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.gitfrom 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.