Skip to content

GitLab Platform Module

Terraform module for deploying GitLab CI/CD platform to Kubernetes. Provides a complete DevOps platform including source control, CI/CD pipelines, container registry, and package registry with Auth0 SSO and MinIO object storage integration.

Note: GitLab only supports AMD64 architecture. This module is automatically skipped on ARM64 clusters.

Architecture

flowchart TB
    subgraph external [External]
        User[Developer]
        Auth0[Auth0 IdP]
    end

    subgraph k8s [Kubernetes Cluster]
        subgraph ingress [Edge Layer]
            Gateway[Istio Gateway<br/>gitlab and registry hosts]
        end

        subgraph ns [Namespace: gitlab]
            subgraph webservice [Webservice]
                WebUI[GitLab UI]
                API[GitLab API]
            end

            subgraph sidekiq [Background Jobs]
                Sidekiq[Sidekiq Workers]
            end

            subgraph gitaly [Git Storage]
                Gitaly[Gitaly]
                GitalyPVC[(Gitaly PVC)]
            end

            subgraph registry [Container Registry]
                Registry[Registry]
            end

            subgraph runner [CI/CD]
                Runner[GitLab Runner]
            end

            subgraph data [Data Stores]
                PostgreSQL[(CloudNativePG - PostgreSQL 17)]
                Valkey[(Valkey)]
                PGSQLPVC[(PostgreSQL PVC)]
                ValkeyPVC[(Valkey PVC)]
            end

            Shell[GitLab Shell]
            Toolbox[Toolbox]
        end

        subgraph minio_ns [Namespace: minio-tenant]
            MinIO[(MinIO Object Storage)]
        end
    end

    User -->|HTTPS| Gateway
    Gateway --> WebUI
    Gateway --> Registry
    User -->|SSH| Shell

    WebUI --> PostgreSQL
    WebUI --> Valkey
    WebUI --> Gitaly
    Sidekiq --> PostgreSQL
    Sidekiq --> Valkey
    Registry --> PostgreSQL

    Registry --> MinIO
    Toolbox -->|backups| MinIO
    Runner -->|cache| MinIO

    WebUI -->|OIDC| Auth0

CI/CD Pipeline Flow

sequenceDiagram
    participant Dev as Developer
    participant GitLab as GitLab Webservice
    participant Runner as GitLab Runner
    participant Registry as Container Registry
    participant MinIO as MinIO Storage

    Dev->>GitLab: Push code
    GitLab->>Runner: Trigger pipeline
    Runner->>MinIO: Restore cache
    Runner->>Runner: Execute jobs
    Runner->>Registry: Push container image
    Runner->>MinIO: Save artifacts
    Runner->>MinIO: Update cache
    Runner-->>GitLab: Report status
    GitLab-->>Dev: Pipeline complete

Resources Created

Namespace and Secrets

  • kubernetes_namespace.gitlab - Dedicated namespace
  • kubernetes_secret.initial_root_password - Root admin password
  • kubernetes_secret.valkey_password - Valkey authentication, key named default per the ACL user
  • kubernetes_secret.postgresql_password - PostgreSQL credentials
  • kubernetes_secret.rails_secret - Rails encryption keys
  • kubernetes_secret.gitlab_shell_secret - Shell authentication
  • kubernetes_secret.gitaly_secret - Gitaly authentication
  • kubernetes_secret.gitlab_shell_host_keys - SSH host keys
  • kubernetes_secret.gitlab_object_store_connection - MinIO connection
  • kubernetes_secret.gitlab_registry_httpsecret - Registry secret
  • kubernetes_secret.gitlab_registry_storage_secret - Registry MinIO config
  • kubernetes_secret.gitlab_runner_s3_access - Runner S3 credentials
  • kubernetes_secret.gitlab_toolbox_s3cmd - Backup toolbox config
  • kubernetes_secret.gitlab_auth0_provider - Auth0 OIDC config

Storage

  • kubernetes_storage_class_v1.gitlab_backup_ephemeral - Delete-reclaim class for the nightly backup's ephemeral volume

