Welcome!

Slide to unlock and explore

Slide to unlock

Command Palette

Search for a command to run...

0
Blog
PreviousNext

Self-Hosting GitLab - A Complete Guide

Step-by-step guide to deploying and managing your own GitLab instance for complete control over your development workflow.

Why Self-Host GitLab?

After working with various Git hosting platforms, I decided to self-host GitLab to gain complete control over my development infrastructure. Here's why it made sense for my projects:

  • Complete Control: Own your data and infrastructure
  • Unlimited Private Repositories: No storage or user limits
  • Custom CI/CD Runners: Deploy on your own hardware
  • Enhanced Privacy: Keep sensitive code on your own servers
  • Cost-Effective: One-time setup vs. recurring subscription fees
  • Learning Experience: Deep dive into DevOps and infrastructure

Prerequisites

Before starting, ensure you have:

  • A server with at least 4GB RAM (8GB recommended)
  • 20GB+ free disk space
  • Ubuntu 20.04 or 22.04 LTS
  • A domain name (optional but recommended)
  • SSH access to your server

Installation Process

System Preparation

First, update your system and install dependencies:

# Update system packages
sudo apt update && sudo apt upgrade -y
 
# Install required dependencies
sudo apt install -y curl openssh-server ca-certificates tzdata perl
 
# Install Postfix for email notifications
sudo apt install -y postfix
# Select "Internet Site" during installation

Add GitLab Repository

# Add GitLab package repository
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
 
# Note: Using gitlab-ee (Enterprise Edition) - free features are available

Install GitLab

# Set your domain name (or use IP address)
sudo EXTERNAL_URL="https://gitlab.example.com" apt install gitlab-ee
 
# This will take several minutes to complete

Initial Configuration

After installation, GitLab automatically configures itself. Access the web interface:

# Get the initial root password
sudo cat /etc/gitlab/initial_root_password
 
# Login at: https://gitlab.example.com
# Username: root
# Password: (from the file above)

Important: Change the root password immediately after first login!

SSL/TLS Configuration

Edit GitLab configuration:

sudo nano /etc/gitlab/gitlab.rb

Add these lines:

# Enable Let's Encrypt
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['admin@example.com']
 
# External URL with HTTPS
external_url 'https://gitlab.example.com'

Reconfigure GitLab:

sudo gitlab-ctl reconfigure

Manual SSL Certificate

If you have your own SSL certificate:

# In /etc/gitlab/gitlab.rb
external_url 'https://gitlab.example.com'
nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.example.com.crt"
nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.example.com.key"

Essential Configuration

Email Settings

Configure SMTP for notifications:

# In /etc/gitlab/gitlab.rb
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.gmail.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "your-email@gmail.com"
gitlab_rails['smtp_password'] = "your-app-password"
gitlab_rails['smtp_domain'] = "gmail.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
 
# From address
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'

Apply changes:

sudo gitlab-ctl reconfigure

Backup Configuration

Set up automated backups:

# In /etc/gitlab/gitlab.rb
gitlab_rails['backup_keep_time'] = 604800  # 7 days
gitlab_rails['backup_path'] = "/var/opt/gitlab/backups"

Create a backup cron job:

# Edit crontab
sudo crontab -e
 
# Add daily backup at 2 AM
0 2 * * * /opt/gitlab/bin/gitlab-backup create CRON=1

Manual backup:

sudo gitlab-backup create

Resource Optimization

For smaller servers, optimize memory usage:

# In /etc/gitlab/gitlab.rb
# Reduce Puma worker processes
puma['worker_processes'] = 2
 
# Reduce Sidekiq concurrency
sidekiq['max_concurrency'] = 10
 
# Reduce PostgreSQL connections
postgresql['shared_buffers'] = "256MB"
postgresql['max_worker_processes'] = 4

Setting Up CI/CD Runners

Install GitLab Runner

# Download and install
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install gitlab-runner
 
# Register runner
sudo gitlab-runner register
 
# Follow prompts:
# - GitLab instance URL: https://gitlab.example.com
# - Registration token: (from GitLab Admin → Runners)
# - Description: my-runner
# - Tags: docker, linux
# - Executor: docker
# - Default Docker image: alpine:latest

Configure Docker Executor

# Edit runner config
sudo nano /etc/gitlab-runner/config.toml

Example configuration:

[[runners]]
  name = "my-docker-runner"
  url = "https://gitlab.example.com"
  token = "your-token"
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = false
    volumes = ["/cache"]

Creating Your First Pipeline

Example .gitlab-ci.yml for a Node.js project:

stages:
  - build
  - test
  - deploy
 
build:
  stage: build
  image: node:18
  script:
    - npm install
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
 
test:
  stage: test
  image: node:18
  script:
    - npm install
    - npm test
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
 
deploy:
  stage: deploy
  image: alpine:latest
  script:
    - apk add --no-cache rsync openssh
    - rsync -avz dist/ user@server:/var/www/app/
  only:
    - main

Monitoring and Maintenance

Check GitLab Status

# Overall status
sudo gitlab-ctl status
 
# Detailed service status
sudo gitlab-rake gitlab:check
 
# Check configuration
sudo gitlab-rake gitlab:env:info

View Logs

# All logs
sudo gitlab-ctl tail
 
# Specific service logs
sudo gitlab-ctl tail nginx
sudo gitlab-ctl tail postgresql
sudo gitlab-ctl tail sidekiq

Update GitLab

# Backup before updating
sudo gitlab-backup create
 
# Update
sudo apt update
sudo apt install gitlab-ee
 
# Reconfigure
sudo gitlab-ctl reconfigure

Security Hardening

Firewall Configuration

# Allow SSH, HTTP, and HTTPS
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Rate Limiting

# In /etc/gitlab/gitlab.rb
gitlab_rails['rack_attack_git_basic_auth'] = {
  'enabled' => true,
  'ip_whitelist' => ["127.0.0.1"],
  'maxretry' => 10,
  'findtime' => 60,
  'bantime' => 3600
}

Two-Factor Authentication

Enable 2FA for all users:

  1. Go to Admin Area → Settings → General
  2. Expand "Sign-in restrictions"
  3. Enable "Require all users to set up Two-factor authentication"

Performance Tuning

PostgreSQL Optimization

# In /etc/gitlab/gitlab.rb
postgresql['shared_buffers'] = "256MB"
postgresql['work_mem'] = "16MB"
postgresql['maintenance_work_mem'] = "64MB"
postgresql['effective_cache_size'] = "1GB"
postgresql['checkpoint_completion_target'] = 0.9

Redis Optimization

# In /etc/gitlab/gitlab.rb
redis['maxmemory'] = "256mb"
redis['maxmemory_policy'] = "allkeys-lru"

Troubleshooting Common Issues

502 Bad Gateway

# Check if all services are running
sudo gitlab-ctl status
 
# Restart GitLab
sudo gitlab-ctl restart
 
# Check Puma/Unicorn logs
sudo gitlab-ctl tail puma

Out of Memory

# Check memory usage
free -h
 
# Reduce worker processes (see Resource Optimization)
# Consider upgrading server RAM

Database Issues

# Check PostgreSQL
sudo gitlab-rake gitlab:db:configure
 
# Run database migrations
sudo gitlab-rake db:migrate

Backup and Recovery

Restore from Backup

# Stop processes that connect to database
sudo gitlab-ctl stop puma
sudo gitlab-ctl stop sidekiq
 
# Restore backup
sudo gitlab-backup restore BACKUP=1234567890_2024_03_15
 
# Restart GitLab
sudo gitlab-ctl restart
sudo gitlab-rake gitlab:check SANITIZE=true

Advanced Features

Container Registry

Enable Docker container registry:

# In /etc/gitlab/gitlab.rb
registry_external_url 'https://registry.gitlab.example.com'
gitlab_rails['registry_enabled'] = true

GitLab Pages

Enable static site hosting:

# In /etc/gitlab/gitlab.rb
pages_external_url "https://pages.example.com"
gitlab_pages['enable'] = true

Cost Analysis

My Setup:

  • VPS: $20/month (4GB RAM, 80GB SSD)
  • Domain: $12/year
  • Total: ~$252/year

Compared to GitLab SaaS:

  • Premium: $29/user/month = $348/year
  • Savings: ~$100/year (plus unlimited users/repos)

Lessons Learned

  1. Start with more RAM: 8GB is comfortable, 4GB is minimum
  2. Regular backups are essential: Automated daily backups saved me
  3. Monitor disk space: GitLab artifacts can grow quickly
  4. Update regularly: Security patches are critical
  5. Document everything: Keep notes on customizations

Useful Commands Reference

# Reconfigure GitLab
sudo gitlab-ctl reconfigure
 
# Restart all services
sudo gitlab-ctl restart
 
# Status check
sudo gitlab-ctl status
 
# Create backup
sudo gitlab-backup create
 
# Check configuration
sudo gitlab-rake gitlab:check
 
# Rails console (for advanced debugging)
sudo gitlab-rails console

Conclusion

Self-hosting GitLab has been incredibly rewarding. I now have complete control over my development infrastructure, unlimited private repositories, and a powerful CI/CD platform running on my own hardware.

The initial setup takes time, but the long-term benefits in cost savings, privacy, and learning make it worthwhile. Start small, monitor closely, and scale as needed.

Have questions about self-hosting GitLab? Connect with me on GitHub or LinkedIn!