Gitea

Lightweight self-hosted Git service

Git Hosting & Forges ★ 58.1k stars Easy setup MIT

Gitea is a lightweight, self-hosted Git service offering repositories, issues, pull requests, and CI integration. It targets developers and teams who want a fast, easy-to-run code platform. It is deployed via Docker or a single binary.

Gitea setup guides & articles

Hands-on coverage of Gitea from the blog.

Key features

  • Fast single-binary Git server
  • Issues, PRs and wiki
  • Built-in package registry
  • Gitea Actions CI

Quick deploy

A starting point for self-hosting Gitea - check the official docs for the full set of options.

  • Image gitea/gitea:latest
  • Web port 3000
  • Persist /data
Docker Compose
services:
  gitea:
    image: gitea/gitea:latest
    ports:
      - "3000:3000"
      - "222:22"
    environment:
      - USER_UID=1000
      - USER_GID=1000
    volumes:
      - ./data:/data
    restart: unless-stopped
docker run
docker run -d --name gitea \
  -p 3000:3000 \
  -p 222:22 \
  -e USER_UID=1000 \
  -e USER_GID=1000 \
  -v ./data:/data \
  --restart unless-stopped \
  gitea/gitea:latest

Watch out for

  • Map container SSH to a spare host port (222 here) - clone URLs will include it

Pros & cons

Strengths

  • Very lightweight
  • Easy to deploy
  • Familiar GitHub-like UI

Trade-offs

  • Fewer features than GitLab
  • Governance concerns spurred a fork

Gitea replaces

Last reviewed Aug 24, 2026 · 717 words

Gitea gives you a GitHub-shaped forge — repos, issues, pull requests, packages, CI — in a single Go binary that idles around 256 MB, which means the spare mini PC can host every repository you own with room to spare. The install is genuinely easy; this guide is about the four decisions that make the instance still pleasant in year three: SSH strategy, mirrors, Actions, and backups.

Decide the SSH story on day one

Git over SSH wants port 22, and your host's own sshd already owns it. Three options, in rising order of elegance: map the container to a spare port (222:22) and live with ssh://git@host:222/... clone URLs; run Gitea's built-in SSH on the alternate port and standardise it in every ~/.ssh/config (Host gitea + Port 222 makes URLs clean again); or give the container its own IP/DNS name so it can own a real port 22. The first is what everyone does, the second is the first done properly, and changing later means touching every clone's remote — hence day one. HTTP(S) cloning through your reverse proxy works regardless and is fine for read-heavy use.

Mirrors: the killer feature nobody sets up

Gitea can pull-mirror any GitHub/GitLab repository on a schedule — your starred projects, your own repos, the dependencies you'd hate to lose — turning your forge into automatic insurance against upstream deletion, license rug-pulls, and account lockouts. New Migration → select "mirror", done; Gitea re-syncs on an interval. The inverse, push mirroring, keeps your canonical Gitea repos mirrored to GitHub for visibility and contributions while your infrastructure stays primary. Together they answer the "but my stuff lives on GitHub" objection with "it lives in both places, and I control one of them" — the self-hosted git forge argument in practical form.

Actions: powerful, and the runner is the risk

Gitea Actions speaks GitHub Actions' workflow syntax, and most simple workflows port with a find-and-replace of action URLs. The architecture matters more than the syntax: the act_runner executes jobs, and a runner executes whatever code a repository's workflow tells it to — so treat runners as untrusted-code engines. Run them in Docker on a box you'd tolerate being compromised, never on the Gitea host itself with a mounted docker socket, and don't enable Actions for mirrored third-party repos (mirrored code you don't review shouldn't get compute). For a homelab, one containerised runner labeled for your own repos covers CI for everything you write, with build caches making second runs pleasantly fast.

One command backs up everything

gitea dump produces a single zip containing the database, repositories, config, and attachments — the complete instance. Schedule it, ship it off-box per 3-2-1, and practice the restore once (unzip, restore DB, point config at the repos — twenty minutes). Two settings worth flipping before others join: DISABLE_REGISTRATION = true after your accounts exist (a public forge signup page attracts spam bots with remarkable speed), and if the instance faces the internet at all, fail2ban or CrowdSec watching the login endpoint. Upgrades are the pull-and-restart kind, with release notes that rarely bite — check the breaking-changes section when crossing minor versions and you'll be fine.

The Forgejo question, briefly

Gitea has a community-governed hard fork, Forgejo, born from governance concerns when Gitea's trademark moved to a company; features remain close, migration Gitea→Forgejo is supported, and the reverse isn't. If that history changes your calculus, the full comparison walks through it — the short version is that existing happy Gitea instances have no urgent reason to move, and new values-driven installs should consider starting on Forgejo.

What I'd do

Gitea in Docker with 222:22 plus an ~/.ssh/config entry, registration closed, pull-mirrors of every repo I'd cry about losing, one sandboxed Actions runner, nightly gitea dump to the backup box, CrowdSec if exposed. The result costs less RAM than a browser tab and removes GitHub from the list of companies whose policy changes can touch your work — which, for infrastructure this cheap to run, is a remarkable trade.

Compare Gitea

11 head-to-head comparisons.

Similar git hosting & forges apps