Helm Release

  • helm_release.gitlab - GitLab Helm chart
  • helm_release.cloudnative_pg - CloudNativePG operator
  • helm_release.valkey - Valkey, replacing the removed bundled Redis
  • kubectl_manifest.gitlab_postgres - CNPG Cluster, replacing the removed bundled PostgreSQL

Gateway API

The chart's Ingresses for the web and registry hosts are disabled per component, not through global.ingress.enabled, which is chart-wide and also drives defaults other components read.

  • kubectl_manifest.web_listener - ListenerSet contributing the GitLab HTTPS listener to the shared Gateway
  • kubectl_manifest.registry_listener - ListenerSet contributing the registry HTTPS listener
  • kubectl_manifest.web_certificate - DNS-01 Certificate for the GitLab host, reusing the Secret name the Ingress used
  • kubectl_manifest.registry_certificate - DNS-01 Certificate for the registry host, reusing the Secret name the Ingress used
  • kubectl_manifest.web_route - HTTPRoute to gitlab-webservice-default:8181, which is workhorse
  • kubectl_manifest.registry_route - HTTPRoute to gitlab-registry:5000

Neither route has an AuthorizationPolicy and neither may gain one. GitLab runs its own session login and the registry authenticates with JWT bearer tokens issued by GitLab, so an ext_authz login redirect would break git over HTTPS, the API, CI and every docker pull.

Databases

Chart 10.0 removed the bundled PostgreSQL, Redis and MinIO subcharts, and the chart refuses to render if postgresql.install or redis.install is present and truthy. MinIO was already external via stage2/minio-object-storage; PostgreSQL and Redis moved here.

GitLab 19.0 sets MINIMUM_POSTGRES_VERSION = 17, so imageName is pinned deliberately: the CNPG operator's own default is an 18.x image, and omitting the field silently provisions PostgreSQL 18.

Service Endpoint Credentials
PostgreSQL gitlab-pg-rw.gitlab.svc.cluster.local:5432 secret postgresql-password, key postgresql-password
Registry metadata DB same host, database registry secret registry-database-password
Valkey gitlab-valkey.gitlab.svc.cluster.local:6379 secret valkey-password, key default

The Valkey secret key is default rather than password because the chart looks up auth.aclUsers.<user>.passwordKey, which defaults to the username, and AUTH <password> with no username authenticates as the default ACL user.

The CNPG Cluster

Defined in postgres-cluster.tf, which carries the rationale for the monolith import type and the pinned imageName. bootstrap.initdb.import runs only once, at bootstrap: re-importing means deleting the Cluster and its PVC first. externalClusters is consulted only during that import and is inert afterward.

postImportApplicationSQL is unsupported with monolith, so extensions must be added by hand after an import.

Operating it

amcheck is required by GitLab but absent from a fresh monolith import. Recreate it after any re-import:

kubectl -n gitlab exec gitlab-pg-1 -- psql -U postgres -d gitlabhq_production \
  -c "CREATE EXTENSION IF NOT EXISTS amcheck;"

Before re-importing, scale sidekiq, webservice and registry to zero. Confirm with pg_stat_activity, not the pod list. Then delete the Cluster and re-apply.

backup-utility does not cover the registry database: Rails' database.yml declares only the main and ci connections, both on gitlabhq_production, and gitlab-backup iterates those connections. Dump registry separately before any migration. Restores also abort on a version mismatch. lib/backup/restore/preconditions.rb compares the backup's GitLab version to the running one by exact string equality, so a backup is only usable against the exact version that produced it.

Variables

Host Configuration

Name Description Default
gitlab_global_hosts_domain Base domain for GitLab chrislee.local
gitlab_global_hosts_host_suffix Subdomain suffix ""
gitlab_global_hosts_https Use HTTPS true
gitlab_global_hosts_external_ip External LoadBalancer IP ""
gitlab_time_zone GitLab application timezone Australia/Melbourne

Ingress Configuration

Name Description Default
gitlab_global_ingress_provider Ingress provider. No component renders an Ingress, so this only feeds the chart's own naming helpers nginx
gitlab_global_ingress_class Ingress class, inert for the same reason nginx
gitlab_global_ingress_enable_tls Enable TLS true
gitlab_certmanager_issuer_email Let's Encrypt email ""
istio_gateway_name Shared Istio Gateway both listeners are added to public
istio_gateway_namespace Namespace of that Gateway, named by the ListenerSet parentRef istio-ingress

