Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Part I: The Problem

You have a 90KB configuration file written in Ruby. You need to inject parts of it as a single environment variable inside a YAML file. You have no schema, no validation, and no IntelliSense. What could possibly go wrong?

GITLAB_OMNIBUS_CONFIG Today

The standard way to configure GitLab in Docker is through the GITLAB_OMNIBUS_CONFIG environment variable. It accepts arbitrary Ruby code that gets evaluated when the container starts:

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.example.com'
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['proxy_set_headers'] = {
          "X-Forwarded-Proto" => "https",
          "X-Forwarded-Ssl" => "on"
        }
        gitlab_rails['smtp_enable'] = true
        gitlab_rails['smtp_address'] = 'smtp.example.com'
        gitlab_rails['smtp_port'] = 587
        gitlab_rails['smtp_user_name'] = 'notifications@example.com'
        gitlab_rails['smtp_password'] = 'changeme'
        gitlab_rails['smtp_authentication'] = 'login'
        gitlab_rails['smtp_enable_starttls_auto'] = true
        gitlab_rails['db_host'] = 'postgresql'
        gitlab_rails['db_password'] = 'changeme'
        redis['enable'] = false
        gitlab_rails['redis_host'] = 'redis'
        registry_external_url 'https://registry.example.com'
        letsencrypt['enable'] = false

