Install Gitea on Raspberry Pi

Why Gitea on a Pi

Gitea is one of the lightest self-hosted git platforms available. It idles at ~100 MB RAM and runs natively on ARM64, making the Raspberry Pi 4 (even the 1 GB model) a viable git server for personal projects or small teams. If you want to own your code without renting a VPS, a Pi in your closet does the job.

Prerequisites

  • Raspberry Pi 4 (1 GB+) or Pi 5 running 64-bit Raspberry Pi OS (Bookworm)
  • Docker and Docker Compose installed (guide)
  • MicroSD card (minimum) or USB SSD (recommended for active repos)
  • Ethernet connection recommended (more stable than Wi-Fi for git operations)
  • 10 GB free disk space minimum

Storage: SD Card vs USB SSD

An SD card works for small personal repos, but active development with frequent pushes and CI will wear it out. For anything beyond casual use, attach a USB SSD:

# Identify the SSD
lsblk

# Format and mount (assuming /dev/sda1)
sudo mkfs.ext4 /dev/sda1
sudo mkdir -p /mnt/ssd
sudo mount /dev/sda1 /mnt/ssd

# Add to /etc/fstab for auto-mount
echo "/dev/sda1 /mnt/ssd ext4 defaults,noatime 0 2" | sudo tee -a /etc/fstab

Then point your Docker volumes to /mnt/ssd/gitea/ instead of using named volumes.

Install Docker on Raspberry Pi OS

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

Log out and back in, then verify:

docker run --rm hello-world

Docker Compose Configuration

Create the project directory:

mkdir -p ~/gitea && cd ~/gitea

Create docker-compose.yml:

services:
  gitea:
    image: gitea/gitea:1.25.4
    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=change_this_password  # Change this
      - GITEA__server__ROOT_URL=http://gitea.local:3000/
      - GITEA__server__SSH_PORT=2222
      - GITEA__server__SSH_DOMAIN=gitea.local  # Your Pi's hostname or IP
    restart: unless-stopped
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:22"
    depends_on:
      db:
        condition: service_healthy
    networks:
      - gitea-net

  db:
    image: postgres:16-alpine
    container_name: gitea-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=change_this_password  # Must match GITEA__database__PASSWD
      - POSTGRES_DB=gitea
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "gitea"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - gitea-net
    # Tune PostgreSQL for limited Pi RAM
    command: >
      postgres
      -c shared_buffers=128MB
      -c effective_cache_size=256MB
      -c work_mem=4MB
      -c maintenance_work_mem=64MB
      -c max_connections=20

volumes:
  gitea_data:
  postgres_data:

networks:
  gitea-net:

Using a USB SSD for Storage

Replace the named volumes with bind mounts to your SSD:

volumes:
  - /mnt/ssd/gitea/data:/data
  # ...
# And for PostgreSQL:
volumes:
  - /mnt/ssd/gitea/postgres:/var/lib/postgresql/data

ARM64 Image Compatibility

The gitea/gitea:1.25.4 and postgres:16-alpine images both publish ARM64 variants. Docker automatically pulls the correct architecture — no special flags needed.

Start the stack:

docker compose up -d

First startup takes 30-60 seconds on a Pi 4 as PostgreSQL initializes. Check progress:

docker compose logs -f

First-Time Setup

Open http://<pi-ip>:3000 in your browser. On the initial configuration page:

  1. Database settings are pre-filled from environment variables — verify they match
  2. Set Server Domain to your Pi’s IP or hostname
  3. Set SSH Server Port to 2222
  4. Set Gitea Base URL to http://<pi-ip>:3000/
  5. Create an admin account at the bottom of the page
  6. Click Install Gitea

SSH for Git Operations

Add your SSH public key to your Gitea profile under Settings > SSH / GPG Keys.

Clone repos using the SSH port:

git clone ssh://git@<pi-ip>:2222/username/repo.git

Or configure ~/.ssh/config on your client machine for cleaner commands:

Host gitea-pi
    HostName 192.168.1.100
    Port 2222
    User git
    IdentityFile ~/.ssh/id_ed25519

