Building a Self-Hosted Git Server with Gitea

Why Self-Hosted Git?

Hosting your own Git server gives you:

What is Gitea?

Gitea is a self-hosted Git service written in Go. It’s lightweight, easy to deploy, and requires minimal resources—perfect for homelabs.

Key Features

Installation with Docker

1. Create Directories

mkdir -p ~/gitea/data ~/gitea/config

2. Docker Compose Setup

Create docker-compose.yml:

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    container_name: gitea-db
    environment:
      POSTGRES_DB: gitea
      POSTGRES_USER: gitea
      POSTGRES_PASSWORD: your_secure_password
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - gitea
    restart: unless-stopped

  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    depends_on:
      - db
    environment:
      - DB_TYPE=postgres
      - DB_HOST=db:5432
      - DB_NAME=gitea
      - DB_USER=gitea
      - DB_PASSWD=your_secure_password
      - GITEA__SECURITY__INSTALL_LOCK=true
      - GITEA__SERVER__ROOT_URL=https://git.example.com/
      - GITEA__SERVER__DOMAIN=git.example.com
      - GITEA__SERVER__HTTP_PORT=3000
      - GITEA__SERVER__SSH_PORT=2222
    volumes:
      - ./data/gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:22"
    networks:
      - gitea
    restart: unless-stopped

networks:
  gitea:
    driver: bridge

3. Launch Gitea

docker-compose up -d
docker-compose logs -f gitea

Initial Configuration

  1. Access Gitea at http://localhost:3000
  2. Complete the installation wizard
  3. Create your admin account

Configuration File

After first run, edit app.ini:

nano ~/gitea/data/gitea/conf/app.ini

Important Settings

[database]
DB_TYPE = postgres
HOST = db:5432
NAME = gitea
USER = gitea
PASSWD = your_secure_password

[repository]
ROOT = /data/git/repositories
SCRIPT_TYPE = bash

[repository.upload]
ENABLED = true
TEMP_PATH = data/tmp/uploads
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
MAX_FILE_SIZE = 50

[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com/
HTTP_PORT = 3000
SSH_PORT = 2222
START_SSH_SERVER = true

[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false

Setting Up HTTPS with Nginx

Create nginx.conf:

upstream gitea {
    server 127.0.0.1:3000;
}

server {
    listen 80;
    server_name git.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name git.example.com;

    ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;

    location / {
        proxy_pass http://gitea;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Host $server_name;
    }
}

SSH Configuration

Generate SSH Keys

ssh-keygen -t ed25519 -C "[email protected]"

Add Key to Gitea

  1. Login to Gitea
  2. Click profile avatar → Settings
  3. SSH/GPG Keys → Add Key
  4. Paste your public key

Clone via SSH

git clone ssh://[email protected]:2222/username/repo.git

Creating Repositories

  1. Click “+” icon in top navigation
  2. Click “New Repository”
  3. Configure:
    • Repository name
    • Description
    • Visibility (public/private)
    • Initialize with README

Team Management

Create Organization

  1. Click “+” → New Organization
  2. Enter organization name
  3. Invite users

User Roles

Webhooks for CI/CD

Configure Webhook

  1. Go to Repository Settings
  2. Click Webhooks
  3. Add Webhook:
    • URL: https://jenkins.example.com/github-webhook/
    • Events: Push, Pull Request
    • Active: Yes

Backup and Recovery

Backup Script

#!/bin/bash
BACKUP_DIR="/backups/gitea"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# Stop Gitea
docker-compose stop gitea

# Backup database and data
tar -czf "$BACKUP_DIR/gitea_$TIMESTAMP.tar.gz" ~/gitea/data

# Start Gitea
docker-compose start gitea

# Keep last 30 days of backups
find "$BACKUP_DIR" -name "gitea_*.tar.gz" -mtime +30 -delete

Schedule with cron:

# 2 AM daily
0 2 * * * /path/to/backup.sh

Performance Tuning

Database Optimization

-- Connect to PostgreSQL
VACUUM ANALYZE gitea;

-- Recreate indexes
REINDEX DATABASE gitea;

Gitea Settings

[cache]
ADAPTER = memory
INTERVAL = 60

[session]
PROVIDER = memory

[queue]
TYPE = level

Security Hardening

  1. Disable Registration: Set DISABLE_REGISTRATION = true
  2. Require Sign-In: Set REQUIRE_SIGNIN_VIEW = true
  3. SSH Key Only: Disable password login
  4. Enable 2FA: Settings → Security → Two-Factor Authentication
  5. API Rate Limiting: Configure in app.ini

Troubleshooting

Cannot access Gitea

docker-compose logs gitea
docker-compose restart gitea

SSH Connection Issues

# Test SSH connection
ssh -vvv -p 2222 [email protected]

# Check SSH service
docker-compose exec gitea gitea admin user check

Conclusion

Gitea provides a lightweight, self-hosted Git solution perfect for homelab environments. It handles code collaboration, issue tracking, and CI/CD integration without the complexity of larger solutions.

Resources