How to Install Gitea on a VPS: Self-Hosted Git with CI/CD, Webhooks, and Team Management
Gitea is a lightweight, self-hosted Git service written in Go — a private GitHub alternative that you run on your own VPS. It provides repository hosting, pull requests, issues, project boards, CI/CD via Gitea Actions (GitHub Actions-compatible), webhooks, team management, and an API — all on hardware you control, with no per-seat licensing fees. This guide installs Gitea with Docker Compose, PostgreSQL, and Nginx with SSL.
Why Self-Host Git
- No per-seat cost: GitHub Team charges $4/user/month; GitLab SaaS $19/user/month. Gitea on a VPS has zero licensing fees for any number of users.
- Unlimited private repositories: No restrictions on private repo count.
- Data sovereignty: Source code stays on your infrastructure — important for IP or compliance requirements.
- Lightweight: Gitea uses 100–200 MB RAM at idle — runs comfortably alongside other services.
Step 1: Install Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update && sudo apt install -y docker-ce docker-compose-plugin
sudo usermod -aG docker $USER
Step 2: Docker Compose Configuration
mkdir -p /opt/gitea && cd /opt/gitea
nano docker-compose.yml
version: '3.8'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: always
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=${POSTGRES_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__service__DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-false}
ports:
- "127.0.0.1:3000:3000"
- "2222:22"
volumes:
- gitea_data:/data
depends_on:
- gitea-db
gitea-db:
image: postgres:16-alpine
container_name: gitea-db
restart: always
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=gitea
volumes:
- gitea_postgres:/var/lib/postgresql/data
volumes:
gitea_data:
gitea_postgres:
nano .env
POSTGRES_PASSWORD=StrongGiteaDbPassword!
DISABLE_REGISTRATION=false
chmod 600 .env
docker compose up -d
Step 3: Configure Nginx
sudo nano /etc/nginx/sites-available/gitea
server {
listen 80;
server_name git.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
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 Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_read_timeout 300s;
}
}
sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d git.yourdomain.com
Step 4: Initial Setup
Visit https://git.yourdomain.com for the installation wizard. Database settings are pre-filled from environment variables. Create your administrator account and click Install Gitea.
After setup, disable open registration for private instances:
# Update .env:
DISABLE_REGISTRATION=true
docker compose up -d
Step 5: SSH Key Setup
Allow port 2222 in UFW, then users configure their SSH client:
sudo ufw allow 2222/tcp
# ~/.ssh/config on each developer's machine
Host git.yourdomain.com
HostName git.yourdomain.com
Port 2222
IdentityFile ~/.ssh/id_ed25519
# Clone a repository
git clone git@git.yourdomain.com:yourorg/myrepo.git
Step 6: Set Up Gitea Actions CI/CD
Enable Gitea Actions in the Docker environment:
- GITEA__actions__ENABLED=true
Deploy an Actions runner:
wget https://gitea.com/gitea/act_runner/releases/download/latest/act_runner-linux-amd64
chmod +x act_runner-linux-amd64
sudo mv act_runner-linux-amd64 /usr/local/bin/act_runner
# Register the runner
act_runner register \
--no-interactive \
--instance https://git.yourdomain.com \
--token YOUR_RUNNER_TOKEN \
--name "vps-runner" \
--labels "ubuntu-latest:docker://gitea/runner-images:ubuntu-latest"
# /etc/systemd/system/gitea-runner.service
[Unit]
Description=Gitea Actions Runner
After=docker.service
[Service]
ExecStart=/usr/local/bin/act_runner daemon
WorkingDirectory=/opt/gitea-runner
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now gitea-runner
Now .gitea/workflows/ YAML files trigger CI/CD pipelines — compatible with GitHub Actions syntax:
# .gitea/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: rsync -avz dist/ user@production-server:/var/www/app/
Backup and Updates
# Backup Gitea
docker exec gitea gitea admin dump -c /data/gitea/conf/app.ini --file /data/gitea-dump.zip
docker cp gitea:/data/gitea-dump.zip /opt/backups/gitea-$(date +%Y%m%d).zip
# Update Gitea
cd /opt/gitea
docker compose pull && docker compose up -d
Getting Started
Gitea runs efficiently on 512 MB–1 GB RAM — it fits comfortably alongside other services on a 2 GB Ubuntu VPS at VPS.DO. For teams with source code that must not leave your own infrastructure, self-hosted Gitea provides GitHub-equivalent functionality with complete data ownership.
Conclusion
Gitea provides a full-featured, GitHub-compatible Git hosting experience on a VPS at zero licensing cost. The Docker Compose setup keeps it easy to maintain and update, PostgreSQL provides reliable storage, and Gitea Actions enables CI/CD pipelines compatible with GitHub Actions syntax. For teams concerned about source code privacy, vendor lock-in, or GitHub’s per-seat pricing, self-hosted Gitea is the practical alternative.