CtrlK
BlogDocsLog inGet started
Tessl Logo

nitinjain999/platform-skills

Production-grade platform engineering handbook — Kubernetes, Terraform, Flux CD, GitHub Actions, AWS, and more.

67

Quality

84%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

fluxcd.mdreferences/

Flux Reference

Contents

  • Scope
  • Controller and CRD reference
  • Source selection
  • Repository patterns
  • Reconciliation model
  • Promotion model
  • Safety rules
  • Flux Operator and FluxInstance
  • ResourceSet and ResourceSetInputProvider
  • Gitless (OCI-based) delivery
  • Reactivity: reconcile.fluxcd.io/watch
  • Common mistakes
  • Image automation (Git-based and Gitless)

Scope

Use Flux for:

  • Cluster add-ons
  • Application delivery
  • Helm release management
  • Kustomize overlays per cluster or environment

Flux should consume a prepared cluster. Bootstrap the cluster and its cloud prerequisites with Terraform unless there is a strong reason to use another control plane.

Controller and CRD reference

KindapiVersionController
FluxInstancefluxcd.controlplane.io/v1flux-operator
ResourceSetfluxcd.controlplane.io/v1flux-operator
ResourceSetInputProviderfluxcd.controlplane.io/v1flux-operator
GitRepositorysource.toolkit.fluxcd.io/v1source-controller
OCIRepositorysource.toolkit.fluxcd.io/v1source-controller
HelmRepositorysource.toolkit.fluxcd.io/v1source-controller
HelmChartsource.toolkit.fluxcd.io/v1source-controller
Bucketsource.toolkit.fluxcd.io/v1source-controller
ArtifactGeneratorsource.extensions.fluxcd.io/v1beta1source-controller
Kustomizationkustomize.toolkit.fluxcd.io/v1kustomize-controller
HelmReleasehelm.toolkit.fluxcd.io/v2helm-controller
Providernotification.toolkit.fluxcd.io/v1beta3notification-controller
Alertnotification.toolkit.fluxcd.io/v1beta3notification-controller
Receivernotification.toolkit.fluxcd.io/v1notification-controller
ImageRepositoryimage.toolkit.fluxcd.io/v1beta2image-reflector-controller
ImagePolicyimage.toolkit.fluxcd.io/v1beta2image-reflector-controller
ImageUpdateAutomationimage.toolkit.fluxcd.io/v1beta1image-automation-controller

Source selection

ScenarioUse
Git repo with YAML / Kustomize overlaysGitRepository
OCI artifact (manifests, configs)OCIRepository
Helm chart from OCI registry (recommended)OCIRepository with layerSelector.mediaType
Helm chart from HTTPS repositoryHelmRepository
S3 / GCS / MinIO bucketBucket
Monorepo — split one Git source into multiple artifact streamsArtifactGenerator

Kustomization vs HelmRelease:

  • Plain YAML / Kustomize overlays → Kustomization
  • Helm chart → HelmRelease

ResourceSet vs Kustomization:

  • One fixed deployment of manifests → Kustomization
  • Same template applied to N inputs (tenants, environments) → ResourceSet

Repository patterns

Three primary patterns — identify which one applies before auditing or extending a repo:

Monorepo

Everything in one repo. Best for smaller teams or those starting their GitOps journey.

clusters/
  production/
  staging/
apps/
  base/          # shared definitions
  production/    # environment overlay
  staging/       # environment overlay
infrastructure/
  controllers/   # installs CRDs (cert-manager, kyverno)
  configs/       # creates CRs (ClusterIssuer, policies)

Dependency chain: infra-controllersinfra-configsapps

Use ArtifactGenerator to split the repo into independent source streams — only the affected component's artifact gets a new revision when its path changes.

Multi-repo (Git-based)

Separate repos for platform and application teams. A fleet repo orchestrates via GitRepository resources pointing to tenant repos. Each tenant gets a scoped ServiceAccount with RoleBinding.

Multi-repo (OCI-based / Gitless)

Most advanced. FluxInstance syncs from an OCI registry. ResourceSet templates generate per-tenant resources. OCI artifacts are immutable, Cosign-signable, and require no Git credentials on clusters.

Tag promotion strategy: latest → staging, latest-stable → production.

Identification heuristics

