12 KiB
Gitea Stack - Self-Hosted Git Server
Overview
Gitea acts as the central Git server with integrated CI/CD capabilities through Gitea Actions, handling:
- Git repository hosting
- User and organization management
- Pull requests and code reviews
- Issue tracking
- Gitea Actions for CI/CD (runner runs on development machine)
- API for automation
Services
- git.michaelschiemer.de - Gitea Web Interface
- git.michaelschiemer.de:2222 - SSH for Git operations
- MySQL 8.0 - Database backend
- Redis 7 - Cache, session, and queue storage
Prerequisites
-
Traefik Stack Running
cd ../traefik docker compose up -d -
DNS Configuration Point
git.michaelschiemer.deto your server IP (94.16.110.151) -
SSH Port Availability Ensure port 2222 is open in your firewall for Git SSH operations
Configuration
1. Create Environment File
cp .env.example .env
2. Generate Strong Passwords
# MySQL root password
openssl rand -base64 32
# MySQL gitea password
openssl rand -base64 32
# Redis password
openssl rand -base64 32
Update .env with generated passwords:
MYSQL_ROOT_PASSWORD=<generated-password-1>
MYSQL_PASSWORD=<generated-password-2>
REDIS_PASSWORD=<generated-password-3>
3. Adjust Configuration (Optional)
Edit .env for:
- Domain customization
- User registration settings
- Database configuration
Deployment
Initial Setup
# Deploy stack
docker compose up -d
# Check logs
docker compose logs -f
# Wait for MySQL initialization (30-60 seconds)
docker compose logs mysql | grep "ready for connections"
# Verify services are healthy
docker compose ps
First Time Configuration
Option 1: Automated Setup (Recommended)
The Gitea initial setup can be automated using Ansible:
cd deployment/ansible
# 1. Set Gitea admin credentials in vault
ansible-vault edit secrets/production.vault.yml --vault-password-file secrets/.vault_pass
# Add these variables:
# vault_gitea_admin_username: "admin"
# vault_gitea_admin_password: "your-secure-password"
# vault_gitea_admin_email: "kontakt@michaelschiemer.de"
# 2. Run the setup playbook
ansible-playbook -i inventory/production.yml \
playbooks/setup-gitea-initial-config.yml \
--vault-password-file secrets/.vault_pass
The playbook will:
- Check if Gitea is already configured
- Generate
app.iniwithINSTALL_LOCK = trueto skip the initial setup page - Copy the configuration file to the Gitea container
- Create admin user via Gitea CLI with credentials from vault
- Use database settings from environment variables
How it works:
The playbook creates a complete app.ini configuration file with INSTALL_LOCK = true in the [security] section. This tells Gitea to skip the initial setup wizard. The admin user is then created using the gitea admin user create command.
Option 2: Manual Setup
-
Access Gitea: https://git.michaelschiemer.de
-
Initial Setup Wizard:
- Database settings are pre-configured via environment variables
- Set up admin account:
- Username:
admin(or your preference) - Email:
kontakt@michaelschiemer.de - Password: Strong password
- Username:
- Server and third-party settings: Use defaults
- Click "Install Gitea"
-
Verify SSH Access:
# Test SSH connection (replace 'git' with your username after setup) ssh -T -p 2222 git@git.michaelschiemer.de
Usage
Creating a Repository
Option 1: Automated Setup (Recommended)
Use Ansible to automatically create the repository and configure Git remote:
cd deployment/ansible
ansible-playbook -i inventory/production.yml \
playbooks/setup-gitea-repository.yml \
--vault-password-file secrets/.vault_pass \
-e "repo_name=michaelschiemer" \
-e "repo_owner=michael" \
-e "repo_private=false"
The playbook will:
- Create repository in Gitea via API
- Configure Git remote automatically
- Use credentials from Ansible Vault
Option 2: Manual Setup
- Log in to https://git.michaelschiemer.de
- Click "+" → "New Repository"
- Fill in repository details
- Clone via HTTPS or SSH:
# HTTPS git clone https://git.michaelschiemer.de/username/repo.git # SSH git clone ssh://git@git.michaelschiemer.de:2222/username/repo.git
Configuration File
Gitea configuration is managed via app.ini file:
- Local file:
deployment/stacks/gitea/app.ini(for local development) - Production: Generated from Ansible template
deployment/ansible/templates/gitea-app.ini.j2 - The
app.iniis mounted read-only into the container at/data/gitea/conf/app.ini - Configuration is based on the official Gitea example: https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini
Key Configuration Sections:
[server]: Domain, ports, SSH settings[database]: PostgreSQL connection[actions]: Actions enabled, no GitHub dependency[service]: Registration settings[cache]/[session]/[queue]: Storage configuration
Gitea Actions
Gitea Actions (GitHub Actions compatible) are enabled by default. To use them:
- Create
.gitea/workflows/directory in your repository - Add workflow YAML files (e.g.,
deploy.yml) - Register a Runner (see Runner setup section below)
Note: The Gitea Actions Runner should run on your development machine, not on the production server. See Stack 9 documentation for runner setup.
User Management
Disable Registration (Default):
- Set
DISABLE_REGISTRATION=truein.env(already default) - Create users via Admin Panel
Enable Registration:
- Set
DISABLE_REGISTRATION=falsein.env - Restart:
docker compose restart gitea
Organizations and Teams
- Navigate to Organizations
- Create organization
- Add repositories to organization
- Manage teams and permissions
API Access
Gitea provides a comprehensive API:
# Generate API token
# Settings → Applications → Generate New Token
# Example: List repositories
curl -H "Authorization: token YOUR_TOKEN" \
https://git.michaelschiemer.de/api/v1/user/repos
API Documentation: https://git.michaelschiemer.de/api/swagger
Backup & Recovery
Manual Backup
# Backup script (run on production server)
#!/bin/bash
BACKUP_DIR="/backups/gitea"
DATE=$(date +%Y%m%d_%H%M%S)
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup Gitea data
docker run --rm \
-v gitea-data:/data \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/gitea-data-$DATE.tar.gz -C /data .
# Backup MySQL database
docker exec gitea-mysql mysqldump \
-u root -p$MYSQL_ROOT_PASSWORD \
--all-databases \
--single-transaction \
--quick \
--lock-tables=false \
> $BACKUP_DIR/gitea-mysql-$DATE.sql
# Backup Redis data
docker run --rm \
-v gitea-redis-data:/data \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/gitea-redis-$DATE.tar.gz -C /data .
echo "Backup completed: $BACKUP_DIR/*-$DATE.*"
Restore from Backup
# Stop services
docker compose down
# Restore Gitea data
docker run --rm \
-v gitea-data:/data \
-v /backups/gitea:/backup \
alpine tar xzf /backup/gitea-data-YYYYMMDD_HHMMSS.tar.gz -C /data
# Restore MySQL
cat /backups/gitea/gitea-mysql-YYYYMMDD_HHMMSS.sql | \
docker exec -i gitea-mysql mysql -u root -p$MYSQL_ROOT_PASSWORD
# Restore Redis
docker run --rm \
-v gitea-redis-data:/data \
-v /backups/gitea:/backup \
alpine tar xzf /backup/gitea-redis-YYYYMMDD_HHMMSS.tar.gz -C /data
# Start services
docker compose up -d
Automated Backups
Add to crontab on production server:
# Daily backup at 2 AM
0 2 * * * /path/to/backup-gitea.sh
# Keep only last 7 days
0 3 * * * find /backups/gitea -type f -mtime +7 -delete
Monitoring
Health Checks
# Check service health
docker compose ps
# Gitea health endpoint
curl -f https://git.michaelschiemer.de/api/healthz
# MySQL health
docker exec gitea-mysql mysqladmin ping -h localhost -u root -p$MYSQL_ROOT_PASSWORD
# Redis health
docker exec gitea-redis redis-cli -a $REDIS_PASSWORD ping
Logs
# All services
docker compose logs -f
# Gitea only
docker compose logs -f gitea
# MySQL only
docker compose logs -f mysql
# Redis only
docker compose logs -f redis
# MySQL slow queries
docker exec gitea-mysql tail -f /var/log/mysql/slow-queries.log
Resource Usage
# Container stats
docker stats gitea gitea-mysql gitea-redis
# Disk usage
docker system df -v | grep gitea
Troubleshooting
Gitea Not Starting
# Check logs
docker compose logs gitea
# Common issues:
# 1. MySQL not ready - wait 30-60 seconds
# 2. Database connection failed - check MYSQL_PASSWORD in .env
# 3. Redis connection failed - check REDIS_PASSWORD
SSH Not Working
# Verify port 2222 is open
sudo ufw status | grep 2222
# Open if needed
sudo ufw allow 2222/tcp
# Test SSH connection
ssh -T -p 2222 git@git.michaelschiemer.de
# Check Gitea SSH settings
# Admin Panel → Configuration → Server and Other Services → SSH Server Domain
Database Connection Issues
# Verify MySQL is running and healthy
docker compose ps mysql
# Test database connection
docker exec gitea-mysql mysql -u gitea -p$MYSQL_PASSWORD -e "SELECT 1;"
# Check MySQL logs
docker compose logs mysql | grep -i error
Redis Connection Issues
# Verify Redis is running
docker compose ps redis
# Test Redis connection
docker exec gitea-redis redis-cli -a $REDIS_PASSWORD ping
# Check Redis logs
docker compose logs redis
Performance Issues
# Check MySQL slow queries
docker exec gitea-mysql tail -100 /var/log/mysql/slow-queries.log
# Analyze MySQL performance
docker exec gitea-mysql mysql -u root -p$MYSQL_ROOT_PASSWORD \
-e "SHOW PROCESSLIST;"
# Check Redis memory usage
docker exec gitea-redis redis-cli -a $REDIS_PASSWORD INFO memory
Reset Admin Password
# Connect to Gitea container
docker exec -it gitea bash
# Change admin password
gitea admin user change-password --username admin --password new-password
Security
Security Best Practices
- Disable User Registration: Set
DISABLE_REGISTRATION=true - Strong Passwords: Use generated passwords for all services
- Regular Updates: Keep Gitea, MySQL, and Redis updated
- SSH Keys: Prefer SSH keys over HTTPS for Git operations
- 2FA: Enable two-factor authentication for admin accounts
- API Token Security: Rotate tokens regularly
- Firewall: Only expose ports 80, 443, and 2222
Update Stack
# Pull latest images
docker compose pull
# Recreate containers
docker compose up -d
# Verify
docker compose ps
Security Headers
Security headers are applied via Traefik's default-chain@file middleware:
- HSTS
- Content-Type Nosniff
- XSS Protection
- Frame Deny
- CSP
Integration with Other Stacks
Docker Registry (Stack 3)
Gitea Actions can push built images to the private Docker Registry:
# .gitea/workflows/deploy.yml
- name: Push to Registry
run: |
docker login registry.michaelschiemer.de -u ${{ secrets.REGISTRY_USER }} -p ${{ secrets.REGISTRY_PASS }}
docker push registry.michaelschiemer.de/myapp:latest
Application Stack (Stack 4)
Deploy applications via Gitea Actions + Ansible:
- name: Deploy to Production
run: |
ansible-playbook -i inventory/production deploy.yml
Performance Tuning
MySQL Optimization
Adjust mysql/conf.d/gitea.cnf:
innodb_buffer_pool_size: Increase for more RAMmax_connections: Increase for more concurrent usersslow_query_log: Monitor slow queries
Redis Optimization
# Add to docker-compose.yml redis command:
# --maxmemory 512mb --maxmemory-policy allkeys-lru
Gitea Configuration
Edit via Admin Panel → Configuration or app.ini:
- Enable caching for static assets
- Adjust session timeout
- Configure queue workers for Actions
Additional Resources
- Gitea Documentation: https://docs.gitea.io/
- Gitea Actions: https://docs.gitea.io/en-us/usage/actions/overview/
- API Documentation: https://git.michaelschiemer.de/api/swagger
- MySQL Tuning: https://dev.mysql.com/doc/refman/8.0/en/optimization.html