GitSelf-Hosted Git (Gitea, Gogs)

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

YAML
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

Bash
# 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

Bash
# 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 gitea
Tip
Put Gitea behind a reverse proxy such as Nginx or Caddy and let it terminate TLS. Caddy will obtain and renew a free Let's Encrypt certificate automatically with a two-line config, giving you HTTPS with almost no effort.
A 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

Bash
# 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

Bash
# 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
Restricting SSH access
For a multi-user bare-repo setup, use the open-source `gitolite` tool, or give the shared `git` user a restricted shell (`git-shell`). This lets people push and pull but not get an interactive login on the server.
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 dump to 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

Bash
# 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

Warning
Self-hosting is not free — you trade subscription fees for an ongoing maintenance burden. You become responsible for security patches, backups, certificate renewals, uptime, storage growth, and disaster recovery. If your code is unreachable because the server is down, that is on you. Be honest about whether your team has the time and on-call capacity to operate the service before you commit to it.
Tip
Start small: run Gitea on a single inexpensive VPS with automated daily `gitea dump` backups synced off-site. It comfortably serves a team of dozens on modest hardware, and you can always migrate to GitLab CE or a managed provider later if your needs outgrow it.