Signal in the repoLikely pattern
apps/base/ + apps/<env>/ overlaysMonorepo with Kustomize overlays
ArtifactGenerator resourcesMonorepo with source decomposition
tenants/ directory + per-tenant GitRepository/KustomizationMulti-repo fleet (Git-based)
ResourceSet + ResourceSetInputProvider resourcesFleet with N-input templating
FluxInstance with sync.kind: OCIRepositoryGitless OCI-based fleet
postBuild.substituteFrom referencing flux-runtime-infoMulti-cluster with per-cluster variables
update/ or update-policies/ directoryRepo with image automation
gotk-sync.yaml in clusters/Bootstrapped with flux bootstrap — consider migrating to Flux Operator

Common layout rules

  • clusters/ defines what each cluster reconciles.
  • apps/ defines application or service workloads.
  • infrastructure/ contains in-cluster platform components (ingress, cert-manager, observability).
  • flux-system/ belongs under clusters/<cluster>/ — never in apps/ or infrastructure/.
  • Do not let overlapping Kustomization paths exist (where one path is a prefix of another).
  • No workloads in the default namespace.

Reconciliation model

Resource flow:

Sources → Artifacts → Appliers → Managed Resources → Notifications
  • Keep reconciliation pull-based from Git.
  • Pin chart and image versions intentionally.
  • Prefer small, well-named Kustomization boundaries with clear dependencies.
  • Use dependsOn, health checks, and intervals deliberately rather than a single large root object.

Interval strategy

LayerRecommended intervalReason
Sources (GitRepository, OCIRepository)5mDetect new commits/artifacts quickly
Appliers (Kustomization, HelmRelease)30mReduce API server load; Receivers handle immediate triggers
retryInterval (on failure)5m (Kustomization), 3m (HelmRelease)Recover from transient failures without waiting the full interval

Receivers handle immediate reconciliation on Git push or OCI push events — applier intervals are the fallback for when no Receiver fires. Set Receivers up for every production GitRepository to avoid depending on the poll interval for fast feedback.

GitRepository + Kustomization

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 5m
  url: https://github.com/org/my-app.git
  ref:
    branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: my-app
  path: ./deploy/production
  prune: true
  wait: true
  timeout: 5m

Promotion model

  • Promote by changing image tags, chart versions, or overlay refs in Git.
  • Keep environment overlays minimal; shared defaults belong in base definitions.
  • For multi-cluster fleets, separate cluster-specific settings from app version promotion.

Safety rules

  • Do not patch resources manually in-cluster and call that the deployed state.
  • Keep secrets out of plain Git unless encrypted and operationally justified.
  • Ensure controllers that need cloud access use workload identity rather than static keys where possible.
  • Treat Flux as the last-mile reconciler, not as the place to invent environment-specific business logic.

Flux Operator and FluxInstance

The Flux Operator manages the full lifecycle of Flux controllers via a FluxInstance CRD — installation, configuration, upgrades, and health reporting.

Rules:

  • Only one FluxInstance per cluster, and it must be named flux.
  • The operator exposes a FluxReport resource for cluster-wide reconciliation health.

FluxInstance with gitless OCI sync (recommended)

apiVersion: fluxcd.controlplane.io/v1
kind: FluxInstance
metadata:
  name: flux
  namespace: flux-system
spec:
  distribution:
    version: "2.x"
    registry: "ghcr.io/fluxcd"
  sync:
    kind: OCIRepository
    url: "oci://ghcr.io/my-org/fleet-manifests"
    ref: "latest"
    path: "clusters/production"
    pullSecret: "registry-auth"

Check FluxReport health

kubectl get fluxreport flux -n flux-system -o yaml

Install Flux Operator

helm install flux-operator oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator \
  --namespace flux-system \
  --create-namespace

ResourceSet and ResourceSetInputProvider

Use ResourceSet to apply the same template to N inputs (tenants, environments, services) without duplicating Kustomization or HelmRelease manifests.

Template delimiters: << inputs.field >> — not {{ }}.

apiVersion: fluxcd.controlplane.io/v1
kind: ResourceSet
metadata:
  name: tenant-deployments
  namespace: flux-system
spec:
  inputsFrom:
    - kind: ResourceSetInputProvider
      name: tenant-tags
  resources:
    - apiVersion: kustomize.toolkit.fluxcd.io/v1
      kind: Kustomization
      metadata:
        name: "<< inputs.tenant >>-app"
        namespace: flux-system
      spec:
        interval: 10m
        prune: true
        sourceRef:
          kind: OCIRepository
          name: "<< inputs.tenant >>-manifests"
        path: ./deploy

ResourceSetInputProvider — OCIArtifactTag (gitless image automation)

apiVersion: fluxcd.controlplane.io/v1
kind: ResourceSetInputProvider
metadata:
  name: tenant-tags
  namespace: flux-system
