Technology Encyclopedia Home >How to Install Gitea — Self-Hosted Git on a VPS

How to Install Gitea — Self-Hosted Git on a VPS

I started self-hosting my Git repositories for a simple reason: some of my projects are for clients and I didn't want to explain why their code was sitting on a third-party server they hadn't agreed to.

Gitea is what I landed on. It's a full Git hosting platform — repositories, pull requests, issues, webhooks, Actions CI/CD — in a single binary that runs comfortably on a small server. The web interface is clean and responsive, and the whole thing uses maybe 50 MB of RAM at idle.

Setup is genuinely simple compared to heavier git platforms. I had it running in about 45 minutes including the database and Nginx configuration.

I run Gitea on Tencent Cloud Lighthouse. The entry-level plan (2 vCPU / 2 GB RAM) handles Gitea well for personal or small team use — Gitea is intentionally resource-efficient. Key reasons I use Lighthouse for this specifically: OrcaTerm means I can manage the Gitea service from a browser if my local environment isn't available, the snapshot feature gives me a quick full backup before upgrading Gitea versions, and the console-level firewall lets me restrict SSH access to specific IP ranges for an additional layer of repository access control.


Table of Contents

  1. Why Self-Host a Git Service?
  2. Prerequisites
  3. Part 1 — Server Setup
  4. Part 2 — Deploy Gitea with Docker Compose
  5. Part 3 — Configure Nginx Reverse Proxy
  6. Part 4 — Enable HTTPS
  7. Part 5 — Complete Gitea Setup
  8. Part 6 — SSH Key Access for Git Operations
  9. Part 7 — Configure Actions (CI/CD)
  10. Part 8 — Backups
  11. The Gotcha: SSH Port and Git Clone URL
  12. Common Gitea Admin Tasks

  • Key Takeaways
  • Use the appropriate Lighthouse application image to skip manual installation steps where available
  • Lighthouse snapshots provide one-click full-server backup before major changes
  • OrcaTerm browser terminal lets you manage the server from any device
  • CBS cloud disk expansion handles growing storage needs without server migration
  • Console-level firewall + UFW = two independent protection layers

