Git, CI/CD & GitOpsXCVIII · Git Hosting FailureGitHostingFail
Self-hosted Git as fallback — Gitea, GitLab CE
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
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:
- Operational burden. Gitea is easier to operate; GitLab CE has more features but more moving parts.
- Familiarity. A team familiar with GitHub Actions adapts to Gitea Actions quickly; a team familiar with GitLab CI adapts to GitLab CE quickly.
- Hardware. Gitea runs on a small VM; GitLab CE requires more memory and CPU.
- 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
- 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.
- 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.
- Migrate the repositories once at the bootstrap, then keep current with push-mirror. Manual migration on every outage is slow and error-prone.
- Reverse push-back when the hosted forge recovers. The fallback is decommissioned after the push-back; the hosted forge is the canonical again.
- 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
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?
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.
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.
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.