spec:
  type: OCIArtifactTag
  url: "oci://ghcr.io/my-org/tenant-app"
  filter:
    semver: "1.x"
  interval: 5m
  secretRef:
    name: registry-auth

Gitless (OCI-based) delivery

The gitless model pushes manifests as OCI artifacts to a container registry. Flux pulls from the registry — no Git credentials on clusters, artifacts are immutable and can be signed.

Why prefer gitless for Flux Operator deployments:

  • No Git polling lag
  • No bot credentials on clusters
  • Artifacts are immutable and signable (Cosign)
  • Works natively with FluxInstance.spec.sync.kind: OCIRepository

Push manifests as OCI artifact (CI side)

flux push artifact oci://ghcr.io/my-org/fleet-manifests:latest \
  --path=./clusters/production \
  --source="$(git config --get remote.origin.url)" \
  --revision="$(git rev-parse HEAD)"

OCIRepository source

apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
  name: fleet-manifests
  namespace: flux-system
spec:
  interval: 5m
  url: oci://ghcr.io/my-org/fleet-manifests
  ref:
    tag: latest
  secretRef:
    name: registry-auth

HelmRelease from OCI (recommended over HTTPS)

apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
  name: cert-manager-chart
  namespace: cert-manager
spec:
  interval: 1h
  url: oci://quay.io/jetstack/charts/cert-manager
  layerSelector:
    mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
    operation: copy
  ref:
    semver: "1.x"
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
  namespace: cert-manager
spec:
  interval: 1h
  chartRef:
    kind: OCIRepository
    name: cert-manager-chart
  install:
    strategy:
      name: RetryOnFailure
      retryInterval: 5m

Reactivity: reconcile.fluxcd.io/watch

Adding the label reconcile.fluxcd.io/watch: Enabled to a ConfigMap or Secret causes any Kustomization or HelmRelease that references it via substituteFrom or valuesFrom to reconcile immediately when that resource changes — bypassing the normal poll interval.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: flux-system
  labels:
    reconcile.fluxcd.io/watch: Enabled   # triggers immediate reconciliation on change
data:
  APP_VERSION: "1.4.2"

Use this label on every ConfigMap or Secret referenced in substituteFrom or valuesFrom.

Common mistakes

MistakeCorrect approach
Using {{ inputs.field }} in ResourceSet templatesUse << inputs.field >>
Setting both spec.chart.spec and spec.chartRef on a HelmReleaseThese are mutually exclusive — use spec.chartRef for OCI, spec.chart.spec for HTTPS
Multiple FluxInstance resources in a clusterOnly one, must be named flux
Using RemediateOnFailure or install.remediation.retriesUse install.strategy.name: RetryOnFailure
OCIRepository for a Helm chart without layerSelectorSet layerSelector.mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
Missing reconcile.fluxcd.io/watch: Enabled on substituteFrom/valuesFrom ConfigMapsAdd the label so changes trigger immediate reconciliation
gotk-sync.yaml in repo rootIndicates flux bootstrap install — migrate to Flux Operator for lifecycle management

Image automation

Flux supports two image automation models. Use the gitless model for Flux Operator deployments; use the Git-based model when Git is the canonical version record and PR-based approval is required.

Comparison

DimensionGit-basedGitless (OCIArtifactTag)
How version flowsFlux commits updated tag back to GitCI pushes new OCI artifact tag; Flux ResourceSet reads it
Git credentials on clusterYes (deploy key with write access)No
Audit trailGit commit historyOCI registry tag history
PR approvalSupported (push to branch, open PR)Not applicable — version is the artifact tag
Recommended forFlux bootstrap installs, PR-gated promotionFlux Operator deployments, fleet management

Git-based image automation

Flux image automation watches a container registry and commits updated image tags back to Git. The Git commit then triggers normal reconciliation.

Components

ResourcePurpose
ImageRepositoryPolls a container registry for available tags
ImagePolicySelects which tag to deploy using semver, alphabetical, or numerical rules
ImageUpdateAutomationCommits the selected tag back to the GitOps branch

Setup

apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: my-app
  namespace: flux-system
spec:
  image: ghcr.io/your-org/my-app
  interval: 5m
  secretRef:
    name: registry-credentials
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: my-app
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: my-app
  policy:
    semver:
      range: ">=1.0.0 <2.0.0"
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: flux-system
  namespace: flux-system
spec:
  interval: 5m
  sourceRef:
    kind: GitRepository
    name: flux-system
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: fluxcdbot@users.noreply.github.com
        name: fluxcdbot
      messageTemplate: 'chore: update {{range .Updated.Images}}{{println .}}{{end}}'
    push:
      branch: main
  update:
    path: ./clusters/production
    strategy: Setters