The gitlab_global_ingress_* settings apply only to chart-wide defaults, since the web and registry hosts are served by the Gateway.

MinIO Object Storage

Name Description Default
gitlab_minio_host MinIO hostname minio.chrislee.local
gitlab_minio_endpoint MinIO endpoint URL http://minio.chrislee.local
gitlab_minio_use_https Use HTTPS for MinIO False
gitlab_minio_access_key MinIO access key minio-user
gitlab_minio_secret_key MinIO secret key (from minio module, sensitive)

Persistence

Name Description Default
gitlab_persistence_storage_class_name Storage class longhorn
gitlab_toolbox_backups_cron_persistence_size Backup staging volume size, per-pod ephemeral 30Gi
gitlab_toolbox_persistence_size Toolbox PVC size 20Gi
gitlab_postgres_storage_size CloudNativePG cluster data volume size 20Gi
gitlab_valkey_persistence_size Valkey data volume size 2Gi
gitlab_gitaly_persistence_size Gitaly PVC size 50Gi

CI/CD Runner

Name Description Default
gitlab_runner_authentication_token Runner auth token "" (sensitive)

Auth0 SSO

Name Description Default
gitlab_auth0_client_id Auth0 client ID ""
gitlab_auth0_client_secret Auth0 client secret "" (sensitive)
gitlab_auth0_domain Auth0 domain chrislee.auth0.com

Usage

1. Configure Auth0 Application

  1. Create a Regular Web Application in Auth0
  2. Set Allowed Callback URLs: https://gitlab.chrislee.local/users/auth/auth0/callback
  3. Copy Client ID and Client Secret

2. Configure Variables

TF_VAR_gitlab_global_hosts_domain="chrislee.local"
TF_VAR_gitlab_auth0_domain="your-tenant.auth0.com"
TF_VAR_gitlab_auth0_client_id="your-client-id"
TF_VAR_gitlab_auth0_client_secret="your-client-secret"

3. Deploy

task stage2:terraform:apply

4. Get Root Password

kubectl -n gitlab get secret initial-root-password -o jsonpath="{.data.password}" | base64 -d

5. Access GitLab

Navigate to https://gitlab.chrislee.local

  • Username: root
  • Password: (from step 4)

6. Configure Runner Token

  1. Go to Admin > CI/CD > Runners
  2. Create a new instance runner
  3. Copy the authentication token
  4. Set TF_VAR_gitlab_runner_authentication_token
  5. Run task stage2:terraform:apply

Helm Chart

Property Value
Repository https://charts.gitlab.io/
Chart gitlab

Endpoints

Service URL
GitLab UI https://gitlab.chrislee.local
Container Registry https://registry.chrislee.local
SSH ssh://git@gitlab.chrislee.local:22

MinIO Buckets Used

Bucket Purpose
registry Container images
git-lfs Git LFS objects
runner-cache CI runner cache
gitlab-uploads User uploads
gitlab-artifacts CI artifacts
gitlab-backups Automated backups
gitlab-packages Package registry

Backup and Restore

Manual Backup

kubectl -n gitlab exec -it $(kubectl -n gitlab get pod -l app=toolbox -o name) -- backup-utility

Scheduled Backups

Backups are configured to run via CronJob and stored in MinIO gitlab-backups bucket.

Each run stages its tarball on its own generic ephemeral volume, garbage-collected with the pod, so no shared claim can wedge the schedule. That volume uses a dedicated Delete-reclaim StorageClass (backup-storageclass.tf); the shared longhorn class is Retain and would strand a volume nightly.

Jobs are capped at 3h (activeDeadlineSeconds: 10800), generous against a healthy run. Without a deadline a stuck backup runs indefinitely, and concurrencyPolicy: Replace then deletes it on the next schedule, leaving no failed job behind to notice.

Troubleshooting

Registry Storage Full

Expand MinIO PVCs:

kubectl edit -n minio-tenant pvc data0-minio-tenant-pool-0-0

Check Component Status

kubectl -n gitlab get pods
kubectl -n gitlab logs deployment/gitlab-webservice-default

References