This is Ruby code embedded in a YAML string. No quotes around it (it's a literal block scalar). No escaping. No validation until the container starts.

Typos are invisible

Change smtp_address to smtp_adress. The container starts fine. GitLab ignores the misspelled setting. You discover the problem 20 minutes later when the first email fails to send. There is no compile-time check, no startup warning, no error message that says "did you mean smtp_address?"

Version-specific settings are silently ignored

GitLab v17 introduced gitlab_kas['enable']. If you deploy v16 with that setting, it is silently ignored. If you remove it when upgrading to v17, you lose the feature. There is no way to know which settings exist in which version without reading the release notes for every minor version you've deployed.

Escaping is fragile

Ruby strings inside a YAML block scalar. If you need a single quote inside a Ruby string, you escape it with \'. If you need a hash with string keys, you use "key" => "value" (Ruby hash rocket syntax). If the YAML parser misinterprets a line, you get a cryptic error that points to the wrong location.

# This breaks because YAML interprets the colon after "https"
GITLAB_OMNIBUS_CONFIG: |
  external_url 'https://gitlab.example.com'
  gitlab_rails['smtp_address'] = 'smtp.example.com'
  # Nested hashes with => syntax, quotes inside quotes...
  gitlab_rails['ldap_servers'] = {
    'main' => {
      'label' => 'LDAP',
      'host' => 'ldap.example.com',
      'port' => 636,
      'uid' => 'sAMAccountName',
      'bind_dn' => 'CN=GitLab,OU=Service Accounts,DC=example,DC=com',
      'password' => 'p@ssw0rd!',
      'active_directory' => true,
      'base' => 'OU=Users,DC=example,DC=com'
    }
  }

No discoverability

How do you know what settings are available? You either:

  1. Browse the gitlab.rb.template on GitLab (90KB, 2700+ lines)
  2. Search the documentation (spread across dozens of pages)
  3. Ask someone who's done it before

There is no autocomplete, no parameter hints, no "go to definition."

The gitlab.rb File

Behind the GITLAB_OMNIBUS_CONFIG string is gitlab.rb -- the Omnibus configuration template. It is a commented-out Ruby file where each setting is documented with a default value:

############################
# GitLab Rails Settings
############################

##! Docs: https://docs.gitlab.com/omnibus/settings/smtp.html
# gitlab_rails['smtp_enable'] = true
# gitlab_rails['smtp_address'] = "smtp.server"
# gitlab_rails['smtp_port'] = 465
# gitlab_rails['smtp_user_name'] = "smtp user"
# gitlab_rails['smtp_password'] = "smtp password"
# gitlab_rails['smtp_domain'] = "example.com"
# gitlab_rails['smtp_authentication'] = "login"
# gitlab_rails['smtp_enable_starttls_auto'] = true

############################
# NGINX Settings
############################

# nginx['enable'] = true
# nginx['client_max_body_size'] = '250m'
# nginx['redirect_http_to_https'] = false
# nginx['listen_port'] = nil
# nginx['listen_https'] = nil
# nginx['proxy_set_headers'] = {}

The file contains 500+ settings organized across 55 Ruby module prefixes:

Category Prefix Example Settings
Core gitlab_rails SMTP, LDAP, object storage, database, Redis, backup
Web nginx Listen port, HTTPS, proxy headers, client body size
Registry registry Storage, auth, notifications
Pages gitlab_pages Access control, domains, artifacts
CI/CD gitlab_ci Builds directory
Shell gitlab_shell Auth timeout, log level
Workhorse gitlab_workhorse Listen settings
Gitaly gitaly Storage paths, auth token, concurrency
PostgreSQL postgresql Shared buffers, max connections, WAL
Redis redis Maxmemory, eviction policy
Prometheus prometheus Scrape configs, retention
Puma puma Workers, threads, memory limits
Sidekiq sidekiq Metrics, health checks
Mattermost mattermost Team, SQL, file, email settings
Let's Encrypt letsencrypt Enable, auto-renew, contact
KAS gitlab_kas Agent Server for Kubernetes
... ... 40+ more prefixes

Each prefix is a Ruby module. Settings are accessed with bracket notation: prefix['key'] = value. Some have nested keys: prefix['key1']['key2'] = value. Values can be strings, booleans, integers, arrays, or hashes.

Version Sprawl

GitLab releases roughly every month. Each release may add new settings, rename existing ones, or deprecate old ones. From version 9.2 (2017) to version 18.10 (2025), there are approximately 80 relevant stable releases. The configuration surface area grows with every version:

Version Range Notable Additions
9.x - 11.x Core settings: SMTP, LDAP, NGINX, PostgreSQL, Redis
12.x - 13.x Pages, Registry, Let's Encrypt, Mattermost
14.x - 15.x KAS (Kubernetes Agent), Praefect, Spamcheck
16.x - 17.x Consolidated object storage, Cells, AI features
18.x gitlab_backup_cli, omnibus_gitconfig

A setting like nginx['listen_port'] has existed since v9.2. A setting like gitlab_kas['enable'] only appeared in v17.0. A setting like high_availability['mountpoint'] was removed after v16.x. Tracking these changes by hand is impractical.

The Vision

What if configuring GitLab looked like this?

var config = new GitLabOmnibusConfigBuilder()
    .WithExternalUrl("https://gitlab.example.com")
    .WithNginx(n => n
        .WithListenPort(80)
        .WithListenHttps(false)
        .WithProxySetHeaders(new Dictionary<string, string?>
        {
            ["X-Forwarded-Proto"] = "https",
            ["X-Forwarded-Ssl"] = "on",
        }))
    .WithGitlabRails(r => r
        .WithSmtpEnable(true)
        .WithSmtpAddress("smtp.example.com")
        .WithSmtpPort(587)
        .WithSmtpAuthentication("login")
        .WithSmtpEnableStarttlsAuto(true)
        .WithDbHost("postgresql")
        .WithDbPassword("${GITLAB_DB_PASSWORD}"))
    .WithRedis(r => r
        .WithEnable(false))
    .WithRegistryExternalUrl("https://registry.example.com")
    .WithLetsencrypt(l => l
        .WithEnable(false))
    .Build();

Every setting has IntelliSense. Every builder method is decorated with [SinceVersion] and [UntilVersion] attributes that appear in XML doc comments. The compiler catches typos. The IDE shows exactly what settings are available for your target version. And the rendered Ruby output is always correctly escaped.

That is what GitLab.DockerCompose delivers -- and the next seven parts explain how.

⬇ Download