Then clone with:

git clone gitea-pi:username/repo.git

Pi-Specific Optimization

Reduce Memory Pressure

On a 1 GB Pi, Gitea + PostgreSQL use ~250-350 MB combined, leaving room for the OS but not much else. If memory is tight:

# Add to gitea service environment
- GITEA__cache__ADAPTER=memory
- GITEA__cache__ITEM_TTL=16h
- GITEA__session__PROVIDER=memory

This avoids additional memory overhead from external cache services.

Disable Unused Features

If you do not need the built-in CI (Gitea Actions), disable it to save resources:

- GITEA__actions__ENABLED=false

Git LFS on Limited Storage

Git LFS stores large files outside the main git database. On a Pi with limited storage, either disable it or point LFS storage to an external drive:

- GITEA__server__LFS_START_SERVER=true
- GITEA__lfs__PATH=/data/lfs

If using LFS, a USB SSD is essential. LFS objects on an SD card will cause significant wear.

Temperature Monitoring

The Pi throttles CPU under sustained load (large git pushes, repo migrations). Keep an eye on temperature:

vcgencmd measure_temp

Use a heatsink or active cooling case if running Gitea alongside other services. Throttling starts at 80C.

Backup

#!/bin/bash
# backup-gitea-pi.sh
BACKUP_DIR="/mnt/ssd/backups/gitea/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"

# Database
docker exec gitea-db pg_dump -U gitea gitea > "$BACKUP_DIR/db.sql"

# Gitea data
docker run --rm -v gitea_gitea_data:/data -v "$BACKUP_DIR":/backup alpine \
  tar czf /backup/gitea-data.tar.gz /data

echo "Backup saved to $BACKUP_DIR"

If you only have an SD card, back up to a network share or external USB drive. Never keep your only backup on the same SD card. See Backup Strategy.

Troubleshooting

Container Fails to Start on Pi 3

Symptom: exec format error or container exits immediately.

Fix: Gitea’s Docker image requires 64-bit ARM (ARM64/aarch64). The Pi 3 runs 32-bit by default. Reflash with 64-bit Raspberry Pi OS, or use a Pi 4/5.

Out of Memory Kills

Symptom: Gitea or PostgreSQL is killed by the OOM killer. Check with dmesg | grep -i oom.

Fix: On a 1 GB Pi, reduce PostgreSQL memory usage further:

-c shared_buffers=64MB
-c effective_cache_size=128MB
-c max_connections=10

And increase swap:

sudo dphys-swapfile swapoff
sudo sed -i 's/CONF_SWAPSIZE=.*/CONF_SWAPSIZE=1024/' /etc/dphys-swapfile
sudo dphys-swapfile setup
sudo dphys-swapfile swapon

Slow Git Push for Large Repos

Symptom: Pushing a large repo takes significantly longer than expected.

Fix: The Pi’s CPU and I/O are the bottleneck during git pack operations. There is no configuration fix — this is a hardware limitation. For repos over 1 GB, consider a more powerful server. For moderate repos, patience is the answer.

SD Card Corruption After Power Loss

Symptom: Gitea data or PostgreSQL is corrupted after an unexpected shutdown.

Fix: PostgreSQL’s WAL provides crash recovery for the database, but repeated hard power-offs degrade SD cards. Use a UPS or at minimum a clean shutdown script. Move to USB SSD for reliability.

SSH Connection Refused

Symptom: ssh: connect to host <ip> port 2222: Connection refused.

Fix: Check the container is running (docker compose ps). Verify port 2222 is open on the Pi’s firewall if using ufw or iptables. Check the Pi’s IP has not changed (assign a static IP or DHCP reservation).

Resource Requirements

  • RAM: ~100 MB idle (Gitea) + ~80 MB (PostgreSQL) = ~180 MB total
  • CPU: Low for typical use. Spikes during git pack/clone of large repos
  • Disk: 300 MB for the application, plus repository storage
  • Recommended Pi: Pi 4 (1 GB minimum, 2 GB comfortable), Pi 5 (any model)

Comments