Gitea: Lightweight GitHub Alternative That Actually Runs Anywhere
Wanted to move off GitHub for privacy reasons, but GitLab needed 4GB RAM minimum. Found Gitea - runs on a Raspberry Pi, handles 100+ repos, and feels just like GitHub. Been running it for 8 months on a $5 VPS with zero issues.
Problem
Set up Gitea, created a repo, added my SSH key. Could clone via SSH just fine,
but pushing failed with "repository not found". HTTPS push worked, but SSH didn't.
Double-checked permissions - everything looked correct.
Error: fatal: repository 'username/repo.git' not found
What I Tried
Attempt 1: Re-added SSH key - same error.
Attempt 2: Changed repo permissions to public - still failed.
Attempt 3: Tried pushing with verbose SSH - showed authentication succeeded but then failed.
Actual Fix
The issue was that Gitea's SSH server was listening on port 2222 (not default 22), but my git config was using default port. Also, the SSH key needed to be added to my user profile, not just the admin SSH keys section. The authentication was succeeding (that's why no auth error), but Gitea couldn't find the repo due to user mismatch.
# 1. Check which port Gitea SSH is using
docker exec gitea grep "SSH_PORT" /data/gitea/conf/app.ini
# 2. Configure SSH to use correct port
# In ~/.ssh/config, add:
Host git.yourdomain.com
HostName git.yourdomain.com
Port 2222
User git
IdentityFile ~/.ssh/id_rsa
# 3. Add SSH key to your user profile (not admin keys)
# Gitea UI: Settings → SSH Keys → Add Key
# Paste your public key
# 4. Test SSH connection
ssh -T git@yourdomain.com -p 2222
# Should show: "Hi username! You've successfully authenticated"
# 5. Test push
git remote set-url origin git@yourdomain.com:username/repo.git
git push origin main
# Alternative: Use HTTPS if SSH is problematic
git remote set-url origin https://yourdomain.com/username/repo.git
Problem
Set up a webhook to trigger a CI pipeline on pushes. Webhook showed "last delivery: never" even after multiple pushes. Manual trigger from Gitea UI worked fine, but automatic triggers on push events didn't fire.
What I Tried
Attempt 1: Deleted and recreated webhook - same issue.
Attempt 2: Changed trigger to "all events" - still nothing.
Attempt 3: Checked CI server logs - no incoming requests at all.
Actual Fix
The issue was in Gitea's app.ini configuration. Webhooks were disabled by default, or the queue worker wasn't running properly. Also, some webhook types need specific permissions to access repository data.
# In Gitea app.ini, enable webhooks:
# [server section]
[server]
LFS_START_SERVER = true
LFS_JWT_SECRET = your_secret_here
# [webhook section]
[webhook]
QUEUE_LENGTH = 1000
DELIVER_TIMEOUT = 10
SKIP_TLS_VERIFY = false
# Enable webhook types
[webhook.issues]
ENABLED = true
[webhook.pull_request]
ENABLED = true
# Restart Gitea
docker restart gitea
# Or via docker-compose:
docker-compose exec gitea /app/gitea/webhook
# Verify webhook is enabled:
docker exec gitea /app/gitea doctor check
# Should show webhooks as "OK"
# Test webhook manually:
curl -X POST \
http://your-gitea-url/api/v1/repos/username/repo/hooks/test \
-H "Authorization: token your_access_token"
Problem
Set up Gitea to mirror a GitHub repo. Initial sync worked fine, but subsequent
updates failed randomly. Sometimes it would sync, sometimes it would timeout.
Mirror status showed "syncing" forever.
Error: fatal: unable to access 'https://github.com/...': Failed to connect to github.com port 443
What I Tried
Attempt 1: Re-authenticated with GitHub personal access token - still intermittent.
Attempt 2: Increased sync interval - didn't help, just delayed the problem.
Attempt 3: Checked network connectivity - could ping github.com fine.
Actual Fix
The issue was that Gitea's mirror sync was using the wrong authentication method for GitHub. Also, sync timeouts were too aggressive for large repos. Needed to configure proper credentials and increase timeout values in app.ini.
# 1. Set up mirror credentials properly
# In Gitea UI: Repository Settings → Mirror Sync
# Use GitHub Personal Access Token with 'repo' scope
# 2. Configure mirror settings in app.ini
# [mirror section]
[mirror]
ENABLED = true
MIN_INTERVAL = 10m
DEFAULT_INTERVAL = 1h
# [git section]
[git]
MAX_GIT_DIFF_LINES = 10000
GIT_ARGS = --timeout=600 # 10 minutes for large repos
# 3. For reliable mirroring, use SSH instead of HTTPS
# Generate SSH key for Gitea user
docker exec gitea gitea generate git
# Add public key to GitHub: Settings → SSH Keys
# Update mirror URL to use SSH:
# git@github.com:username/repo.git
# 4. Force manual sync if needed
docker exec gitea /app/gitea mirror sync
# 5. Check mirror status
docker exec gitea /app/gitea mirror list
What I Learned
- Lesson 1: Gitea SSH runs on port 2222 by default - configure ~/.ssh/config or use custom port.
- Lesson 2: Webhooks need explicit enabling in app.ini - not just UI configuration.
- Lesson 3: Mirror sync needs proper authentication and timeout settings - large repos can take time.
- Overall: Gitea is incredibly lightweight (runs on 256MB RAM) but has some quirks. The documentation is good but assumes some Git/SSH knowledge. Once configured, it's rock solid.
Production Setup
Complete Docker Compose setup with PostgreSQL.
# docker-compose.yml for Gitea
version: "3.8"
services:
gitea-db:
image: postgres:14-alpine
container_name: gitea-db
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=gitea
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- gitea-network
restart: always
gitea:
image: gitea/gitea:latest
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- DB_TYPE=postgres
- DB_HOST=gitea-db:5432
- DB_NAME=gitea
- DB_USER=gitea
- DB_PASSWD=${DB_PASSWORD}
- SSH_DOMAIN=git.yourdomain.com
- SSH_PORT=2222
- HTTP_PORT=3000
- ROOT_URL=https://git.yourdomain.com
- LFS_START_SERVER=true
ports:
- "2222:2222"
- "127.0.0.1:3000:3000"
volumes:
- ./data/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
- gitea-db
networks:
- gitea-network
restart: always
networks:
gitea-network:
# .env file
echo "DB_PASSWORD=$(openssl rand -base64 32)" > .env
# Start Gitea
docker-compose up -d
# Access web UI at: http://localhost:3000
# Or via reverse proxy: https://git.yourdomain.com
# Initial setup (first visit):
# - Database Type: PostgreSQL
# - Host: gitea-db:5432
# - User: gitea
# - Password: (from .env)
# - Database Name: gitea
# Admin account creation
# - Username: admin
# - Password: (choose strong password)
# - Email: admin@yourdomain.com
# Configure reverse proxy (Nginx example)
server {
listen 443 ssl http2;
server_name git.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
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;
}
# WebSocket support for git operations
location ~ ^/([^/]+\.git(/info/refs)?|.*/git-upload-pack\.git)$ {
proxy_pass http://localhost:3000;
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;
}
}
Monitoring & Debugging
Keep your Gitea instance healthy:
Essential Commands
# Check Gitea logs
docker logs -f gitea
# Update Gitea
docker-compose pull
docker-compose up -d
# Backup database
docker exec gitea-db pg_dump -U gitea gitea | gzip > gitea_backup.sql.gz
# Backup data directory
tar -czf gitea_data_backup.tar.gz ./data/gitea
# Check SSH connectivity
ssh -T -p 2222 git@git.yourdomain.com
# Run Gitea doctor (health check)
docker exec gitea gitea doctor check
# Manage users via CLI
docker exec -u git gitea gitea admin user list
docker exec -u git gitea gitea admin user create --username newuser --email user@example.com --password password
# Resync all repositories
docker exec gitea /app/gitea repo-sync re-mirror
Red Flags to Watch For
- SSH push failures - verify SSH port configuration and key placement
- Webhooks not firing - check app.ini webhook settings
- High memory usage - Gitea shouldn't use >512MB, check for memory leaks
- Mirror sync failures - verify authentication tokens and network connectivity