{"id":13521784,"url":"https://github.com/fluxcd/flux2-kustomize-helm-example","last_synced_at":"2025-04-14T20:43:21.499Z","repository":{"id":37476818,"uuid":"310316550","full_name":"fluxcd/flux2-kustomize-helm-example","owner":"fluxcd","description":"A GitOps workflow example for multi-env deployments with Flux, Kustomize and Helm.","archived":false,"fork":false,"pushed_at":"2024-10-14T11:34:59.000Z","size":477,"stargazers_count":1082,"open_issues_count":9,"forks_count":1114,"subscribers_count":35,"default_branch":"main","last_synced_at":"2025-04-07T18:03:56.942Z","etag":null,"topics":["example","flux2","fluxcd","gitops","helm","kubernetes","kustomize"],"latest_commit_sha":null,"homepage":"https://fluxcd.io","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fluxcd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-05T14:01:26.000Z","updated_at":"2025-04-06T22:59:41.000Z","dependencies_parsed_at":"2024-05-13T16:12:15.472Z","dependency_job_id":"1eeb31a9-a0a4-41aa-bda6-dd9e4e419773","html_url":"https://github.com/fluxcd/flux2-kustomize-helm-example","commit_stats":{"total_commits":58,"total_committers":14,"mean_commits":4.142857142857143,"dds":"0.31034482758620685","last_synced_commit":"0ad4dfb50bbba3f3ab2b661aa82c931c2111fb2f"},"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxcd%2Fflux2-kustomize-helm-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxcd%2Fflux2-kustomize-helm-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxcd%2Fflux2-kustomize-helm-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxcd%2Fflux2-kustomize-helm-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluxcd","download_url":"https://codeload.github.com/fluxcd/flux2-kustomize-helm-example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248960117,"owners_count":21189978,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["example","flux2","fluxcd","gitops","helm","kubernetes","kustomize"],"created_at":"2024-08-01T06:00:38.020Z","updated_at":"2025-04-14T20:43:21.476Z","avatar_url":"https://github.com/fluxcd.png","language":"Shell","readme":"# flux2-kustomize-helm-example\n\n[![test](https://github.com/fluxcd/flux2-kustomize-helm-example/workflows/test/badge.svg)](https://github.com/fluxcd/flux2-kustomize-helm-example/actions)\n[![e2e](https://github.com/fluxcd/flux2-kustomize-helm-example/workflows/e2e/badge.svg)](https://github.com/fluxcd/flux2-kustomize-helm-example/actions)\n[![license](https://img.shields.io/github/license/fluxcd/flux2-kustomize-helm-example.svg)](https://github.com/fluxcd/flux2-kustomize-helm-example/blob/main/LICENSE)\n\nFor this example we assume a scenario with two clusters: staging and production.\nThe end goal is to leverage Flux and Kustomize to manage both clusters while minimizing duplicated declarations.\n\nWe will configure Flux to install, test and upgrade a demo app using\n`HelmRepository` and `HelmRelease` custom resources.\nFlux will monitor the Helm repository, and it will automatically\nupgrade the Helm releases to their latest chart version based on semver ranges.\n\n## Prerequisites\n\nYou will need a Kubernetes cluster version 1.28 or newer.\nFor a quick local test, you can use [Kubernetes kind](https://kind.sigs.k8s.io/docs/user/quick-start/).\nAny other Kubernetes setup will work as well though.\n\nIn order to follow the guide you'll need a GitHub account and a\n[personal access token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line)\nthat can create repositories (check all permissions under `repo`).\n\nInstall the Flux CLI on macOS or Linux using Homebrew:\n\n```sh\nbrew install fluxcd/tap/flux\n```\n\nOr install the CLI by downloading precompiled binaries using a Bash script:\n\n```sh\ncurl -s https://fluxcd.io/install.sh | sudo bash\n```\n\n## Repository structure\n\nThe Git repository contains the following top directories:\n\n- **apps** dir contains Helm releases with a custom configuration per cluster\n- **infrastructure** dir contains common infra tools such as ingress-nginx and cert-manager\n- **clusters** dir contains the Flux configuration per cluster\n\n```\n├── apps\n│   ├── base\n│   ├── production \n│   └── staging\n├── infrastructure\n│   ├── configs\n│   └── controllers\n└── clusters\n    ├── production\n    └── staging\n```\n\n### Applications\n\nThe apps configuration is structured into:\n\n- **apps/base/** dir contains namespaces and Helm release definitions\n- **apps/production/** dir contains the production Helm release values\n- **apps/staging/** dir contains the staging values\n\n```\n./apps/\n├── base\n│   └── podinfo\n│       ├── kustomization.yaml\n│       ├── namespace.yaml\n│       ├── release.yaml\n│       └── repository.yaml\n├── production\n│   ├── kustomization.yaml\n│   └── podinfo-patch.yaml\n└── staging\n    ├── kustomization.yaml\n    └── podinfo-patch.yaml\n```\n\nIn **apps/base/podinfo/** dir we have a Flux `HelmRelease` with common values for both clusters:\n\n```yaml\napiVersion: helm.toolkit.fluxcd.io/v2\nkind: HelmRelease\nmetadata:\n  name: podinfo\n  namespace: podinfo\nspec:\n  releaseName: podinfo\n  chart:\n    spec:\n      chart: podinfo\n      sourceRef:\n        kind: HelmRepository\n        name: podinfo\n        namespace: flux-system\n  interval: 50m\n  values:\n    ingress:\n      enabled: true\n      className: nginx\n```\n\nIn **apps/staging/** dir we have a Kustomize patch with the staging specific values:\n\n```yaml\napiVersion: helm.toolkit.fluxcd.io/v2\nkind: HelmRelease\nmetadata:\n  name: podinfo\nspec:\n  chart:\n    spec:\n      version: \"\u003e=1.0.0-alpha\"\n  test:\n    enable: true\n  values:\n    ingress:\n      hosts:\n        - host: podinfo.staging\n```\n\nNote that with ` version: \"\u003e=1.0.0-alpha\"` we configure Flux to automatically upgrade\nthe `HelmRelease` to the latest chart version including alpha, beta and pre-releases.\n\nIn **apps/production/** dir we have a Kustomize patch with the production specific values:\n\n```yaml\napiVersion: helm.toolkit.fluxcd.io/v2\nkind: HelmRelease\nmetadata:\n  name: podinfo\n  namespace: podinfo\nspec:\n  chart:\n    spec:\n      version: \"\u003e=1.0.0\"\n  values:\n    ingress:\n      hosts:\n        - host: podinfo.production\n```\n\nNote that with ` version: \"\u003e=1.0.0\"` we configure Flux to automatically upgrade\nthe `HelmRelease` to the latest stable chart version (alpha, beta and pre-releases will be ignored).\n\n### Infrastructure\n\nThe infrastructure is structured into:\n\n- **infrastructure/controllers/** dir contains namespaces and Helm release definitions for Kubernetes controllers\n- **infrastructure/configs/** dir contains Kubernetes custom resources such as cert issuers and networks policies\n\n```\n./infrastructure/\n├── configs\n│   ├── cluster-issuers.yaml\n│   └── kustomization.yaml\n└── controllers\n    ├── cert-manager.yaml\n    ├── ingress-nginx.yaml\n    └── kustomization.yaml\n```\n\nIn **infrastructure/controllers/** dir we have the Flux `HelmRepository` and `HelmRelease` definitions such as:\n\n```yaml\napiVersion: helm.toolkit.fluxcd.io/v2\nkind: HelmRelease\nmetadata:\n  name: cert-manager\n  namespace: cert-manager\nspec:\n  interval: 30m\n  chart:\n    spec:\n      chart: cert-manager\n      version: \"1.x\"\n      sourceRef:\n        kind: HelmRepository\n        name: cert-manager\n        namespace: cert-manager\n      interval: 12h\n  values:\n    installCRDs: true\n```\n\nNote that with ` interval: 12h` we configure Flux to pull the Helm repository index every twelfth hours to check for updates.\nIf the new chart version that matches the `1.x` semver range is found, Flux will upgrade the release.\n\nIn **infrastructure/configs/** dir we have Kubernetes custom resources, such as the Let's Encrypt issuer:\n\n```yaml\napiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt\nspec:\n  acme:\n    # Replace the email address with your own contact email\n    email: fluxcdbot@users.noreply.github.com\n    server: https://acme-staging-v02.api.letsencrypt.org/directory\n    privateKeySecretRef:\n      name: letsencrypt-nginx\n    solvers:\n      - http01:\n          ingress:\n            class: nginx\n```\n\nIn **clusters/production/infrastructure.yaml** we replace the Let's Encrypt server value to point to the production API:\n\n```yaml\napiVersion: kustomize.toolkit.fluxcd.io/v1\nkind: Kustomization\nmetadata:\n  name: infra-configs\n  namespace: flux-system\nspec:\n  # ...omitted for brevity\n  dependsOn:\n    - name: infra-controllers\n  patches:\n    - patch: |\n        - op: replace\n          path: /spec/acme/server\n          value: https://acme-v02.api.letsencrypt.org/directory\n      target:\n        kind: ClusterIssuer\n        name: letsencrypt\n```\n\nNote that with `dependsOn` we tell Flux to first install or upgrade the controllers and only then the configs.\nThis ensures that the Kubernetes CRDs are registered on the cluster, before Flux applies any custom resources.\n\n## Bootstrap staging and production\n\nThe clusters dir contains the Flux configuration:\n\n```\n./clusters/\n├── production\n│   ├── apps.yaml\n│   └── infrastructure.yaml\n└── staging\n    ├── apps.yaml\n    └── infrastructure.yaml\n```\n\nIn **clusters/staging/** dir we have the Flux Kustomization definitions, for example:\n\n```yaml\napiVersion: kustomize.toolkit.fluxcd.io/v1\nkind: Kustomization\nmetadata:\n  name: apps\n  namespace: flux-system\nspec:\n  interval: 10m0s\n  dependsOn:\n    - name: infra-configs\n  sourceRef:\n    kind: GitRepository\n    name: flux-system\n  path: ./apps/staging\n  prune: true\n  wait: true\n```\n\nNote that with `path: ./apps/staging` we configure Flux to sync the staging Kustomize overlay and \nwith `dependsOn` we tell Flux to create the infrastructure items before deploying the apps.\n\nFork this repository on your personal GitHub account and export your GitHub access token, username and repo name:\n\n```sh\nexport GITHUB_TOKEN=\u003cyour-token\u003e\nexport GITHUB_USER=\u003cyour-username\u003e\nexport GITHUB_REPO=\u003crepository-name\u003e\n```\n\nVerify that your staging cluster satisfies the prerequisites with:\n\n```sh\nflux check --pre\n```\n\nSet the kubectl context to your staging cluster and bootstrap Flux:\n\n```sh\nflux bootstrap github \\\n    --context=staging \\\n    --owner=${GITHUB_USER} \\\n    --repository=${GITHUB_REPO} \\\n    --branch=main \\\n    --personal \\\n    --path=clusters/staging\n```\n\nThe bootstrap command commits the manifests for the Flux components in `clusters/staging/flux-system` dir\nand creates a deploy key with read-only access on GitHub, so it can pull changes inside the cluster.\n\nWatch for the Helm releases being installed on staging:\n\n```console\n$ watch flux get helmreleases --all-namespaces\n\nNAMESPACE    \tNAME         \tREVISION\tSUSPENDED\tREADY\tMESSAGE \ncert-manager \tcert-manager \tv1.11.0 \tFalse    \tTrue \tRelease reconciliation succeeded\ningress-nginx\tingress-nginx\t4.4.2   \tFalse    \tTrue \tRelease reconciliation succeeded\npodinfo      \tpodinfo      \t6.3.0   \tFalse    \tTrue \tRelease reconciliation succeeded\n```\n\nVerify that the demo app can be accessed via ingress:\n\n```console\n$ kubectl -n ingress-nginx port-forward svc/ingress-nginx-controller 8080:80 \u0026\n\n$ curl -H \"Host: podinfo.staging\" http://localhost:8080\n{\n  \"hostname\": \"podinfo-59489db7b5-lmwpn\",\n  \"version\": \"6.2.3\"\n}\n```\n\nBootstrap Flux on production by setting the context and path to your production cluster:\n\n```sh\nflux bootstrap github \\\n    --context=production \\\n    --owner=${GITHUB_USER} \\\n    --repository=${GITHUB_REPO} \\\n    --branch=main \\\n    --personal \\\n    --path=clusters/production\n```\n\nWatch the production reconciliation:\n\n```console\n$ flux get kustomizations --watch\n\nNAME             \tREVISION     \tSUSPENDED\tREADY\tMESSAGE                         \napps             \tmain/696182e\tFalse    \tTrue \tApplied revision: main/696182e\t\nflux-system      \tmain/696182e\tFalse    \tTrue \tApplied revision: main/696182e\t\ninfra-configs    \tmain/696182e\tFalse    \tTrue \tApplied revision: main/696182e\t\ninfra-controllers\tmain/696182e\tFalse    \tTrue \tApplied revision: main/696182e\t\n```\n\n## Add clusters\n\nIf you want to add a cluster to your fleet, first clone your repo locally:\n\n```sh\ngit clone https://github.com/${GITHUB_USER}/${GITHUB_REPO}.git\ncd ${GITHUB_REPO}\n```\n\nCreate a dir inside `clusters` with your cluster name:\n\n```sh\nmkdir -p clusters/dev\n```\n\nCopy the sync manifests from staging:\n\n```sh\ncp clusters/staging/infrastructure.yaml clusters/dev\ncp clusters/staging/apps.yaml clusters/dev\n```\n\nYou could create a dev overlay inside `apps`, make sure\nto change the `spec.path` inside `clusters/dev/apps.yaml` to `path: ./apps/dev`. \n\nPush the changes to the main branch:\n\n```sh\ngit add -A \u0026\u0026 git commit -m \"add dev cluster\" \u0026\u0026 git push\n```\n\nSet the kubectl context and path to your dev cluster and bootstrap Flux:\n\n```sh\nflux bootstrap github \\\n    --context=dev \\\n    --owner=${GITHUB_USER} \\\n    --repository=${GITHUB_REPO} \\\n    --branch=main \\\n    --personal \\\n    --path=clusters/dev\n```\n\n## Identical environments\n\nIf you want to spin up an identical environment, you can bootstrap a cluster\ne.g. `production-clone` and reuse the `production` definitions.\n\nBootstrap the `production-clone` cluster:\n\n```sh\nflux bootstrap github \\\n    --context=production-clone \\\n    --owner=${GITHUB_USER} \\\n    --repository=${GITHUB_REPO} \\\n    --branch=main \\\n    --personal \\\n    --path=clusters/production-clone\n```\n\nPull the changes locally:\n\n```sh\ngit pull origin main\n```\n\nCreate a `kustomization.yaml` inside the `clusters/production-clone` dir:\n\n```yaml\napiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nresources:\n  - flux-system\n  - ../production/infrastructure.yaml\n  - ../production/apps.yaml\n```\n\nNote that besides the `flux-system` kustomize overlay, we also include\nthe `infrastructure` and `apps` manifests from the production dir.\n\nPush the changes to the main branch:\n\n```sh\ngit add -A \u0026\u0026 git commit -m \"add production clone\" \u0026\u0026 git push\n```\n\nTell Flux to deploy the production workloads on the `production-clone` cluster:\n\n```sh\nflux reconcile kustomization flux-system \\\n    --context=production-clone \\\n    --with-source \n```\n\n## Testing\n\nAny change to the Kubernetes manifests or to the repository structure should be validated in CI before\na pull requests is merged into the main branch and synced on the cluster.\n\nThis repository contains the following GitHub CI workflows:\n\n* the [test](./.github/workflows/test.yaml) workflow validates the Kubernetes manifests and Kustomize overlays with [kubeconform](https://github.com/yannh/kubeconform)\n* the [e2e](./.github/workflows/e2e.yaml) workflow starts a Kubernetes cluster in CI and tests the staging setup by running Flux in Kubernetes Kind\n","funding_links":[],"categories":["Tutorials","Shell","kubernetes"],"sub_categories":["Secrets"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluxcd%2Fflux2-kustomize-helm-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluxcd%2Fflux2-kustomize-helm-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluxcd%2Fflux2-kustomize-helm-example/lists"}