Mark the image field in the deployment manifest:

containers:
  - name: my-app
    image: ghcr.io/your-org/my-app:1.0.0  # {"$imagepolicy": "flux-system:my-app"}

Gitless image automation

CI publishes a new OCI artifact tag. A ResourceSetInputProvider of type OCIArtifactTag polls the registry and feeds the selected tag into a ResourceSet, which generates the Kustomization or HelmRelease with the new version — no Git commit, no bot credentials.

apiVersion: fluxcd.controlplane.io/v1
kind: ResourceSetInputProvider
metadata:
  name: app-image-tag
  namespace: flux-system
spec:
  type: OCIArtifactTag
  url: "oci://ghcr.io/my-org/my-app"
  filter:
    semver: "1.x"
  interval: 5m
  secretRef:
    name: registry-auth
---
apiVersion: fluxcd.controlplane.io/v1
kind: ResourceSet
metadata:
  name: my-app
  namespace: flux-system
spec:
  inputsFrom:
    - kind: ResourceSetInputProvider
      name: app-image-tag
  resources:
    - apiVersion: kustomize.toolkit.fluxcd.io/v1
      kind: Kustomization
      metadata:
        name: my-app
        namespace: flux-system
      spec:
        interval: 10m
        prune: true
        sourceRef:
          kind: OCIRepository
          name: my-app-manifests
        postBuild:
          substitute:
            APP_IMAGE_TAG: "<< inputs.tag >>"

Registry authentication

Create a docker-registry Secret for any private registry:

kubectl create secret docker-registry registry-credentials \
  --namespace=flux-system \
  --docker-server=ghcr.io \
  --docker-username=<username> \
  --docker-password=<token>

Registry-specific notes:

RegistryToken typeExpiry
GitHub Container Registry (GHCR)Personal Access Token or GitHub App tokenNo expiry for PAT; App tokens expire — automate refresh
AWS ECRTemporary token via aws ecr get-login-password12 hours — requires automated refresh (CronJob or aws-ecr-credential-helper)
Azure Container Registry (ACR)Service principal secret or Workload IdentitySP secrets expire based on policy; prefer Workload Identity
Google Artifact Registry (GAR)Service account key or Workload IdentitySA keys don't expire but should be rotated; prefer Workload Identity
Docker HubAccess tokenNo expiry; rate limiting applies to anonymous pulls — always authenticate

For any registry that issues short-lived tokens, automate the Secret refresh before expiry.

Troubleshooting image automation

# Check ImageRepository is scanning successfully
kubectl -n flux-system describe imagerepository my-app

# Check what tag the policy selected
kubectl -n flux-system get imagepolicy my-app \
  -o jsonpath='{.status.latestImage}'

# Check automation controller logs
kubectl -n flux-system logs deploy/image-automation-controller | tail -50

# Check if the Git commit was pushed
kubectl -n flux-system describe imageupdateautomation flux-system

Common failures:

SymptomCauseFix
ImageRepository not ready, auth errorRegistry credentials missing, wrong, or expiredRecreate the registry-credentials Secret with a valid token; check registry-specific expiry
ImagePolicy shows no latestImageNo tags match the policy rangeVerify pushed tags conform to the semver range; check with crane ls <image>
No Git commit despite policy selecting a tagMarker comment absent or malformed in manifestConfirm the # {"$imagepolicy": "..."} comment is on the same line as image:
ImageUpdateAutomation failing to pushDeploy key lacks write access to the branchRotate the deploy key with write permission; or use a GitHub App token

Safety rules for image automation

  • Set push.branch to a staging branch for staging clusters. For production, prefer committing to a release branch and merging via PR rather than direct main pushes.
  • Restrict the automation's Git credentials to the specific path it manages — do not reuse the bootstrap deploy key.
  • Use semver ranges rather than latest or alphabetical unless the registry enforces a reliable tag convention.
  • For registries with short-lived tokens: automate Secret refresh on a schedule shorter than the token TTL.

BEFORE_AFTER.md

CHANGELOG.md

CODE_OF_CONDUCT.md

COMMANDS.md

CONTRIBUTING.md

EDITOR_INTEGRATIONS.md

GETTING_STARTED.md

HOW_IT_WORKS.md

install.sh

INSTALLATION.md

LAUNCH.md

PROMPTS.md

QUICKSTART.md

README.md

renovate.json

SECURITY.md

SKILL.md

tessl.json

tile.json