Install Gitea on Ubuntu Server

Why Ubuntu for Gitea

Ubuntu Server is the most common platform for self-hosted services. Gitea runs well on it with minimal overhead — you get a full GitHub-like experience with PostgreSQL-backed reliability and native SSH integration for git operations.

Prerequisites

  • Ubuntu 22.04 or 24.04 LTS server (fresh install or existing)
  • Docker and Docker Compose installed (guide)
  • 1 GB RAM minimum (2 GB recommended for active teams)
  • 10 GB free disk space (more for large repositories)
  • A domain name pointed at your server (optional, for HTTPS)
  • Root or sudo access

Install Docker

If Docker is not already installed:

sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER

Log out and back in for the group change to take effect.

Docker Compose Configuration

Create a directory for Gitea:

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=gitea_db_password  # Change this
      - GITEA__server__ROOT_URL=http://localhost:3000/  # Change to your domain
      - GITEA__server__SSH_PORT=2222
      - GITEA__server__SSH_DOMAIN=localhost  # Change to your domain
    restart: unless-stopped
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"    # Web UI
      - "2222:22"      # SSH for git operations
    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=gitea_db_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

volumes:
  gitea_data:
  postgres_data:

networks:
  gitea-net:

SSH Port Considerations

Port 2222 avoids conflict with the host’s SSH daemon on port 22. Your git clone URLs will look like:

git clone ssh://git@your-server:2222/username/repo.git

If you want clean git@your-server:username/repo.git URLs without the port, you can move the host SSH to a different port and map Gitea to 22. Edit /etc/ssh/sshd_config:

sudo sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
sudo systemctl restart sshd

Then change the Docker Compose port mapping to "22:22" and set GITEA__server__SSH_PORT=22.

Start Gitea

docker compose up -d

Verify both containers are running:

docker compose ps

UFW Firewall Rules

If UFW is enabled (recommended), open the required ports:

# Web UI
sudo ufw allow 3000/tcp comment 'Gitea Web'

# Git SSH
sudo ufw allow 2222/tcp comment 'Gitea SSH'

# If using a reverse proxy for HTTPS
sudo ufw allow 443/tcp comment 'HTTPS'

# Verify
sudo ufw status

If you moved host SSH to port 2222 and Gitea SSH to 22, adjust accordingly — make sure you allow the new host SSH port before reloading UFW or you will lock yourself out.

First-Time Setup

Open http://your-server-ip:3000 in your browser. Gitea shows a one-time setup page. The database fields are pre-filled from the Docker Compose environment variables. Verify they are correct, then:

  1. Site Title — name your instance (e.g., “My Gitea”)
  2. Server Domain — your domain or IP address
  3. SSH Server Port2222 (or 22 if you remapped)
  4. Gitea Base URLhttp://your-domain:3000/ or https://your-domain/ if behind a reverse proxy
  5. Admin Account — create your admin user at the bottom of the page

Click Install Gitea. You will be redirected to the login page.

Git Over SSH Setup

To clone repos over SSH, add your public key to your Gitea account:

  1. Generate a key pair if you do not have one:
ssh-keygen -t ed25519 -C "[email protected]"
  1. Copy your public key:
cat ~/.ssh/id_ed25519.pub
  1. In Gitea, go to Settings > SSH / GPG Keys > Add Key and paste it.

  2. Test the connection:

ssh -T git@your-server -p 2222

You should see: Hi there, username! You've successfully authenticated...

HTTPS via Reverse Proxy

For production, put Gitea behind a reverse proxy with Let’s Encrypt. Update the environment variables first:

- GITEA__server__ROOT_URL=https://git.yourdomain.com/
- GITEA__server__SSH_DOMAIN=git.yourdomain.com

Then configure your reverse proxy to forward HTTPS traffic to localhost:3000. See Reverse Proxy Setup for Nginx Proxy Manager, Traefik, or Caddy configurations.

Remove the 3000:3000 port mapping from Docker Compose if the reverse proxy runs on the same host and connects via Docker network.

systemd Auto-Start

Docker containers with restart: unless-stopped start automatically when Docker starts. Docker itself starts on boot by default on Ubuntu. Verify:

sudo systemctl is-enabled docker

If it returns disabled:

sudo systemctl enable docker

Backup

Back up both the Gitea data volume and PostgreSQL:

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

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

# Gitea data (repos, avatars, attachments)
docker run --rm -v gitea_gitea_data:/data -v "$BACKUP_DIR":/backup alpine \
  tar czf /backup/gitea-data.tar.gz /data

echo "Backup complete: $BACKUP_DIR"

Schedule with cron for daily backups. See Backup Strategy for the full 3-2-1 approach.

Troubleshooting

Permission Denied on SSH Clone

Symptom: Permission denied (publickey) when cloning via SSH.

Fix: Verify your SSH key is added to your Gitea account. Check you are connecting to the correct port:

ssh -T git@your-server -p 2222 -v

The verbose flag shows which key is being offered. Ensure the key in ~/.ssh/id_ed25519.pub matches the one in Gitea.

Port 3000 Already in Use

Symptom: Container fails to start with bind: address already in use.

Fix: Another service is using port 3000. Find it:

sudo lsof -i :3000

Change the host port in Docker Compose to something else, e.g., "3001:3000", and update ROOT_URL accordingly.

Database Connection Refused

Symptom: Gitea logs show dial tcp: connect: connection refused for PostgreSQL.

Fix: The database container may not be ready. Check the health status:

docker compose ps

If db shows as unhealthy, check its logs:

docker compose logs db

Common cause: mismatched password between POSTGRES_PASSWORD and GITEA__database__PASSWD.

Web UI Shows 502 After Reverse Proxy Setup

Symptom: Nginx/Caddy returns 502 Bad Gateway.

Fix: The reverse proxy cannot reach Gitea. If both run in Docker, they must share a network. If the proxy runs on the host, Gitea must expose port 3000. Check ROOT_URL matches your actual access URL exactly.

Large Push Rejected

Symptom: RPC failed; HTTP 413 when pushing large files.

Fix: If behind a reverse proxy, increase the client max body size. For Nginx:

client_max_body_size 100M;

For Gitea itself, edit app.ini inside the container or set the environment variable:

- GITEA__server__LFS_MAX_FILE_SIZE=104857600  # 100 MB

Resource Requirements

  • RAM: ~150 MB idle, ~300 MB under active use
  • CPU: Low (spikes during git operations and CI)
  • Disk: 500 MB for the application, plus repository storage

Comments