Self-Hosted Git (Gitea, Gogs)
You do not need GitHub, GitLab.com, or Azure DevOps to host Git repositories. Git is decentralized by design, and a handful of lightweight, open-source servers let you run your own code-hosting platform on a cheap VPS, a Raspberry Pi, or a server in your own data center. This page covers when self-hosting makes sense, the main options, and how to set up the two most common approaches: a full Gitea instance and a plain bare repository over SSH.
Why self-host?
Privacy: your source code never leaves infrastructure you control — important for sensitive or proprietary work.
Cost: a $5/month VPS can host unlimited private repos for a whole team; no per-seat fees.
Control: you own the data, the backups, the uptime, and the upgrade schedule. No surprise feature removals or pricing changes.
Compliance: data-residency, air-gapped, or regulated environments (HIPAA, GDPR, ITAR) often forbid third-party cloud hosting.
Learning: running your own server teaches you how Git hosting actually works under the hood.
Options overview
Tool | Language | Resource use | Best for |
|---|---|---|---|
Gitea | Go | Very low (~100 MB RAM) | Most teams; the popular, actively developed choice |
Gogs | Go | Very low | Minimalist setups; slower development pace |
GitLab CE | Ruby | High (4+ GB RAM) | Teams wanting built-in CI/CD, registry, and full DevOps suite |
Forgejo | Go | Very low | Community-governed Gitea fork; values independence |
Plain bare repos over SSH | None | Negligible | A handful of repos, no web UI needed |
Gitea and Forgejo (a community-governed fork of Gitea) are the sweet spot for most people: a fast, single-binary server with a GitHub- like web UI, pull requests, issues, and CI — all running comfortably on modest hardware. GitLab CE is far more powerful but also far heavier. For just a few repos with no web UI, nothing beats a bare repo over SSH.
Gogs vs Gitea
Gitea began in 2016 as a community fork of Gogs after contributors wanted faster, more open development than the largely single-maintainer Gogs project allowed. Today Gitea is the more active project by a wide margin — more frequent releases, more features (PR review workflows, actions, packages), and a larger community. Gogs still works and is admirably lean, but for a new install Gitea is the recommended choice. (Forgejo is in turn a fork of Gitea, made for similar governance reasons.)
Setting up Gitea with Docker Compose
The easiest way to run Gitea is with Docker Compose. The following file runs Gitea alongside a PostgreSQL database, persists data to named volumes, and exposes the web UI on port 3000 and SSH on port 222.
docker-compose.yml
version: "3"
networks:
gitea:
external: false
services:
server:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD}
restart: always
networks:
- gitea
volumes:
- gitea-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
depends_on:
- db
db:
image: postgres:16
container_name: gitea-db
restart: always
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${GITEA_DB_PASSWORD}
- POSTGRES_DB=gitea
networks:
- gitea
volumes:
- gitea-db:/var/lib/postgresql/data
volumes:
gitea-data:
gitea-db:Start Gitea and finish setup in the browser
# Provide the DB password via an .env file (never commit it) echo "GITEA_DB_PASSWORD=$(openssl rand -base64 24)" > .env # Launch docker compose up -d # Watch the logs until it is ready docker compose logs -f server # Then open http://your-server:3000 and complete the install wizard: # - confirm database settings # - set the site title and base URL # - create the first admin account
Installing Gitea from the binary
If you prefer not to use Docker, Gitea ships as a single static binary. Create a dedicated user, drop the binary in place, and run it under systemd.
Binary install on Linux
# Download the binary for your architecture
wget -O gitea https://dl.gitea.com/gitea/1.22/gitea-1.22-linux-amd64
chmod +x gitea
sudo mv gitea /usr/local/bin/gitea
# Create a dedicated system user and directories
sudo adduser --system --group --disabled-password --home /home/git git
sudo mkdir -p /var/lib/gitea/{custom,data,log} /etc/gitea
sudo chown -R git:git /var/lib/gitea /etc/gitea
# Create the systemd service, then:
sudo systemctl enable --now gitea
sudo systemctl status giteaA bare repo over SSH (no server software)
If you only need to share a few repositories and do not want a web UI at all, you can host Git with nothing but SSH access and a --bare repository. A bare repo has no working directory — it is the .git data only — which is exactly what a remote should be.
On the server: create a bare repository
# SSH into the server, then: mkdir -p /srv/git/myproject.git cd /srv/git/myproject.git git init --bare # Make sure the 'git' user owns it chown -R git:git /srv/git/myproject.git
On your machine: add the remote and push
# Add the SSH remote (user@host:path) git remote add origin git@your-server:/srv/git/myproject.git # Push your branch — others clone the same URL git push -u origin main # A teammate clones it like any other repo git clone git@your-server:/srv/git/myproject.git
Backup strategies
When you self-host, you are responsible for backups. There is no provider safety net. A good backup covers both the repository data and the metadata (issues, users, pull requests) stored in the database.
Gitea built-in dump: run
gitea dumpto produce a single archive containing repos, the database, config, and attachments.Database backups: schedule
pg_dump(or the equivalent) for the metadata if you run a separate DB.Volume snapshots: snapshot the Docker volumes or the data directory on a schedule.
Off-site copies: push backups to object storage (S3, B2) so a server failure does not also destroy the backups.
Test restores: a backup you have never restored is a hope, not a backup — rehearse recovery periodically.
Backing up a Dockerized Gitea instance
# Create a Gitea dump inside the container docker compose exec -u git server gitea dump -c /data/gitea/conf/app.ini # Copy the resulting archive out to the host docker compose cp server:/app/gitea/gitea-dump-*.zip ./backups/ # Sync backups off-site (example with rclone to S3) rclone copy ./backups remote:gitea-backups
Security considerations
Always use HTTPS for the web UI — terminate TLS at a reverse proxy and redirect plain HTTP to HTTPS.
Prefer SSH keys over passwords for Git access, and disable password authentication on the server SSH daemon.
Lock down the firewall: expose only ports 443 (web) and 22/222 (SSH); never expose the database port to the internet.
Keep the software patched: subscribe to release notes and apply security updates promptly.
Enable 2FA for all accounts, especially admins.
Run as a non-root user and isolate the service (container or dedicated system user) so a compromise is contained.
Disable open registration unless you genuinely want anyone to sign up.
When self-hosting makes sense — and when it does not
Self-host when... | Use a SaaS provider when... |
|---|---|
You have strict privacy or compliance requirements | You want zero maintenance overhead |
You want to avoid per-seat pricing at scale | Your team is small and a free tier covers you |
You already operate servers and ops capacity | You have no one to handle uptime, patching, backups |
You need full control over data and configuration | You rely on a large ecosystem of integrations |
You want an air-gapped or on-premises deployment | High availability is critical and you cannot staff it |