Quick Start: Deploying Applications with Flux (Infra-Owned Registry)
Revised 2026-08-16 — rewritten for clarity. The steps and manifests are unchanged.
This guide shows how to deploy a new application into a cluster using one shared Flux installation and an infra-owned app registry. The application repo stays clean—standard Kustomize base/ and overlays/, no Flux CRDs—and onboarding an app takes a single small change in the infra repo.
Prerequisites
kubectlaccess to the cluster- The Flux CLI installed
- An infra/cluster Git repository ready (it can be empty)
Step 0: Bootstrap Flux (infra repo)
Flux bootstrap installs the Flux controllers into the cluster and commits the core sync manifests into your infra repo.
Validate prerequisites first:
flux check --pre
Then bootstrap. This example uses GitHub:
flux bootstrap github \
--owner=YOUR_GH_ORG \
--repository=YOUR_INFRA_REPO \
--branch=main \
--path=clusters/prod \
--personal=false
Your infra repo will now include:
clusters/prod/flux-system/
gotk-components.yaml
gotk-sync.yaml
kustomization.yaml
Flux continuously reconciles the infra repo path you bootstrapped.
Step 1: Add the apps registry entry point
Bootstrap syncs only clusters/prod/flux-system/. To onboard apps through an infra-owned registry, add one more Flux Kustomization pointing at an apps registry directory.
Create the directory in your infra repo:
clusters/prod/apps/
kustomization.yaml
clusters/prod/apps/kustomization.yaml starts empty and will later list one registry file per application:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources: []
Next, create the Flux Kustomization that activates the registry, at clusters/prod/apps-registry.yaml:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps-registry
namespace: flux-system
spec:
interval: 10m
prune: true
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/prod/apps
Finally, make sure Flux’s sync picks it up. Edit clusters/prod/flux-system/kustomization.yaml—this is the Kustomize file, not the Flux CR—and add the new registry Kustomization:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
- ../apps-registry.yaml
Commit and push. Flux is installed, the infra repo is reconciling, and the registry directory is live—but no applications are deployed yet.
Understanding path resolution: Flux vs Kustomize
Flux and Kustomize resolve paths differently, on purpose, and this is where most mistakes happen.
Flux’s Kustomization.spec.path is always resolved relative to the root of the Git repository referenced by sourceRef—never relative to the location of the YAML file that declares it. So:
spec:
path: ./clusters/prod/apps
always means <git-repo-root>/clusters/prod/apps. That’s why Flux paths tend to look absolute within the repo.
Paths inside a kustomization.yaml, such as entries under resources:, work the ordinary way: they resolve relative to the directory containing that file. Inside clusters/prod/apps/kustomization.yaml, this:
resources:
- app-foo.yaml
resolves to clusters/prod/apps/app-foo.yaml.
The short version is that Flux chooses the entry directory and Kustomize resolves everything underneath it.
Step 2: Create the application repository
Create a Git repository for your app with this structure:
base/
kustomization.yaml
deployment.yaml
service.yaml
overlays/
prod/
kustomization.yaml
base/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
overlays/prod/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- replicas.yaml
Check that the manifests build locally before going further:
kustomize build overlays/prod
Step 3: Create a deploy key for the app repo
Create a deploy key for the app repository with read-only access, then store it in the cluster as a Secret in the flux-system namespace:
apiVersion: v1
kind: Secret
metadata:
name: app-foo-git-ssh
namespace: flux-system
type: Opaque
stringData:
identity: |
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----
known_hosts: |
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
Step 4: Register the app in the infra repo
In the infra repo, add a registry entry at clusters/prod/apps/app-foo.yaml:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: app-foo
namespace: flux-system
spec:
interval: 1m
url: ssh://git@github.com/yourorg/app-foo.git
ref:
branch: main
secretRef:
name: app-foo-git-ssh
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: app-foo
namespace: flux-system
spec:
interval: 10m
prune: true
sourceRef:
kind: GitRepository
name: app-foo
path: ./overlays/prod
targetNamespace: app-foo
Then add the file to the registry’s Kustomize list:
# clusters/prod/apps/kustomization.yaml
resources:
- app-foo.yaml
Commit and push.
Step 5: Watch Flux deploy the app
Flux fetches the app repository, builds overlays/prod, applies the manifests into the app-foo namespace, and reconciles changes from then on. Watch it happen with:
flux get kustomizations
flux get sources git
Updating and removing the app
To update, push changes to the app repo. Flux reconciles automatically and the infra repo needs no changes at all.
To remove, delete app-foo.yaml from clusters/prod/apps/, then commit and push. Flux prunes the app’s registry objects and stops reconciling it.
Why this model works
Ownership is clear: infra owns cluster topology, apps own their own manifests. The Flux surface area stays small, since there’s one installation per cluster and one registry entry point. And the lifecycle is predictable—adding or removing a file in the registry is the same thing as deploying or undeploying the app, which is what makes this scale to many apps and teams without turning into a pile of one-off Flux CRDs.