Skip to content

Install fluxcd/fluxv2

Flux is a set of continuous and progressive delivery solutions for Kubernetes that are open and extensible.

Using flux to manage deployments into the cluster means:

  1. All change is version-controlled (i.e. "GitOps")
  2. It's not necessary to expose the cluster API (i.e., which would otherwise be the case if you were using CI)
  3. Deployments can be paused, rolled back, examine, debugged using Kubernetes primitives and tooling


  • Install the flux CLI tools on a host which has access to your cluster's apiserver.
  • Create a GitHub personal access token that can create repositories by checking all permissions under repo.
  • Create a private GitHub repository dedicated to your flux deployments

Fluxv2 components

Here's a simplified way to think about the various flux components..

  1. You need a source for flux to look at. This is usually a Git repository, although it can also be a helm repository, an S3 bucket. A source defines the entire repo (not a path or a folder structure).
  2. Within your source, you define one or more kustomizations. Each kustomization is a location on your source (i.e., myrepo/nginx) containing YAML files to be applied directly to the API server.
  3. The YAML files inside the kustomization include:
    1. HelmRepositories (think of these as the repos you'd add to helm with helm repo)
    2. HelmReleases (these are charts which live in HelmRepositories)
    3. Any other valid Kubernetes YAML manifests (i.e., ConfigMaps, etc)


Install flux CLI

This section is a direct copy of the official docs, to save you having to open another tab..

With Homebrew for macOS and Linux:

brew install fluxcd/tap/flux

With Bash for macOS and Linux:

curl -s | sudo bash

With Chocolatey for Windows:

choco install flux

Create GitHub Token

Create a GitHub personal access token that can create repositories by checking all permissions under repo, as well as all options under admin:public_key. (we'll use the token in the bootstrapping step below)


A personal token, not one of these new-fangled "fine grained access tokens", which don't work with Flux (yet) :)

Create GitHub Repo

Now we'll create a repo for flux - it can (and probably should!) be private. I've created a template repo to get you started, but you could simply start with a blank repo too (although you'll need at least a bootstrap directory included or the command below will fail).1

Bootstrap Flux

Having prepared all of the above, we're now ready to deploy flux. Before we start, take a look at all the running pods in the cluster, with kubectl get pods -A. You should see something like this...

root@shredder:~# k3s kubectl get pods -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
kube-system   coredns-7448499f4d-qfszx                  1/1     Running   0          6m32s
kube-system   local-path-provisioner-5ff76fc89d-rqh52   1/1     Running   0          6m32s
kube-system   metrics-server-86cbb8457f-25688           1/1     Running   0          6m32s

Now, run a customized version of the following:

GITHUB_TOKEN=<your-token> \
flux bootstrap github \
  --owner=my-github-username \
  --repository=my-repository \
  --personal \
  --path bootstrap

What if SSH is blocked?

Per @jmmassou, if you're behind a restrictive firewall which may block outgoing SSH, you might see an error like this:

SSH key scan for host failed, error: ssh: handshake failed: EOF

A clever workaround is to use SSH over port 443 instead (apparently GitHub is configured to make this work)2, by appending the following to your bootstrap command:


Once the flux bootstrap is completed without errors, list the pods in the cluster again, with kubectl get pods -A. This time, you see something like this:

root@shredder:~# k3s kubectl get pods -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
flux-system   helm-controller-f7c5b6c56-nk7rm            1/1     Running   0          5m48s
flux-system   kustomize-controller-55db56f44f-4kqs2      1/1     Running   0          5m48s
flux-system   notification-controller-77f68bf8f4-9zlw9   1/1     Running   0          5m48s
flux-system   source-controller-8457664f8f-8qhhm         1/1     Running   0          5m48s
kube-system   coredns-7448499f4d-qfszx                   1/1     Running   0          15m
kube-system   local-path-provisioner-5ff76fc89d-rqh52    1/1     Running   0          15m
kube-system   metrics-server-86cbb8457f-25688            1/1     Running   0          15m
traefik       svclb-traefik-ppvhr                        2/2     Running   0          5m31s
traefik       traefik-f48b94477-d476p                    1/1     Running   0          5m31s

What just happened?

Flux installed its controllers into the flux-system namespace, and created two new objects:

  1. A GitRepository called flux-system, pointing to your GitHub repo.
  2. A Kustomization called flux-system, pointing to the flux-system directory in the above repo.

If you used my template repo, some extra things also happened..

  1. I'd pre-populated the bootstrap directory in the template repo with 3 folders:
    1. helmrepositories, for storing repositories used for deploying helm charts
    2. kustomizations, for storing additional kustomizations (which in turn can reference other paths in the repo)
    3. namespaces, for storing namespace manifests (since these need to exist before we can deploy helmreleases into them)
  2. Because the bootstrap Kustomization includes everything recursively under bootstrap path in the repo, all of the above were also applied to the cluster
  3. I'd pre-prepared a Namespace, HelmRepository, and Kustomization for "podinfo", a simple example application, so these were applied to the cluster
  4. The kustomization we added for podinfo refers to the /podinfo path in the repo, so everything in this folder was also applied to the cluster
  5. In the /podinfo path of the repo is a HelmRelease (an object describing how to deploy a helm chart), and a ConfigMap (which ontain the values.yaml for the podinfo helm chart)
  6. Flux recognized the podinfo HelmRelease, applied it along with the values in the ConfigMap, and consequently we have podinfo deployed from the latest helm chart, into the cluster, and managed by Flux! 💪

Wait, but why?

That's best explained on the next page, describing the design we're using...

Chef's notes 📓

  1. The template repo also "bootstraps" a simple example re how to operate flux, by deploying the podinfo helm chart. 

  2. TIL that GitHub listens for SSH on on port 443! 

Tip your waiter (sponsor) 👏

Did you receive excellent service? Want to compliment the chef? (..and support development of current and future recipes!) Sponsor me on Github / Ko-Fi / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏

Employ your chef (engage) 🤝

Is this too much of a geeky PITA? Do you just want results, stat? I do this for a living - I'm a full-time Kubernetes contractor, providing consulting and engineering expertise to businesses needing short-term, short-notice support in the cloud-native space, including AWS/Azure/GKE, Kubernetes, CI/CD and automation.

Learn more about working with me here.

Flirt with waiter (subscribe) 💌

Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated.

Your comments? 💬