Skip to content

CRD Mental Model

The operator defines three Custom Resource Definitions. Each has a distinct role and scope.

The three CRDs

flowchart TD
    VM[GitlabVersionMap\ncluster-scoped · glvm\nname: default] -->|version resolution| GI
    P[GitlabProfile\ncluster-scoped · glprofile\nname: production] -->|spec.profile| GI
    GI[GitlabInstance\nnamespaced · gli\none per GitLab deployment]

    style VM fill:#374151,stroke:#6b7280,color:#e5e7eb
    style P fill:#374151,stroke:#6b7280,color:#e5e7eb
    style GI fill:#6366f1,stroke:#4f46e5,color:#fff

GitlabInstance (gli) — namespaced

One CR = one GitLab deployment.

This is the resource you create per tenant or environment. It carries everything specific to that deployment: the domain names, which edition (CE or EE), which version, which backends to use, the S3 and SMTP credentials references, backup schedule, and node placement.

The operator reconciles every GitlabInstance into a Flux HelmRelease plus the backend clusters and Secrets it needs.

apiVersion: k8s.bnerd.com/v1alpha1
kind: GitlabInstance
metadata:
  name: my-gitlab
  namespace: my-gitlab       ← lives in the same namespace as the GitLab workload

When to create one: whenever you want a new GitLab deployment. One namespace per instance is the recommended pattern.

GitlabVersionMap (glvm) — cluster-scoped

Maps GitLab version strings to Helm chart versions.

There is normally exactly one GitlabVersionMap in the cluster, named default. The operator always looks it up by that name.

The map supports: - Exact matches"17.11.7""8.11.8" - Prefix resolution"18" → the highest 18.x.y entry in the map - Aliases"latest" and "stable" point to specific version strings

You only need to update this resource when new GitLab versions are qualified for your platform. In most deployments this is managed by the platform team centrally.

apiVersion: k8s.bnerd.com/v1alpha1
kind: GitlabVersionMap
metadata:
  name: default    ← always this name; the operator looks it up by name

When to create one: once per cluster, during initial setup. Use examples/versionmap-default.yaml.

GitlabProfile (glprofile) — cluster-scoped

Bundles opinionated defaults for a class of deployments.

A GitlabProfile carries backend topology defaults (Postgres HA vs standalone, Redis topology, Elasticsearch node count) and a default backup schedule. Individual GitlabInstance resources reference a profile via spec.profile.

Profiles decouple how a class of GitLab should be deployed from what a specific instance deploys. A platform team can publish production, staging, and development profiles, and operators choose the right class.

apiVersion: k8s.bnerd.com/v1alpha1
kind: GitlabProfile
metadata:
  name: production   ← referenced via spec.profile: production

Profile defaults are the lowest-priority layer in value composition. An explicit setting on the GitlabInstance always wins over the profile default.

When to create one: once per deployment class. The examples/profile-production.yaml ships a production-ready example.

Scopes and naming

CRD Scope Required name Short name
GitlabInstance Namespaced any gli
GitlabVersionMap Cluster default (the operator hard-codes this lookup) glvm
GitlabProfile Cluster any (referenced by name from spec.profile) glprofile

How they relate

GitlabVersionMap/default      GitlabProfile/production
        │                              │
        │ version resolution           │ defaults
        ▼                              ▼
              GitlabInstance/my-gitlab
              ├── spec.version: "18"
              ├── spec.profile: production
              ├── spec.domains.gitlab: git.example.com
              ├── spec.postgres.managed: true
              └── ...

The GitlabInstance is the tenant-facing resource. The other two are platform-level resources that shape how instances are built, without being per-tenant.