GitOps with GitLab

Learn how GitLab integrates Flux for GitOps. From OCI artifact sources and immediate reconciliation to drift remediation and migration from legacy GitOps, build pull-based deployments that keep your cluster in sync.

GitOps Flux GitLab
Why GitOps with GitLab and Flux?

GitOps flips the deployment model. Instead of a pipeline pushing changes to a cluster, an operator inside the cluster pulls the desired state from a source and reconciles the cluster to match. The source — typically a Git repository or an OCI artifact — is the single source of truth. If someone manually changes the cluster, the operator detects the drift and reverts it. If a deployment fails, reverting the source reverts the cluster. This is the pull-based model, and GitLab recommends it for production deployments because it has a stronger security model than CI/CD push deployments [citation:2][citation:4].

GitLab integrates Flux as its GitOps operator. Flux runs inside the cluster, watches the source, and applies changes. The GitLab agent (agentk) complements Flux: it simplifies the Flux setup, provides cluster-to-GitLab access management, and visualizes the cluster state in the GitLab UI. Together, Flux keeps the cluster synchronized while the agent provides visibility and control [citation:1][citation:2].

The recommended source for Flux is not a Git repository but an OCI artifact stored in the GitLab Container Registry. OCI artifacts are immutable, designed for scale, and can be security-scanned. A Git repository is mutable and its default branch triggers a sync when used to store cluster state — a problem if you push documentation changes. OCI artifacts separate the source of truth (Git) from the delivery mechanism (OCI), giving you the best of both [citation:1][citation:2].

Core Principle: GitOps is pull-based. Flux runs in the cluster, pulls the desired state from an OCI artifact, and reconciles continuously. Drift is detected and corrected automatically.

Flux

The GitOps operator. Runs in the cluster, watches sources, applies changes, detects drift. Installed via flux bootstrap.

GitLab Agent

Simplifies Flux setup, manages cluster access, visualizes state in GitLab UI. Works alongside Flux, not instead of it.

OCI Artifact

Recommended source for Flux. Immutable, scannable, scalable. Built and pushed by GitLab CI/CD from a Git repository.

Immediate Reconciliation

Agent detects git push and triggers Flux Receiver. Reduces delay between push and sync. Works for public projects and the agent config project.

Drift Remediation

If the cluster diverges from the source, Flux reverts it automatically. Manual changes don't persist.

Migration

Legacy agent GitOps (gitops.manifest_projects) is deprecated. Migrate to Flux by removing the config and running flux bootstrap.
How GitOps Works with GitLab

The deployment sequence starts with a developer pushing code to an application repository. That push triggers a GitLab CI/CD pipeline, which builds a container image and pushes it to the registry. A separate delivery repository — or a job within the same pipeline — builds an OCI artifact containing the Kubernetes manifests (with the new image tag) and pushes it to the container registry. The agent detects the push and notifies Flux, which pulls the OCI artifact and applies the changes to the cluster [citation:1][citation:4].

This separation is intentional. The application repository is for code. The delivery repository (or the OCI artifact) is for the desired state of the cluster. A single Git repository can be the source for multiple OCI artifacts, each packaging a small set of manifests. This avoids downloading an entire repository just to retrieve a few manifests, and it lets Flux follow a well-known versioning scheme — semantic versions, for example — so it can auto-deploy patch releases while requiring manual approval for major versions [citation:2][citation:6].

The agent's role is to make Flux easier to use with GitLab. Without the agent, you would configure Flux manually with deploy tokens and GitRepository objects. With the agent, Flux is bootstrapped with a single command, and the agent configures the Receiver webhook automatically. The agent also shows the cluster state in the GitLab UI, so you can see what's deployed without leaving GitLab [citation:1][citation:2].

# GitOps deployment sequence (simplified) # 1. Developer pushes code to application repo git push origin main # 2. GitLab CI/CD builds image and pushes to registry build: script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA # 3. CI/CD builds OCI artifact with manifests # (image tag updated in the manifests) push_manifests: image: fluxcd/flux-cli:latest script: - flux push artifact oci://$CI_REGISTRY_IMAGE/deploy:$CI_COMMIT_SHORT_SHA \ --path="./deploy" \ --source="$CI_PROJECT_URL" \ --revision="$CI_COMMIT_REF_NAME@sha1:$CI_COMMIT_SHA" \ --creds="$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" # 4. Flux pulls the OCI artifact and applies # (configured via OCIRepository and Kustomization) # 5. Agent detects push and triggers Flux Receiver # (if immediate reconciliation is enabled) # 6. Flux reconciles cluster state with the artifact # (drift remediation: manual changes are reverted) # Key actors: # - GitLab: source of truth, CI/CD, container registry # - Agent: access management, UI visibility, Receiver config # - Flux: pulls artifacts, applies manifests, detects drift # - Kubernetes: the target cluster
GitOps Benefits:
  • No cluster API exposure: Flux runs in the cluster; GitLab never connects to the API
  • Drift remediation: Manual changes are reverted automatically
  • Audit trail: Every change is a commit or artifact in GitLab
  • Rollback: Revert a commit or deploy a previous artifact
  • Scale: OCI artifacts are designed for high-volume pulls
OCI Artifacts as Flux Source

Using OCI artifacts instead of Git repositories as the Flux source is a deliberate architectural choice. Git repositories are mutable: a force-push rewrites history, and the default branch triggers a sync whenever it changes. If you store cluster state in the default branch, every documentation change triggers a reconciliation. OCI artifacts are immutable: a pushed artifact cannot be changed, and its version (tag) identifies exactly what it contains [citation:1][citation:2].

The GitLab Container Registry supports OCI artifacts natively. You build the artifact with flux push artifact, using the registry credentials that GitLab provides to every job. The artifact contains the Kubernetes manifests (or Kustomize overlays, or Helm charts) that define the desired state. Flux's OCIRepository resource points to the artifact, and a Kustomization resource applies it to the cluster [citation:2][citation:4].

The OCI artifact is a cache layer between Git and the cluster. The real source of truth remains the Git repository — the artifact is built from it. This means you get the reviewability of Git (merge requests, approvals, history) and the immutability and scalability of OCI. It also means the artifact can be security-scanned by the container registry, catching vulnerabilities before they reach the cluster [citation:6].

# Push OCI artifact with Flux CLI (GitLab CI/CD) push_manifests: stage: deploy image: fluxcd/flux-cli:latest script: - flux push artifact oci://$CI_REGISTRY_IMAGE/deploy:$CI_COMMIT_SHORT_SHA \ --path="./deploy" \ --source="$CI_PROJECT_URL" \ --revision="$CI_COMMIT_REF_NAME@sha1:$CI_COMMIT_SHA" \ --creds="$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" # Flux OCIRepository (in cluster) apiVersion: source.toolkit.fluxcd.io/v1beta2 kind: OCIRepository metadata: name: my-app namespace: flux-system spec: interval: 5m url: oci://registry.gitlab.com/my-group/my-project/deploy ref: tag: main # Or a specific version secretRef: name: gitlab-registry-auth # Flux Kustomization (applies the manifests) apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: my-app namespace: flux-system spec: interval: 10m sourceRef: kind: OCIRepository name: my-app path: ./ prune: true targetNamespace: production # OCI artifact commands: # flux push artifact oci:///: --path= # flux pull artifact oci:///: --output= # flux tag artifact oci:///: --tag= # Registry authentication: # --creds="$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" # Or use a Kubernetes secret with registry credentials
OCI vs Git as Flux Source: