Installation¶
This page covers installing the cluster prerequisites and the bnerd-gitlab-operator itself.
Cluster prerequisites¶
The operator depends on several external controllers. Install them once per cluster before installing the operator. All are optional depending on which features you use, with one hard requirement: Flux.
1. Flux CD (required)¶
The operator emits HelmRelease (helm.toolkit.fluxcd.io/v2) and HelmRepository (source.toolkit.fluxcd.io/v1beta2) resources. Flux's source-controller and helm-controller must be running to apply them.
Flux >= 2.3 required
The operator emits the GA helm.toolkit.fluxcd.io/v2 HelmRelease API. This API was promoted in Flux 2.3 and the v2beta1 form was removed in Flux 2.4. You must run Flux >= 2.3 on the cluster — earlier versions do not serve the v2 API and will not reconcile the emitted HelmRelease.
Install Flux (2.3 or later):
Verify:
2. Percona PostgreSQL Operator (for managed Postgres)¶
Required when any GitlabInstance uses spec.postgres.managed: true. Install the Percona PG Operator into its own namespace:
# Via Helm
helm repo add percona https://percona.github.io/percona-helm-charts/
helm install pg-operator percona/pg-operator \
--namespace pgo \
--create-namespace
Verify:
3. OT-Container-Kit redis-operator (for managed Redis)¶
Required when any GitlabInstance uses spec.redis.managed: true.
helm repo add ot-helm https://ot-container-kit.github.io/helm-charts/
helm install redis-operator ot-helm/redis-operator \
--namespace ot-operators \
--create-namespace
Verify:
OT-Container-Kit v0.25.0 readiness quirk
OT-Container-Kit v0.25.0 ships an empty RedisStatus struct; the status.readyReplicas field is pruned by the API server. The operator detects this and falls back to inspecting the underlying StatefulSet's readyReplicas instead. No configuration needed — this is handled transparently.
4. ECK — Elastic Cloud on Kubernetes (for managed Elasticsearch, EE only)¶
Required only when deploying GitLab Enterprise Edition with managed Elasticsearch (spec.elasticsearch.managed: true).
kubectl create -f https://download.elastic.co/downloads/eck/2.12.1/crds.yaml
kubectl apply -f https://download.elastic.co/downloads/eck/2.12.1/operator.yaml
Verify:
5. cert-manager (required — cluster infrastructure)¶
The operator emits installCertmanager: false on every GitlabInstance: it never
installs the GitLab chart's bundled cert-manager. cert-manager is expected to
already exist on the cluster as shared infrastructure (typically Flux-managed).
This is deliberate. The GitLab chart's bundled cert-manager templates fixed-name,
cluster-scoped resources (the cert-manager CRDs, a GatewayClass, and
ValidatingAdmissionPolicy/Binding objects). Those can have exactly one Helm
owner per cluster, ever — so if the chart installed them, a second GitlabInstance
(or any cluster that already runs cert-manager) would be refused by Helm with
exists and cannot be imported into the current release: invalid ownership
metadata. Leaving cert-manager as a cluster prerequisite avoids that collision.
Install it once per cluster (any supported method), for example:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
Verify:
Provision Issuers/ClusterIssuers (e.g. Let's Encrypt) at the cluster level and
reference them from spec.ingress.annotations — see the
Ingress guide.
Installing the operator¶
helm install bnerd-gitlab-operator charts/bnerd-gitlab-operator \
--namespace bnerd-gitlab-operator \
--create-namespace \
--set image.tag=0.1.0-beta
The Helm chart installs:
- The operator
Deploymentwith leader election enabled - A
ServiceAccountandClusterRolewith the minimum RBAC permissions - All three CRDs (
GitlabInstance,GitlabVersionMap,GitlabProfile) fromcharts/bnerd-gitlab-operator/crds/ - The default
GitlabVersionMap(versionMap.installDefault: true, the chart default) — see Applying the default GitlabVersionMap below
Verify the operator is running:
# Build and apply via kustomize
make deploy IMG=git.bnerd.net/cloud/operators/bnerd-gitlab-operator:0.1.0-beta
This applies the manifests in config/manager/ using kustomize and sets the operator image. CRDs from config/crd/bases/ are applied first.
Useful for development or when you want to run directly from a repository checkout without packaging a Helm chart.
Applying the default GitlabVersionMap¶
The GitlabVersionMap named default is required for version resolution — without
it, every GitlabInstance reconcile fails to resolve spec.version and the instance
never progresses past an empty/statusless state.
Installed automatically as part of the chart (versionMap.installDefault: true,
the default). No extra step needed. Verify it's there:
Set versionMap.installDefault: false if you manage your own GitlabVersionMap/default
out-of-band, then apply your own copy.
The default map covers the GitLab 17.x → 19.x upgrade path, including all required upgrade stops. See Version Map Reference for the full ladder.
Verifying the installation¶
# Operator pod
kubectl get pods -n bnerd-gitlab-operator
# CRDs registered
kubectl get crd | grep k8s.bnerd.com
# Default version map
kubectl get glvm default
# Operator logs (no errors expected)
kubectl logs -n bnerd-gitlab-operator deploy/bnerd-gitlab-operator --tail=50
RBAC summary¶
The operator requires cluster-level read access to CRDs (for capability gating) and namespace-level create/update/delete access for Secrets, PerconaPGCluster, Redis CRs, ECK Elasticsearch, and Flux HelmRelease/HelmRepository resources. The full role is in config/rbac/role.yaml.
The operator runs with a non-root UID (65532) and a read-only root filesystem by default.