Install and configure GitLab CE with CI/CD runners and backup automation

Intermediate 45 min Apr 01, 2026 20 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up a complete self-hosted GitLab CE instance with SSL encryption, Docker-based CI/CD runners, automated backup system, and security hardening for production workloads.

Prerequisites

  • Minimum 4GB RAM (8GB recommended)
  • 2 CPU cores
  • 50GB available disk space
  • Domain name with DNS pointing to server
  • Root or sudo access

What this solves

GitLab Community Edition provides a complete DevOps platform for source code management, CI/CD pipelines, and project collaboration. This tutorial shows you how to install GitLab CE with SSL certificates, configure GitLab Runner with Docker executor for automated testing and deployment, and implement automated backup procedures with monitoring.

Prerequisites

System requirements: Minimum 4GB RAM (8GB recommended), 2 CPU cores, 50GB disk space, and a registered domain name pointing to your server.

Step-by-step installation

Update system packages and install dependencies

Start by updating your system and installing required packages for GitLab CE installation.

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl openssh-server ca-certificates tzdata perl postfix
sudo dnf update -y
sudo dnf install -y curl openssh-server ca-certificates tzdata perl postfix

Add GitLab CE repository

Download and add the official GitLab repository to your system package manager.

curl -fsSL https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
curl -fsSL https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash

Install GitLab CE

Install GitLab Community Edition with your domain name configured for SSL setup.

sudo EXTERNAL_URL="https://gitlab.example.com" apt install -y gitlab-ce
sudo EXTERNAL_URL="https://gitlab.example.com" dnf install -y gitlab-ce

Configure SSL with Let's Encrypt

Enable automatic SSL certificate management using Let's Encrypt for secure HTTPS access.

external_url 'https://gitlab.example.com'
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['admin@example.com']
letsencrypt['auto_renew'] = true
letsencrypt['auto_renew_hour'] = 2
letsencrypt['auto_renew_minute'] = 30
letsencrypt['auto_renew_day_of_month'] = "*/7"

Configure GitLab settings

Set up basic GitLab configuration including email notifications, backup retention, and performance settings.

gitlab_rails['gitlab_email_enabled'] = true
gitlab_rails['gitlab_email_from'] = 'gitlab@example.com'
gitlab_rails['gitlab_email_display_name'] = 'GitLab'
gitlab_rails['backup_keep_time'] = 604800
gitlab_rails['backup_path'] = "/var/opt/gitlab/backups"
gitlab_rails['backup_upload_connection'] = {
  'provider' => 'Local',
  'local_root' => '/opt/backups'
}
puma['worker_processes'] = 2
puma['min_threads'] = 4
puma['max_threads'] = 4
postgresql['shared_buffers'] = "512MB"
postgresql['max_connections'] = 200

Apply configuration and start GitLab

Reconfigure GitLab to apply all settings and start all services.

sudo gitlab-ctl reconfigure
sudo gitlab-ctl status

Configure firewall rules

Open necessary ports for GitLab web interface, SSH access, and container registry.

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Install Docker for GitLab Runner

Install Docker to provide container execution environment for CI/CD pipelines.

sudo apt install -y docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker gitlab-runner
sudo dnf install -y docker
sudo systemctl enable --now docker
sudo usermod -aG docker gitlab-runner

Install GitLab Runner

Install GitLab Runner to execute CI/CD jobs defined in your repositories.

curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt install -y gitlab-runner
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
sudo dnf install -y gitlab-runner

Register GitLab Runner

Register the runner with your GitLab instance using a registration token from GitLab admin area.

sudo gitlab-runner register \
  --non-interactive \
  --url "https://gitlab.example.com" \
  --registration-token "your-registration-token" \
  --executor "docker" \
  --docker-image alpine:latest \
  --description "docker-runner" \
  --tag-list "docker,linux" \
  --run-untagged="true" \
  --locked="false" \
  --access-level="not_protected"
Note: Get the registration token from GitLab Admin Area > Runners > Register an instance runner.

Configure GitLab Runner for Docker

Optimize the runner configuration for Docker executor with caching and security settings.