Why Self-Host a Git Service? {#why}

Self-hosting Gitea gives you:

  • Private repositories at no extra cost — unlimited private repos without a subscription
  • Full data ownership — your code, issues, and history stay on your server
  • Integration with your infrastructure — webhooks to your own deployment pipelines
  • No rate limits or API restrictions — run as many automated operations as needed
  • Custom authentication — integrate with LDAP, OAuth providers, or manage users yourself
  • Runs on minimal hardware — unlike GitLab, Gitea runs comfortably on 512 MB RAM

Prerequisites {#prerequisites}

Requirement Notes
Cloud server Tencent Cloud Lighthouse Ubuntu 22.04
A domain name For HTTPS and SSH clone URLs
Docker and Docker Compose We'll install these

Part 1 — Server Setup {#part-1}

ssh ubuntu@YOUR_SERVER_IP
sudo apt update && sudo apt upgrade -y

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

sudo apt install -y nginx
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw allow 2222/tcp   # Custom SSH port for Gitea Git operations
sudo ufw enable

Part 2 — Deploy Gitea with Docker Compose {#part-2}

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

Create docker-compose.yml:

version: '3.8'

services:
  gitea-db:
    image: postgres:16-alpine
    container_name: gitea-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: gitea
      POSTGRES_USER: gitea
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - gitea_db:/var/lib/postgresql/data

  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    ports:
      - "3000:3000"    # Web interface
      - "2222:22"      # SSH for Git operations
    environment:
      USER_UID: 1000
      USER_GID: 1000
      GITEA__database__DB_TYPE: postgres
      GITEA__database__HOST: gitea-db:5432
      GITEA__database__NAME: gitea
      GITEA__database__USER: gitea
      GITEA__database__PASSWD: ${DB_PASSWORD}
      GITEA__server__DOMAIN: git.yourdomain.com
      GITEA__server__ROOT_URL: https://git.yourdomain.com
      GITEA__server__SSH_DOMAIN: git.yourdomain.com
      GITEA__server__SSH_PORT: 2222
      GITEA__server__HTTP_PORT: 3000
      GITEA__service__DISABLE_REGISTRATION: "false"  # Set to "true" after first admin setup
      GITEA__service__REQUIRE_SIGNIN_VIEW: "false"
      GITEA__mailer__ENABLED: "false"   # Configure SMTP later
    volumes:
      - gitea_data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      - gitea-db

volumes:
  gitea_db:
  gitea_data:

Create .env:

nano .env
DB_PASSWORD=generate_a_strong_password
chmod 600 .env

docker compose up -d
docker compose logs -f gitea
# Wait for: Server listening on...

Part 3 — Configure Nginx Reverse Proxy {#part-3}

sudo nano /etc/nginx/sites-available/gitea
server {
    listen 80;
    server_name git.yourdomain.com;

    client_max_body_size 512m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        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_connect_timeout 60s;
        proxy_read_timeout    300s;
        proxy_send_timeout    300s;
    }
}
sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Part 4 — Enable HTTPS {#part-4}

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d git.yourdomain.com

Part 5 — Complete Gitea Setup {#part-5}

Visit https://git.yourdomain.com.

The setup page appears (or first-time install wizard if using the web installer):

  1. Database settings — should be pre-configured from environment variables
  2. General settings:
    • Site title: My Gitea
    • Repository root path: /data/gitea/repositories
    • Git user: git
    • SSH server domain: git.yourdomain.com
    • SSH port: 2222
    • Gitea base URL: https://git.yourdomain.com
  3. Admin account: create your first admin user
  4. Click Install Gitea

Disable public registration (after setup)

Once your admin account is created, disable open registration:

In Gitea admin: Site Administration → Configuration → Service → Disable Self-Registration

Or update docker-compose.yml:

GITEA__service__DISABLE_REGISTRATION: "true"

Then restart: docker compose restart gitea


Part 6 — SSH Key Access for Git Operations {#part-6}

Add your public SSH key to Gitea:

  1. Log in → Settings → SSH / GPG Keys
  2. Click Add Key
  3. Paste your public key (~/.ssh/id_ed25519.pub)

Clone a repository:

# SSH (uses port 2222)
git clone ssh://git@git.yourdomain.com:2222/username/repo.git

# HTTPS
git clone https://git.yourdomain.com/username/repo.git

Configure SSH to use the correct port

Add to ~/.ssh/config on your local machine:

Host git.yourdomain.com
    HostName git.yourdomain.com
    User git
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

After this, you can use the standard Git SSH URL format:

git clone git@git.yourdomain.com:username/repo.git

Part 7 — Configure Actions (CI/CD) {#part-7}

Gitea Actions is compatible with GitHub Actions syntax. Enable it:

Update docker-compose.yml:

GITEA__actions__ENABLED: "true"

Restart Gitea:

docker compose restart gitea

Deploy an Actions runner

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

Get a runner registration token from Gitea admin: Administration → Runners → Create new runner

# docker-compose.yml for the runner
version: '3.8'

services:
  runner:
    image: gitea/act_runner:latest
    container_name: gitea-runner
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - runner_data:/data
    environment:
      GITEA_INSTANCE_URL: https://git.yourdomain.com
      GITEA_RUNNER_REGISTRATION_TOKEN: YOUR_REGISTRATION_TOKEN
      GITEA_RUNNER_NAME: my-runner

volumes:
  runner_data:
docker compose up -d

Now you can use .gitea/workflows/deploy.yml files (same syntax as GitHub Actions) in your repositories.


Part 8 — Backups {#part-8}

nano ~/backup_gitea.sh
#!/bin/bash
BACKUP_DIR=~/backups/gitea
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR

source ~/apps/gitea/.env

# Backup database
docker exec gitea-db pg_dump -U gitea gitea | \
  gzip > $BACKUP_DIR/gitea_db_$DATE.sql.gz

# Backup Gitea data (repos, attachments, etc.)
docker run --rm \
  -v gitea_gitea_data:/data \
  -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/gitea_data_$DATE.tar.gz -C /data .

# Keep 7 days
find $BACKUP_DIR -mtime +7 -delete

echo "Gitea backup complete: $DATE"
chmod +x ~/backup_gitea.sh
(crontab -l; echo "0 3 * * * ~/backup_gitea.sh") | crontab -

The Gotcha: SSH Port and Git Clone URL {#gotcha}

Gitea maps SSH to a non-standard port (2222 in our setup) because port 22 is used by the server's own SSH daemon. This means the Git SSH clone URL format is slightly different from GitHub:

Standard format (doesn't work with non-standard port):

git clone git@git.yourdomain.com:username/repo.git  # Port 22

Explicit port format (works):

git clone ssh://git@git.yourdomain.com:2222/username/repo.git

Or configure ~/.ssh/config to map the host to port 2222 (shown in Part 6) — then the standard format works.

Gitea shows the correct clone URL in its web interface. If users report SSH clone failures, check the SSH port configuration in the Gitea admin panel and ensure it matches the Docker port mapping.


Common Gitea Admin Tasks {#admin}

# Update Gitea
cd ~/apps/gitea
docker compose pull gitea
docker compose up -d --remove-orphans

# Admin CLI (inside container)
docker exec -u git gitea gitea admin user create --username newuser --password password --email user@example.com --admin

# View logs
docker compose logs -f gitea

# Reset admin password
docker exec -u git gitea gitea admin user change-password --username admin --password newpassword

# Generate a new token
docker exec -u git gitea gitea admin user generate-access-token --username admin --token-name ci-token

Troubleshooting {#troubleshooting}

Issue Likely Cause Fix
Connection refused Service not running or wrong port Check systemctl status SERVICE and verify firewall rules
Permission denied Wrong file ownership or permissions Check file ownership with ls -la and use chown/chmod to fix
502 Bad Gateway Backend service not running Restart the backend service; check logs with journalctl -u SERVICE
SSL certificate error Certificate expired or domain mismatch Run sudo certbot renew and verify domain DNS points to server IP
Service not starting Config error or missing dependency Check logs with journalctl -u SERVICE -n 50 for specific error
Out of disk space Logs or data accumulation Run df -h to identify usage; clean logs or attach CBS storage
High memory usage Too many processes or memory leak Check with htop; consider upgrading instance plan if consistently high
Firewall blocking traffic Port not open in UFW or Lighthouse console Open port in Lighthouse console firewall AND sudo ufw allow PORT

Frequently Asked Questions {#faq}

Is self-hosted Gitea suitable for production use?
Yes — Gitea is used in production environments ranging from individual developers to small teams. Pair it with regular Lighthouse snapshots and stay current with updates.

How do I migrate from a cloud-hosted Gitea to self-hosted?
Export your data from the cloud service, import it to the self-hosted instance, update DNS or internal service configurations, and verify everything works before switching fully.

How much disk space does Gitea need?
Initial installation is minimal. Disk usage grows with usage — artifacts, repositories, and caches accumulate over time. Monitor with df -h and use CBS cloud disk expansion when needed.

How do I set up backups for Gitea?
Use Lighthouse snapshots for full-server recovery. Additionally, export Gitea's application data directly (usually a backup command or data directory export) for granular restore capability.

Can I run Gitea alongside other services on the same server?
Yes, with sufficient RAM. Check the resource requirements and monitor actual usage. Containerized deployments (Docker) provide good isolation between services.

Host your own Git server today:
👉 Tencent Cloud Lighthouse — Lightweight VPS for Gitea
👉 View current pricing and promotions
👉 Explore all active deals and offers