Zum Inhalt

Self-Hosting Git & DevSecOps Platform: GitLab CE on Debian Linux (Omnibus, Container Registry & CI Runners)

Executive Summary: GitLab Community Edition (CE) is a complete, single-application Enterprise DevSecOps lifecycle platform bundling Git repository management (Gitaly), Merge Request code review, integrated CI/CD pipelines (.gitlab-ci.yml), a private OCI Container & Package Registry (PyPI, Maven, npm), and security scanning. Because default GitLab Omnibus settings are sized for 1,000+ user clusters (consuming 6 GB–8 GB of RAM at idle), this guide shows how to deploy GitLab CE on Debian Linux with memory-tuned Puma/Sidekiq/Prometheus parameters (~2.8 GB RAM), external reverse proxy TLS, and isolated GitLab CI Runners.

  • Complete DevSecOps Lifecycle


    Unify Git hosting, Merge Requests, Epics/Issues, Wiki, CI/CD pipelines, and Terraform state management in one self-hosted platform.

  • RAM-Optimized GITLAB_OMNIBUS_CONFIG


    Slash idle RAM consumption by 55% for small teams (1–25 users) by tuning puma['worker_processes'] and sidekiq['concurrency'].

  • Private OCI Container Registry


    Host your own private Docker/Podman container images (registry.nejat-hakan.de) and Python/Rust packages with zero Docker Hub rate limits.

  • Isolated gitlab-runner Executors


    Execute .gitlab-ci.yml build, pytest, and deployment stages on a dedicated runner container without granting CI jobs root access to the GitLab host.


1. GitLab CE Internal Architecture & Memory Footprint

Unlike lightweight single-binary Git servers (Forgejo), GitLab CE is a distributed microservice suite packaged inside the Omnibus container:

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    PROXY["🔒 Host Reverse Proxy<br/>gitlab.nejat-hakan.de (443)<br/>& SSH Port 2222"]

    subgraph Omnibus["🦊 GitLab CE Omnibus Stack (127.0.0.1:8929)"]
        WORK["GitLab Workhorse<br/>(Go Reverse Router)"]
        PUMA["Puma Rails App<br/>(Web UI & REST API)"]
        SIDE["Sidekiq Workers<br/>(Async CI & Webhooks)"]
        GIT["Gitaly RPC Daemon<br/>(Git Repository Storage)"]
        DATA["PostgreSQL + Redis<br/>(Internal State Store)"]
        WORK --> PUMA
        WORK --> GIT
        PUMA <--> DATA
        SIDE <--> DATA
    end

    RUN["🤖 GitLab CI Runner<br/>(Isolated Executor Node)"]

    PROXY <--> WORK
    RUN <-->|"Polls CI Jobs (HTTPS)"| WORK

2. Production compose.yaml (Memory-Tuned for 4–8 GB RAM Servers)

Notice how we map Git SSH to port 2222 (so your Debian host's own administrative SSH daemon remains untouched on port 22) and tune GITLAB_OMNIBUS_CONFIG for lean RAM consumption:

# File: /srv/containers/gitlab/compose.yaml
services:
  gitlab:
    image: docker.io/gitlab/gitlab-ce:latest
    container_name: gitlab-ce
    hostname: gitlab.nejat-hakan.de
    restart: unless-stopped
    shm_size: "256m"
    ports:
      - "127.0.0.1:8929:8929/tcp" # HTTP forwarded from host Reverse Proxy
      - "0.0.0.0:2222:22/tcp"     # Git over SSH (ssh://git@gitlab.nejat-hakan.de:2222/...)
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.nejat-hakan.de'
        gitlab_rails['gitlab_shell_ssh_port'] = 2222
        gitlab_rails['time_zone'] = 'Europe/Berlin'

        # Terminate TLS at upstream host reverse proxy (Caddy / Nginx / Traefik)
        nginx['listen_port'] = 8929
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = {
          "X-Forwarded-Proto" => "https",
          "X-Forwarded-Ssl" => "on"
        }

        # --- Memory Optimization for Small Teams (Saves ~3.5 GB RAM!) ---
        puma['worker_processes'] = 2
        puma['per_worker_max_memory_mb'] = 1024
        sidekiq['concurrency'] = 8
        postgresql['shared_buffers'] = "256MB"
        prometheus_monitoring['enable'] = false
    volumes:
      - /srv/containers/gitlab/config:/etc/gitlab:rw
      - /srv/containers/gitlab/logs:/var/log/gitlab:rw
      - /srv/containers/gitlab/data:/var/opt/gitlab:rw

Retrieve the auto-generated initial root password within 24 hours of first boot:

# File: /opt/gitlab-ops/get_initial_root_password.sh
docker exec -it gitlab-ce grep 'Password:' /etc/gitlab/initial_root_password

3. Registering an Isolated GitLab CI Runner (gitlab-runner)

Never run untrusted CI build jobs directly on your primary database or production host without isolation. Deploy gitlab/gitlab-runner and register it with a project or group Runner Authentication Token (glrt-...):

# File: /opt/gitlab-ops/register_ci_runner.sh
#!/usr/bin/env bash
set -euo pipefail

docker run -d \
    --name gitlab-runner \
    --restart unless-stopped \
    -v /srv/containers/gitlab-runner/config:/etc/gitlab-runner \
    -v /var/run/docker.sock:/var/run/docker.sock \
    docker.io/gitlab/gitlab-runner:alpine

docker exec -it gitlab-runner gitlab-runner register \
    --non-interactive \
    --url "https://gitlab.nejat-hakan.de" \
    --token "${GITLAB_RUNNER_AUTH_TOKEN}" \
    --executor "docker" \
    --docker-image "python:3.12-slim-bookworm" \
    --description "debian-docker-runner-01"

4. Automated Backup & Secret Key Protection

Back Up /etc/gitlab/gitlab-secrets.json Separately!

Running gitlab-backup create backs up all Git repositories, PostgreSQL tables, and CI artifacts—but for security reasons it excludes /etc/gitlab/gitlab-secrets.json and /etc/gitlab/gitlab.rb. Without gitlab-secrets.json, encrypted 2FA TOTP secrets and CI/CD runner variables in the database cannot be decrypted during a restore!

# File: /opt/gitlab-ops/backup_gitlab_full.sh
#!/usr/bin/env bash
set -euo pipefail

# 1. Create consistent application & repository backup archive in /var/opt/gitlab/backups
docker exec -t gitlab-ce gitlab-backup create CRON=1

# 2. Back up the critical encryption keys & configuration
docker exec -t gitlab-ce gitlab-ctl backup-etc

Where to Go Next

Compare heavyweight GitLab CE with featherweight Forgejo (< 100 MB RAM) and off-site BorgBackup:

  • Forgejo Lightweight Git Server


    Want Git + Pull Requests + GitHub-Actions-compatible CI/CD in < 100 MB of RAM instead of 3 GB? Choose Forgejo.

    Forgejo Guide

  • BorgBackup + Borgmatic


    Ship your /srv/containers/gitlab/data/backups and /etc/gitlab secrets to an encrypted off-site Borg repository.

    BorgBackup Guide

  • Automated Pytest Quality Gates


    Configure your GitLab CI Runner to enforce pytest and ruff checks on every Merge Request.

    Pytest Guide