concurrent = 2
check_interval = 3

[[runners]]
  name = "docker-runner"
  url = "https://gitlab.example.com"
  token = "runner-token-here"
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "alpine:latest"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    extra_hosts = ["host.docker.internal:host-gateway"]
    shm_size = 0
  [runners.cache]
    Type = "local"
    Path = "/tmp/gitlab-runner-cache"
    Shared = true

Set up automated backups

Create a comprehensive backup script that includes GitLab data, configuration files, and SSL certificates.

#!/bin/bash
set -e

BACKUP_DIR="/opt/backups"
DATE=$(date +"%Y%m%d_%H%M%S")
RETENTION_DAYS=7

Create backup directory

mkdir -p $BACKUP_DIR

Create GitLab backup

echo "Creating GitLab backup..." gitlab-backup create BACKUP_DIR=$BACKUP_DIR

Backup configuration files

echo "Backing up configuration files..." tar -czf $BACKUP_DIR/gitlab-config-$DATE.tar.gz \ /etc/gitlab/gitlab.rb \ /etc/gitlab/gitlab-secrets.json \ /etc/ssh/ \ /etc/letsencrypt/ 2>/dev/null || true

Backup GitLab Runner configuration

cp /etc/gitlab-runner/config.toml $BACKUP_DIR/gitlab-runner-config-$DATE.toml

Clean old backups

find $BACKUP_DIR -name "*.tar" -mtime +$RETENTION_DAYS -delete find $BACKUP_DIR -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete find $BACKUP_DIR -name "*.toml" -mtime +$RETENTION_DAYS -delete echo "Backup completed successfully" echo "Backup location: $BACKUP_DIR" ls -lah $BACKUP_DIR/ | tail -5

Make backup script executable and schedule

Set proper permissions and create a cron job for automated daily backups.

sudo chmod 755 /opt/gitlab-backup.sh
sudo chown root:root /opt/gitlab-backup.sh
sudo mkdir -p /opt/backups
sudo chown git:git /opt/backups
sudo chmod 755 /opt/backups
Never use chmod 777. It gives every user on the system full access to your files. Instead, use specific permissions like 755 for executables and proper ownership.

Configure automated backup scheduling

Set up daily backups at 2 AM with email notifications on failure.

sudo crontab -e
0 2   * /opt/gitlab-backup.sh >> /var/log/gitlab-backup.log 2>&1 || echo "GitLab backup failed on $(hostname)" | mail -s "Backup Failure" admin@example.com

Create backup restore script

Prepare a restore script for disaster recovery scenarios.

#!/bin/bash
set -e

if [ "$#" -ne 1 ]; then
    echo "Usage: $0 "
    echo "Example: $0 20240115_020001"
    exit 1
fi

BACKUP_TIMESTAMP=$1
BACKUP_DIR="/opt/backups"

echo "Stopping GitLab services..."
gitlab-ctl stop puma
gitlab-ctl stop sidekiq

echo "Restoring GitLab data..."
gitlab-backup restore BACKUP=$BACKUP_TIMESTAMP BACKUP_DIR=$BACKUP_DIR

echo "Restoring configuration files..."
if [ -f "$BACKUP_DIR/gitlab-config-$BACKUP_TIMESTAMP.tar.gz" ]; then
    tar -xzf "$BACKUP_DIR/gitlab-config-$BACKUP_TIMESTAMP.tar.gz" -C /
fi

echo "Reconfiguring GitLab..."
gitlab-ctl reconfigure
gitlab-ctl restart

echo "Restore completed successfully"
sudo chmod 755 /opt/gitlab-restore.sh
sudo chown root:root /opt/gitlab-restore.sh

Configure monitoring and health checks

Set up basic monitoring to track GitLab performance and runner status.

#!/bin/bash
set -e

HEALTH_URL="https://gitlab.example.com/-/health"
RUNNER_STATUS=$(gitlab-runner status 2>&1 || echo "runner not running")
GITLAB_STATUS=$(gitlab-ctl status | grep -c "run:" || echo "0")

