Private Git Server Configuration: Complete Self-Hosted Git Solution Guide
In today's development landscape, version control is essential for managing code, collaborating with teams, and maintaining project history. While cloud-based solutions like GitHub, GitLab, and Bitbucket are popular, hosting your own private Git server offers complete control over your code, enhanced security, and freedom from third-party dependencies. This comprehensive guide walks you through setting up, configuring, and managing a private Git server on your own infrastructure.
Introduction
A private Git server provides centralized version control infrastructure under your complete control. Whether you're concerned about data sovereignty, need enhanced security for proprietary code, want to avoid subscription costs, or simply prefer self-hosted solutions, setting up your own Git server is a practical and achievable goal.
Why Host Your Own Git Server?
Security and Privacy: Your code never leaves your infrastructure, giving you complete control over access and data protection. This is crucial for organizations handling sensitive intellectual property or operating under strict compliance requirements.
Cost Efficiency: After initial setup, hosting costs are typically lower than enterprise cloud Git solutions, especially for larger teams or extensive repository collections.
Customization: Full control over the server environment allows custom integrations, backup strategies, and workflows tailored to your specific needs.
No Vendor Lock-in: Independence from third-party service providers means no concerns about service changes, price increases, or platform availability.
Learning Opportunity: Setting up a Git server deepens your understanding of Git's architecture and server administration.
Git Server Approaches
This guide covers multiple approaches to hosting Git, from basic SSH-based solutions to full-featured web interfaces:
- Basic SSH Git Server: Simple, secure, command-line focused
- Gitolite: Advanced access control with minimal overhead
- Gitea: Lightweight, self-contained Git service with web interface
- GitLab CE: Full-featured DevOps platform with CI/CD integration
Each approach has different resource requirements, feature sets, and complexity levels, allowing you to choose the solution that best fits your needs.
Prerequisites
Before beginning the installation, ensure you have:
System Requirements
Minimum Requirements (Basic SSH Git Server):
- Linux server (Ubuntu 20.04+, Debian 11+, CentOS 8+, Rocky Linux 8+)
- 1 CPU core
- 512MB RAM
- 10GB disk space
- Root or sudo access
Recommended Requirements (Gitea):
- 2 CPU cores
- 2GB RAM
- 20GB+ disk space (depending on repository size)
- Domain name or static IP address
Recommended Requirements (GitLab CE):
- 4 CPU cores
- 4GB RAM (8GB recommended)
- 50GB+ disk space
- Domain name with SSL certificate
Required Software
- Git (version 2.x or higher)
- SSH server (OpenSSH)
- Text editor (vim, nano, or preferred alternative)
- Web server (Nginx or Apache) for web-based solutions
- Database (PostgreSQL or MySQL) for Gitea/GitLab
Network Requirements
- SSH port 22 (or custom port) open for Git operations
- Port 80/443 for web interface (web-based solutions)
- Static IP address or dynamic DNS for consistent access
- Domain name (optional but recommended for web-based solutions)
Security Prerequisites
- Firewall configured (UFW, firewalld, or iptables)
- SSH key-based authentication configured
- Regular backup solution planned
- SSL/TLS certificates for HTTPS (Let's Encrypt recommended)
Installation
Method 1: Basic SSH Git Server
The simplest Git server requires only SSH access and Git installation. This method is ideal for small teams or personal use.
Step 1: Install Git
# Ubuntu/Debian
sudo apt update
sudo apt install -y git
# CentOS/Rocky Linux
sudo dnf install -y git
Verify installation:
git --version
Step 2: Create Git User
Create a dedicated user for Git operations:
sudo adduser git
sudo passwd git # Set a secure password
Step 3: Configure SSH Access
Set up SSH key-based authentication:
# Switch to git user
sudo su - git
# Create SSH directory
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Add client SSH public keys to ~/.ssh/authorized_keys:
# On client machine, generate SSH key if needed
ssh-keygen -t ed25519 -C "[email protected]"
# Copy public key to server
cat ~/.ssh/id_ed25519.pub | ssh git@your-server "cat >> ~/.ssh/authorized_keys"
Step 4: Create Repository Directory
# As git user
sudo su - git
mkdir -p ~/repositories
cd ~/repositories
Step 5: Initialize Bare Repository
# Create bare repository
git init --bare project.git
cd project.git
git config core.sharedRepository group
Step 6: Configure Git Server
Set appropriate permissions:
# As root
sudo chown -R git:git /home/git/repositories
sudo chmod -R 755 /home/git/repositories
Step 7: Test Remote Access
From a client machine:
# Clone the repository
git clone git@your-server:repositories/project.git
# Make changes and push
cd project
echo "# My Project" > README.md
git add README.md
git commit -m "Initial commit"
git push origin master
Method 2: Gitolite Installation
Gitolite provides advanced access control while maintaining simplicity.
Step 1: Install Gitolite
# Ubuntu/Debian
sudo apt update
sudo apt install -y gitolite3
# CentOS/Rocky Linux
sudo dnf install -y gitolite3
Step 2: Configure Gitolite
# Create git user
sudo adduser git
# Copy admin SSH key
sudo cp /path/to/admin-key.pub /tmp/admin.pub
sudo chmod 644 /tmp/admin.pub
# Initialize gitolite
sudo su - git
gitolite setup -pk /tmp/admin.pub
Step 3: Clone Admin Repository
From your local machine:
git clone git@your-server:gitolite-admin
cd gitolite-admin
Step 4: Configure Access Control
Edit conf/gitolite.conf:
# gitolite.conf
@developers = alice bob charlie
@admins = admin
repo gitolite-admin
RW+ = @admins
repo project1
RW+ = @admins
RW = @developers
repo project2
RW+ = @admins
R = @developers
Commit and push changes:
git add conf/gitolite.conf
git commit -m "Configure repository access"
git push origin master
Method 3: Gitea Installation
Gitea provides a lightweight, self-hosted Git service with web interface.
Step 1: Install Dependencies
# Ubuntu/Debian
sudo apt update
sudo apt install -y git wget sqlite3
# CentOS/Rocky Linux
sudo dnf install -y git wget sqlite
Step 2: Create Gitea User
sudo adduser --system --shell /bin/bash --group --disabled-password --home /home/git git
Step 3: Download Gitea
# Check latest version at https://github.com/go-gitea/gitea/releases
wget -O /tmp/gitea https://dl.gitea.io/gitea/1.21.0/gitea-1.21.0-linux-amd64
sudo mv /tmp/gitea /usr/local/bin/gitea
sudo chmod +x /usr/local/bin/gitea
Step 4: Create Directory Structure
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R git:git /var/lib/gitea
sudo chmod -R 750 /var/lib/gitea
sudo mkdir /etc/gitea
sudo chown root:git /etc/gitea
sudo chmod 770 /etc/gitea
Step 5: Create Systemd Service
Create /etc/systemd/system/gitea.service:
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
Step 6: Start Gitea
sudo systemctl daemon-reload
sudo systemctl enable gitea
sudo systemctl start gitea
sudo systemctl status gitea
Step 7: Configure Firewall
# UFW (Ubuntu/Debian)
sudo ufw allow 3000/tcp
# firewalld (CentOS/Rocky)
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload
Step 8: Complete Web Installation
Access http://your-server:3000 and complete the installation wizard.
Method 4: GitLab CE Installation
GitLab provides a complete DevOps platform with Git hosting, CI/CD, and project management.
Step 1: Install Dependencies
# Ubuntu/Debian
sudo apt update
sudo apt install -y curl openssh-server ca-certificates tzdata perl
# CentOS/Rocky Linux
sudo dnf install -y curl policycoreutils openssh-server perl
sudo systemctl enable sshd
sudo systemctl start sshd
Step 2: Add GitLab Repository
# Ubuntu/Debian
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
# CentOS/Rocky Linux
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
Step 3: Install GitLab
# Ubuntu/Debian
sudo EXTERNAL_URL="https://gitlab.yourdomain.com" apt install -y gitlab-ce
# CentOS/Rocky Linux
sudo EXTERNAL_URL="https://gitlab.yourdomain.com" dnf install -y gitlab-ce
Step 4: Configure GitLab
Edit /etc/gitlab/gitlab.rb:
external_url 'https://gitlab.yourdomain.com'
gitlab_rails['gitlab_shell_ssh_port'] = 22
gitlab_rails['time_zone'] = 'UTC'
Reconfigure GitLab:
sudo gitlab-ctl reconfigure
Step 5: Access GitLab
Access https://gitlab.yourdomain.com and set the root password.
Configuration
SSH Server Configuration
Optimize SSH for Git operations by editing /etc/ssh/sshd_config:
# Security settings
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
# Performance settings
ClientAliveInterval 120
ClientAliveCountMax 3
MaxStartups 10:30:100
# Git-specific
AllowUsers git
Restart SSH:
sudo systemctl restart sshd
Nginx Reverse Proxy for Gitea
Create /etc/nginx/sites-available/gitea:
server {
listen 80;
server_name git.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name git.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem;
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;
}
}
Enable configuration:
sudo ln -s /etc/nginx/sites-available/gitea /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Database Configuration for Gitea
For production use, configure PostgreSQL instead of SQLite.
Install PostgreSQL
# Ubuntu/Debian
sudo apt install -y postgresql postgresql-contrib
# CentOS/Rocky Linux
sudo dnf install -y postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl enable postgresql
sudo systemctl start postgresql
Create Database
sudo -u postgres psql
CREATE DATABASE gitea;
CREATE USER gitea WITH PASSWORD 'secure_password';
GRANT ALL PRIVILEGES ON DATABASE gitea TO gitea;
\q
Update Gitea Configuration
Edit /etc/gitea/app.ini:
[database]
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = gitea
USER = gitea
PASSWD = secure_password
Restart Gitea:
sudo systemctl restart gitea
Backup Configuration
Automated Backup Script
Create /usr/local/bin/git-backup.sh:
#!/bin/bash
BACKUP_DIR="/backup/git"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=30
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Backup repositories (basic Git server)
tar -czf "$BACKUP_DIR/repositories_$DATE.tar.gz" /home/git/repositories
# Backup Gitea (if using)
if systemctl is-active --quiet gitea; then
sudo -u git gitea dump -c /etc/gitea/app.ini -f "$BACKUP_DIR/gitea_$DATE.zip"
fi
# Remove old backups
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete
echo "Backup completed: $DATE"
Make executable:
sudo chmod +x /usr/local/bin/git-backup.sh
Schedule with Cron
sudo crontab -e
# Add daily backup at 2 AM
0 2 * * * /usr/local/bin/git-backup.sh >> /var/log/git-backup.log 2>&1
Practical Examples
Example 1: Creating and Cloning Repository
Server-side:
# SSH Git server
sudo su - git
cd ~/repositories
git init --bare myproject.git
# Gitea/GitLab: Use web interface
Client-side:
# SSH server
git clone git@your-server:repositories/myproject.git
# Gitea
git clone https://git.yourdomain.com/username/myproject.git
# GitLab
git clone https://gitlab.yourdomain.com/username/myproject.git
Example 2: Managing Multiple Users (Gitolite)
# Clone admin repository
git clone git@your-server:gitolite-admin
cd gitolite-admin
# Add user SSH key
cp ~/Downloads/user-key.pub keydir/users/newuser.pub
# Grant repository access
cat >> conf/gitolite.conf << EOF
repo newproject
RW+ = admin
RW = newuser
EOF
# Apply changes
git add keydir/users/newuser.pub conf/gitolite.conf
git commit -m "Add newuser with access to newproject"
git push origin master
Example 3: Setting Up Git Hooks
Pre-receive hook for code quality checks:
Create repositories/project.git/hooks/pre-receive:
#!/bin/bash
while read oldrev newrev refname; do
# Check commit message format
if ! git log --format=%s $oldrev..$newrev | grep -qE '^(feat|fix|docs|style|refactor|test|chore):'; then
echo "Error: Commit messages must follow conventional commits format"
exit 1
fi
done
exit 0
Make executable:
chmod +x repositories/project.git/hooks/pre-receive
Example 4: Mirroring External Repository
# Create mirror repository
cd /home/git/repositories
git clone --mirror https://github.com/username/project.git
# Set up automatic sync
cat > /usr/local/bin/sync-mirror.sh << 'EOF'
#!/bin/bash
cd /home/git/repositories/project.git
git fetch --all --prune
EOF
chmod +x /usr/local/bin/sync-mirror.sh
# Schedule sync every hour
echo "0 * * * * /usr/local/bin/sync-mirror.sh" | sudo -u git crontab -
Example 5: Repository Migration
From GitHub to private server:
# Clone with all branches and tags
git clone --mirror https://github.com/username/project.git
# Create new repository on private server
ssh git@your-server "cd repositories && git init --bare project.git"
# Push to private server
cd project.git
git push --mirror git@your-server:repositories/project.git
# Update local clone to use new remote
cd /path/to/local/clone
git remote set-url origin git@your-server:repositories/project.git
Verification
Testing SSH Connectivity
# Test SSH connection
ssh -T git@your-server
# Expected output for basic Git server:
# PTY allocation request failed on channel 0
# Expected output for Gitolite:
# hello username, this is git@server running gitolite3...
Repository Access Verification
# Clone test
git clone git@your-server:repositories/test.git test-clone
# Push test
cd test-clone
echo "test" > test.txt
git add test.txt
git commit -m "Test commit"
git push origin master
Service Status Checks
# Check Gitea
sudo systemctl status gitea
curl http://localhost:3000
# Check GitLab
sudo gitlab-ctl status
curl http://localhost
# Check SSH
sudo systemctl status sshd
ss -tlnp | grep :22
Performance Testing
# Clone performance test
time git clone git@your-server:repositories/large-repo.git
# Push performance test
cd large-repo
dd if=/dev/zero of=largefile bs=1M count=100
git add largefile
time git commit -m "Add large file"
time git push origin master
Troubleshooting
Issue 1: Permission Denied (publickey)
Symptom: Permission denied (publickey) when attempting to clone or push
Solution:
# Verify SSH key is added
ssh-add -l
# Add SSH key if missing
ssh-add ~/.ssh/id_ed25519
# Test SSH connection with verbose output
ssh -vT git@your-server
# Check authorized_keys on server
sudo cat /home/git/.ssh/authorized_keys
# Verify permissions
sudo ls -la /home/git/.ssh
# Should show: drwx------ for .ssh directory
# Should show: -rw------- for authorized_keys file
# Fix permissions if needed
sudo chmod 700 /home/git/.ssh
sudo chmod 600 /home/git/.ssh/authorized_keys
sudo chown -R git:git /home/git/.ssh
Issue 2: Repository Not Found
Symptom: fatal: repository not found error
Solution:
# Verify repository exists on server
sudo ls -la /home/git/repositories/
# Check repository path in clone command
# Correct: git clone git@server:repositories/project.git
# Wrong: git clone git@server:project.git
# Verify git user can access repository
sudo -u git ls -la /home/git/repositories/project.git
Issue 3: Push Rejected
Symptom: ! [remote rejected] master -> master (pre-receive hook declined)
Solution:
# Check hook permissions
sudo ls -la /home/git/repositories/project.git/hooks/
# Hooks must be executable
sudo chmod +x /home/git/repositories/project.git/hooks/pre-receive
# Check hook output for errors
sudo -u git /home/git/repositories/project.git/hooks/pre-receive
# Temporarily disable hook for testing
sudo mv /home/git/repositories/project.git/hooks/pre-receive \
/home/git/repositories/project.git/hooks/pre-receive.disabled
Issue 4: Gitea Web Interface Not Accessible
Symptom: Cannot access Gitea web interface
Solution:
# Check Gitea service status
sudo systemctl status gitea
# Check if port is listening
sudo ss -tlnp | grep 3000
# Check Gitea logs
sudo journalctl -u gitea -n 50
# Verify firewall rules
sudo ufw status | grep 3000
# Or for firewalld:
sudo firewall-cmd --list-ports
# Test local access
curl http://localhost:3000
Issue 5: Slow Git Operations
Symptom: Clone and push operations are extremely slow
Solution:
# Enable Git repack
cd /home/git/repositories/project.git
sudo -u git git repack -ad
# Enable garbage collection
sudo -u git git gc --aggressive
# Check disk I/O
iostat -x 1 5
# Check available memory
free -h
# Enable Git compression
git config --global core.compression 9
# For large repositories, increase buffer size
git config --global http.postBuffer 524288000
Issue 6: Database Connection Failed (Gitea)
Symptom: Gitea fails to start with database connection errors
Solution:
# Check PostgreSQL status
sudo systemctl status postgresql
# Verify database exists
sudo -u postgres psql -l | grep gitea
# Test connection
sudo -u postgres psql -d gitea -c "SELECT version();"
# Check Gitea configuration
sudo cat /etc/gitea/app.ini | grep -A5 "\[database\]"
# Verify PostgreSQL allows local connections
sudo grep "local.*all.*all" /etc/postgresql/*/main/pg_hba.conf
# Restart services
sudo systemctl restart postgresql
sudo systemctl restart gitea
Best Practices
Security Best Practices
- Disable Password Authentication: Always use SSH keys
# /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
- Implement Fail2ban: Protect against brute force attacks
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
- Regular Security Updates:
# Ubuntu/Debian
sudo apt update && sudo apt upgrade -y
# CentOS/Rocky Linux
sudo dnf update -y
- Use HTTPS for Web Interfaces: Always configure SSL/TLS
# Let's Encrypt
sudo certbot --nginx -d git.yourdomain.com
-
Implement Access Control: Use Gitolite or GitLab roles for granular permissions
-
Enable Two-Factor Authentication: For Gitea/GitLab web interfaces
-
Restrict Git User Shell:
# Limit git user to Git operations only
sudo usermod -s /usr/bin/git-shell git
Backup Best Practices
-
Automated Daily Backups: Schedule regular backups with cron
-
Off-site Storage: Store backups in remote location
# Sync to remote server
rsync -avz /backup/git/ remote-server:/backup/git/
- Test Restore Procedures: Regularly verify backups are recoverable
# Test restore
mkdir /tmp/restore-test
tar -xzf /backup/git/repositories_20240101.tar.gz -C /tmp/restore-test
-
Version Retention: Keep multiple backup versions (daily, weekly, monthly)
-
Document Recovery Process: Maintain clear restoration documentation
Performance Best Practices
- Repository Maintenance: Regular garbage collection and repacking
# Create maintenance script
#!/bin/bash
for repo in /home/git/repositories/*.git; do
cd "$repo"
git gc --aggressive --prune=now
git repack -ad
done
- Enable Caching: For web-based Git servers
# Nginx caching configuration
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=gitea_cache:10m max_size=1g;
location / {
proxy_cache gitea_cache;
proxy_cache_valid 200 10m;
proxy_pass http://localhost:3000;
}
- Resource Monitoring: Monitor CPU, memory, and disk usage
# Install monitoring tools
sudo apt install htop iotop nethogs
- Optimize Database: For Gitea/GitLab with database backends
-- PostgreSQL optimization
VACUUM ANALYZE;
REINDEX DATABASE gitea;
- SSD Storage: Use SSDs for repository storage when possible
Collaboration Best Practices
-
Standardized Workflows: Document Git workflows for team
-
Branch Protection: Implement branch protection rules
-
Code Review Process: Use merge/pull requests for all changes
-
Commit Message Standards: Enforce conventional commit messages with hooks
-
Documentation: Maintain comprehensive repository documentation
Operational Best Practices
- Monitoring and Alerting: Set up service monitoring
# Example monitoring script
#!/bin/bash
if ! systemctl is-active --quiet gitea; then
echo "Gitea is down!" | mail -s "Alert: Gitea Down" [email protected]
fi
- Log Rotation: Configure proper log rotation
# /etc/logrotate.d/gitea
/var/log/gitea/*.log {
daily
rotate 14
compress
delaycompress
notifempty
create 640 git git
sharedscripts
postrotate
systemctl reload gitea
endscript
}
-
Capacity Planning: Monitor growth and plan resources accordingly
-
Update Strategy: Test updates in staging before production
-
Disaster Recovery Plan: Document and test disaster recovery procedures
Conclusion
Setting up a private Git server provides complete control over your source code management infrastructure while offering flexibility, security, and cost savings. Whether you choose a simple SSH-based solution for personal projects, Gitolite for advanced access control, or full-featured platforms like Gitea or GitLab for team collaboration, this guide has provided comprehensive instructions for implementation.
The key to successful Git server management lies in choosing the right solution for your needs, implementing proper security measures, maintaining regular backups, and following operational best practices. Start with a simple setup and scale complexity as your requirements grow.
Your private Git server investment pays dividends through enhanced security, complete data control, and the flexibility to customize your development infrastructure exactly as needed. By following this guide's recommendations, you've established a solid foundation for reliable, secure version control that will serve your development needs for years to come.
Remember that Git server administration is an ongoing responsibility requiring regular maintenance, monitoring, and updates. Stay current with security patches, monitor performance metrics, test your backup procedures, and continuously refine your workflows to maintain a healthy, efficient Git infrastructure.
With your private Git server properly configured and maintained, you can focus on what matters most: writing great code and building exceptional software products with confidence in your version control foundation.


