Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXCVIII · Git Hosting FailureGitHostingFail

Self-hosted Git as fallback — Gitea, GitLab CE

Advanced⏱ ~26 mingit

What you'll learn

  • Distinguish the self-hosted fallback (full forge) from the mirror (read-only) and the local bare (CLI-only)
  • Bootstrap a self-hosted Gitea or GitLab CE instance as the long-term fallback forge
  • Migrate the repositories from the hosted forge to the self-hosted fallback with git clone --mirror and git push --mirror
  • Configure the push-mirror sync that keeps the self-hosted fallback current and the reverse sync that pushes back to the hosted forge when it recovers
  • Identify the operational trade-offs: maintenance burden, security updates, single-region risk, the cost of running a second forge

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

The self-hosted fallback is the long-term answer to a hosted forge outage. Where the mirror (Part XCVIII-03) is read-only and the local bare (Part XCVIII-04) is CLI-only, a self-hosted Gitea or GitLab CE instance is a full forge: web UI, pull-request workflow, branch protection, CI integration, audit trail. The instance is the place the team lands when the hosted forge is down for more than a few hours; it is the recovery path that does not require the team to abandon the production discipline of the pull-request workflow.

flowchart LR
    A["hosted forge\nGitHub / GitLab.com"] --> B["push-mirror sync\nwhen hosted is up"]
    B --> C["self-hosted fallback\nGitea / GitLab CE"]
    A --> D["outage event"]
    D --> E["self-hosted is the active forge"]
    E --> F["engineers push to self-hosted"]
    E --> G["CI runs against self-hosted"]
    H["hosted recovers"] --> I["reverse push-back"]
    I --> A
    C --> I

Choosing the forge: Gitea or GitLab CE

Two open-source forges dominate the self-hosted space:

  • Gitea is a lightweight forge written in Go. It runs on modest hardware (2 vCPU, 4 GB RAM for a small team) and ships as a single binary. The web UI is functional and familiar to GitHub users. The CI integration is through Gitea Actions, which is a fork of GitHub Actions.
  • GitLab CE is the open-source edition of GitLab. It runs on more substantial hardware (4 vCPU, 8 GB RAM minimum) and ships as a set of containers or a single omnibus package. The web UI is the full GitLab UI; the CI integration is GitLab CI.

A team choosing the fallback forge weighs:

  1. Operational burden. Gitea is easier to operate; GitLab CE has more features but more moving parts.
  2. Familiarity. A team familiar with GitHub Actions adapts to Gitea Actions quickly; a team familiar with GitLab CI adapts to GitLab CE quickly.
  3. Hardware. Gitea runs on a small VM; GitLab CE requires more memory and CPU.
  4. Security updates. Both ship security updates; the team’s patch cadence determines the maintenance burden.

For most teams, Gitea is the right answer for the fallback. The fallback needs to be operationally quiet; a heavy maintenance burden on a forge that runs only during outages is a burden that goes unmet.

Bootstrap

The bootstrap is the same shape for both Gitea and GitLab CE: provision a host, install the forge, configure the database and the authentication, and expose the web UI over HTTPS.

# Gitea on a small VM (Ubuntu, single binary)
wget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/1.22/gitea-1.22-linux-amd64
chmod +x /usr/local/bin/gitea
mkdir -p /var/lib/gitea/{custom,data,log}
gitea web
# GitLab CE on a more substantial VM (Ubuntu, omnibus)
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | bash
apt-get install -y gitlab-ce
gitlab-ctl reconfigure

Both forges ship with a web installer that walks through the initial configuration: the database, the administrator account, the SSH and HTTP endpoints, the email settings.

Migrating repositories

The migration from the hosted forge to the self-hosted fallback is the standard mirror-and-push pattern:

# Clone the hosted repo as a mirror
git clone --mirror https://github.com/acme/infra.git /srv/git/infra.git

# Push to the self-hosted fallback
cd /srv/git/infra.git
git remote add fallback https://fallback.internal/acme/infra.git
git push --mirror fallback

git clone --mirror (Part XCVIII-03) creates a bare repository with every ref; git push --mirror pushes every ref - branches, tags, remote-tracking branches, notes - to the fallback. The push creates the repository on the self-hosted forge if it does not exist (depending on the forge’s configuration).

For a fleet of repositories, the migration is scripted:

REPOS="infra platform services docs"
for repo in $REPOS; do
  git clone --mirror "https://github.com/acme/$repo.git" "/srv/git/$repo.git"
  cd "/srv/git/$repo.git"
  git remote add fallback "https://fallback.internal/acme/$repo.git"
  git push --mirror fallback
  cd -
done

The script runs once at the bootstrap of the fallback forge. After the bootstrap, the fallback is kept current with a push-mirror sync from the hosted forge.

Push-mirror sync (hosted to self-hosted)

When the hosted forge is up, the self-hosted fallback is updated by a push-mirror sync: the hosted forge pushes every commit to the self-hosted fallback. GitLab supports push mirroring natively; GitHub supports it via the API or external services.

# Verify the push-mirror is current
git -C /srv/git/infra.git fetch
git -C /srv/git/infra.git log --oneline -1

The push-mirror period is the RPO of the fallback. With native push-mirror (push-on-every-commit), the RPO is measured in seconds. With a scheduled push-mirror (cron), the RPO is measured in the schedule period.

Reverse push-back (self-hosted to hosted)

When the hosted forge recovers, the self-hosted fallback’s state is pushed back to the hosted forge:

# Push the fallback state back to the hosted forge
cd /srv/git/infra.git
git push --mirror https://github.com/acme/infra.git

The reverse push-back is the moment the fallback is decommissioned. A --mirror push to the hosted forge overwrites every ref on the hosted side; the team’s hosted forge has the fallback’s state.

Production discipline

  1. Gitea for the fallback unless the team has a GitLab CE reason. Gitea is operationally lighter; GitLab CE is appropriate when the team already runs GitLab CE for other reasons.
  2. Patch the fallback even when it is not in active use. The moment the fallback is active is the moment a vulnerability is most exposed.
  3. Migrate the repositories once at the bootstrap, then keep current with push-mirror. Manual migration on every outage is slow and error-prone.
  4. Reverse push-back when the hosted forge recovers. The fallback is decommissioned after the push-back; the hosted forge is the canonical again.
  5. Rehearse the bootstrap and the reverse push-back quarterly. The drill catches the migration script’s gaps and the push-back’s risks.

Cross-course references

  • Git, CI/CD & GitOps — Part XCVIII-03 (Mirror Repositories and CDN) covers the read-only mirror that is the precursor to the self-hosted fallback.
  • Git, CI/CD & GitOps — Part XCVIII-04 (Local Bare Repository Fallback) covers the CLI-only fallback that is one step below the self-hosted fallback.
  • Linux for Production Sysadmins — Part XL (ServiceBoot) and Part XII (RepoSecurity) cover the operational foundations of running a long-lived self-hosted service.

Quiz

Knowledge check · 4 questions

  1. Q1. A team needs a long-term fallback forge that provides a web UI, pull-request workflow, and CI integration during a hosted forge outage. Which option is the right answer?

  2. Q2. A self-hosted Git fallback should be patched for security updates even when it is not in active use, because the moment it becomes active (during a forge outage) is the moment it is most exposed.

  3. Q3. Explain why the reverse push-back (self-hosted fallback to hosted forge) uses `git push --mirror` rather than a per-branch push, and what happens to refs that exist only on the hosted side.

  4. Q4. Diagnose the operational gap and recommend the bootstrap and migration sequence.

    A team has decided to provision a self-hosted Gitea instance as the long-term fallback for their hosted forge. The Gitea instance will run on a small VM in a second region. The team needs to migrate 12 repositories from the hosted forge to the Gitea instance. The hosted forge supports push-mirroring to external repositories. The team has not yet decided how to keep the Gitea instance current during normal operation.

Passing score: 75%. Answers are checked in this browser.