echo "=== GitLab Health Check $(date) ==="
echo "GitLab Services Running: $GITLAB_STATUS"
echo "Runner Status: $RUNNER_STATUS"

Check HTTP health endpoint

if curl -sf "$HEALTH_URL" > /dev/null; then echo "HTTP Health Check: OK" else echo "HTTP Health Check: FAILED" echo "GitLab may be experiencing issues" | mail -s "GitLab Health Alert" admin@example.com fi

Check disk space

DISK_USAGE=$(df /var/opt/gitlab | tail -1 | awk '{print $5}' | sed 's/%//') if [ "$DISK_USAGE" -gt 85 ]; then echo "WARNING: Disk usage is $DISK_USAGE%" echo "GitLab disk usage is $DISK_USAGE% on $(hostname)" | mail -s "GitLab Disk Alert" admin@example.com else echo "Disk Usage: $DISK_USAGE%" fi
sudo chmod 755 /opt/gitlab-health-check.sh
sudo chown root:root /opt/gitlab-health-check.sh

Schedule health monitoring

Run health checks every 15 minutes to detect issues early.

/15    * /opt/gitlab-health-check.sh >> /var/log/gitlab-health.log 2>&1

Apply security hardening

Configure security settings including rate limiting, session timeouts, and access controls.

# Security settings
gitlab_rails['gitlab_shell_ssh_port'] = 22
gitlab_rails['rate_limit_requests_per_period'] = 10
gitlab_rails['rate_limit_period'] = 60
gitlab_rails['session_expire_delay'] = 10080
gitlab_rails['password_authentication_enabled_for_web'] = true
gitlab_rails['password_authentication_enabled_for_git'] = true

Disable unused features

gitlab_rails['gravatar_enabled'] = false gitlab_rails['usage_ping_enabled'] = false prometheus_monitoring['enable'] = false alertmanager['enable'] = false

Configure fail2ban integration

nginx['real_ip_trusted_addresses'] = ['127.0.0.1/32'] nginx['real_ip_header'] = 'X-Real-IP' nginx['real_ip_recursive'] = 'on'

Apply final configuration

Reconfigure GitLab to apply all security and monitoring settings.

sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
sudo systemctl restart gitlab-runner

Verify your setup

Confirm that GitLab CE, runners, and backup systems are working correctly.

# Check GitLab services status
sudo gitlab-ctl status

Verify GitLab Runner registration

sudo gitlab-runner list

Test SSL certificate

curl -I https://gitlab.example.com

Check backup directory

ls -la /opt/backups/

Test backup script

sudo /opt/gitlab-backup.sh

View GitLab logs

sudo gitlab-ctl tail

Check Docker functionality

sudo docker run --rm hello-world

Access your GitLab instance at https://gitlab.example.com and log in with the root account. The initial password is stored in:

sudo cat /etc/gitlab/initial_root_password

Performance optimization

Memory optimization: For systems with limited RAM, consider reducing worker processes and adjusting PostgreSQL settings in gitlab.rb.
Setting4GB RAM8GB RAM16GB+ RAM
puma worker_processes248
postgresql shared_buffers256MB512MB1GB
gitlab_rails backup_keep_time259200 (3 days)604800 (7 days)1209600 (14 days)

Common issues

SymptomCauseFix
502 Bad Gateway errorGitLab services not startedsudo gitlab-ctl restart
Runner jobs fail immediatelyDocker permission issuessudo usermod -aG docker gitlab-runner && sudo systemctl restart gitlab-runner
SSL certificate issuesLet's Encrypt rate limitsCheck /var/log/gitlab/nginx/error.log and wait before retry
Backup fails with permission deniedIncorrect backup directory ownershipsudo chown git:git /opt/backups && sudo chmod 755 /opt/backups
High memory usageDefault settings too highReduce puma workers and PostgreSQL buffers in gitlab.rb
Repository clone failsSSH key or firewall issuesCheck SSH port 22 is open and keys are properly configured

Next steps

Automated install script

Run this to automate the entire setup

#gitlab-ce #gitlab-runner #ci-cd-pipeline #gitlab-backup #self-hosted-gitlab #gitlab-ssl #docker-executor #git-repository

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer