Flux in Practice: Installation & Configuration

Flux in Practice: Installation & Configuration

Disclaimer: This post was originally written for my company’s blog . I’ve translated it and added a personal touch for this space.

After looking into ArgoCD in the previous articles, it’s time to turn our attention to Flux, another GitOps tool under the CNCF umbrella - and also the one I am working with in my job at b’nerd .

Like before, I’ll focus on a practical setup: installing Flux and using it to deploy a simple demo application consisting of a frontend and a backend. The application is packaged as Helm charts and built via a GitLab CI pipeline, which pushes images to the GitLab Container Registry.

Prerequisites

If you want to follow along, you’ll need:

  • A Git repository containing source code and Helm charts for a frontend and a backend
  • A GitLab pipeline that builds the images and pushes them to the GitLab Container Registry
  • Access to a Kubernetes cluster

Installing the Flux CLI & Bootstrapping

Before installing Flux, we need the Flux CLI. On macOS, this can easily be done using Homebrew:

brew install fluxcd/tap/flux

You can find more installation options in the Flux documentation .

Unlike ArgoCD, Flux is bootstrapped directly from a Git repository. During this process, Flux installs its controllers into the cluster and sets up everything required to continuously synchronize the repository.

To authenticate against GitLab, we first create a Personal Access Token (PAT) with the api scope. Create it in your GitLab project under Settings → Access Tokens, then export it in your terminal:

export GITLAB_TOKEN=<Your-PAT>

Now we can bootstrap Flux:

flux bootstrap gitlab \
  --owner=<GitLab-group> \
  --repository=<Repository-name> \
  --branch=main \
  --path=infrastructure \
  --hostname=<GitLab-URL> \
  --token-auth

The --path flag defines where Flux stores its configuration inside the repository. The --hostname option is only required when working with a self-hosted GitLab instance.

Once the command completes, Flux commits several files to the repository and installs all required components in the cluster.

Inside the repository, a new flux-system directory appears. It contains several YAML files:

  • gotk-components.yaml – installs the Flux components (Source, Kustomize, Helm, and Notification Controller)

  • gotk-sync.yaml – defines a GitRepository and a Kustomization resource so Flux can continuously reconcile its own configuration

  • kustomization.yaml – ties the bootstrap resources above together using Kustomize

Looking into our cluster, we can see the installed controllers:

  • Source Controller – handles Git repositories, Helm repositories, and other sources

  • Kustomize Controller – applies Kustomizations and keeps cluster state in sync

  • Helm Controller – manages HelmRelease resources

  • Notification Controller – optional, used for alerts and events

Kustomize plays a central role in Flux: The Kustomize Controller regularly renders the manifests stored in Git and ensures the cluster state matches what’s defined in the repository.

Deploying our demo application

With Flux installed, we can now deploy our demo application.

1. Create a GitRepository source

First, we tell Flux where our Helm charts live by creating a GitRepository resource.

Create a file called gitrepository.yaml inside the flux-system directory:

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: demo-app
  namespace: flux-system
spec:
  interval: 1m
  url: <Path to our repository>
  ref:
    branch: main
  secretRef:
    name: flux-system

We then reference this file in our kustomization.yaml, which is the central entry point for synchronization:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - gotk-components.yaml
  - gotk-sync.yaml
  - gitrepository.yaml

After pushing the changes, Flux detects the new resource and creates the GitRepository in the cluster.

2. Creating a Registry Secret

Our GitLab PAT created during bootstrap is stored in the flux-system namespace and cannot be reused automatically by workloads in other namespaces.

To allow our application to pull images from the GitLab Container Registry, we create a Docker registry secret in the application namespace.

In a real production setup, this would usually be a separate token with read-only registry permissions. For this demo, we’ll reuse the existing PAT:

kubectl create secret docker-registry regcred \
  --namespace=<Our namespace> \
  --docker-server=<URL of our registry> \
  --docker-username=<GitLab username> \
  --docker-password=<GitLab PAT> \
  --docker-email=flux-demo@example.com

This secret will later be referenced by our Helm releases.

3. Creating HelmReleases

We now define one HelmRelease for the frontend and one for the backend. Both files are placed in the flux-system directory so they are picked up by Flux.

frontend.helmrelease.yaml

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: demo-app-frontend
  namespace: flux-demo
spec:
  interval: 1m
  chart:
    spec:
      chart: ./charts/frontend # Path to the Helm chart
      sourceRef:
        kind: GitRepository
        name: demo-app
        namespace: flux-system
  values:
    image:
      repository: <Path to the frontend image in the GitLab registry>
      tag: <Tag>
    imagePullSecrets:
      - name: regcred

backend.helmrelease.yaml

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: demo-app-backend
  namespace: flux-demo
spec:
  interval: 1m
  chart:
    spec:
      chart: ./charts/backend # Path to the Helm chart
      sourceRef:
        kind: GitRepository
        name: demo-app
        namespace: flux-system
  values:
    image:
      repository: <Path to the backend image in the GitLab registry>
      tag: <Tag>
    imagePullSecrets:
      - name: regcred

Finally, add both files to kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - gotk-components.yaml
  - gotk-sync.yaml
  - gitrepository.yaml
  - frontend.helmrelease.yaml
  - backend.helmrelease.yaml

After pushing these changes, Flux reconciles the repository and deploys both components. After a short wait, the frontend and backend pods are running in the cluster. 🎉

Summing up

Flux makes it easy and straightforward to deploy applications in a structured way. Access to private container registries – like our example with GitLab – can be set up without much hassle.