# Flux dependencies: Git and OCI sources, Kustomizations and HelmReleases

> How Flux objects depend on each other: a GitRepository feeding two Kustomizations ordered with dependsOn, a HelmRelease from a HelmRepository, another from an OCIRepository, variable substitution and Slack alerts.

Interactive graph: https://k8wiz.com/graphs/flux-sources-kustomizations-helmreleases

Flux splits GitOps into sources and appliers. Source objects (GitRepository, OCIRepository, HelmRepository) fetch something and store it as an artifact inside the cluster. Applier objects (Kustomization, HelmRelease) take an artifact and apply it. Each kind has its own controller, and they only talk to each other through these objects.

Ordering comes from `dependsOn`. Here the `apps` Kustomization waits for `infra` to be ready, so cert-manager's CRDs exist before any app asks for a Certificate.

## Resources

### Git repository github.com/acme/fleet

outside the cluster

The fleet repo, with one directory per layer: `clusters/production` for the Flux objects themselves, `infrastructure/` for shared add-ons and `apps/production/` for the workloads.

### Secret flux-system

comes with an add-on, v1, namespace flux-system

The SSH deploy key that `flux bootstrap` created and added to the repo. source-controller uses it to clone.

### GitRepository flux-system

comes with an add-on, source.toolkit.fluxcd.io/v1, namespace flux-system

Every minute, source-controller fetches `main`, packs the files into a tarball artifact and records the commit in `status.artifact.revision`. Kustomizations read that artifact, not Git.

### Kustomization infra

you write it, kustomize.toolkit.fluxcd.io/v1, namespace flux-system

Applies `./infrastructure` from the Git artifact. `wait: true` makes it report ready only once everything it applied is healthy, which is what `dependsOn` waits for. `prune: true` deletes objects you remove from Git.

### Kustomization apps

you write it, kustomize.toolkit.fluxcd.io/v1, namespace flux-system

Applies `./apps/production`, but only after `infra` is ready. Before applying, it replaces `${cluster_name}` and other variables in the manifests with values from the `cluster-vars` ConfigMap.

### ConfigMap cluster-vars

you write it, v1, namespace flux-system

Values that differ per cluster, such as the cluster name and the base domain. The same `apps/` directory can then deploy to several clusters.

### HelmRepository jetstack

you write it, source.toolkit.fluxcd.io/v1, namespace flux-system

A classic HTTPS chart repository, stored in Git under `infrastructure/`. source-controller downloads its `index.yaml` every hour.

### HelmRelease cert-manager

you write it, helm.toolkit.fluxcd.io/v2, namespace cert-manager

Installs cert-manager from the `jetstack` repository, any 1.19 patch release. helm-controller turns `spec.chart` into a HelmChart object and installs whatever chart that produces.

### HelmChart cert-manager-cert-manager

created by a controller, source.toolkit.fluxcd.io/v1, namespace flux-system

Created by helm-controller from the HelmRelease's `spec.chart`, in the HelmRepository's namespace and named `<namespace>-<release>`. source-controller resolves `1.19.x` to a real version and stores the chart as an artifact.

### OCIRepository podinfo

you write it, source.toolkit.fluxcd.io/v1, namespace apps

Pulls the podinfo Helm chart as an OCI artifact from a container registry. With OCI there's no index to download: Flux lists the tags and picks the newest that matches `semver`. Signature checks with cosign can go here too.

### HelmRelease podinfo

you write it, helm.toolkit.fluxcd.io/v2, namespace apps

Installs the chart that the OCIRepository fetched, through `chartRef`. No HelmChart object is needed. `${base_domain}` is filled in by the `apps` Kustomization before this is applied.

### Deployment podinfo

created by a controller, apps/v1, namespace apps

Created by helm-controller as part of the Helm release. `helm list -n apps` shows the release, and Flux checks its health after every upgrade.

### Provider slack

you write it, notification.toolkit.fluxcd.io/v1beta3, namespace flux-system

Where notifications go: a Slack channel, through a bot token kept in a Secret.

### Alert prod-failures

you write it, notification.toolkit.fluxcd.io/v1beta3, namespace flux-system

Sends error events from every Kustomization and HelmRelease to the `slack` Provider, so a failed reconcile reaches a human.

## How the resources connect

- GitRepository flux-system → Git repository github.com/acme/fleet: clones every 1m (traffic)
- GitRepository flux-system → Secret flux-system: secretRef (references)
- Kustomization infra → GitRepository flux-system: sourceRef (references)
- Kustomization apps → GitRepository flux-system: sourceRef (references)
- Kustomization apps → Kustomization infra: dependsOn (references)
- Kustomization apps → ConfigMap cluster-vars: substituteFrom (references)
- Kustomization infra → HelmRepository jetstack: applies (creates)
- Kustomization infra → HelmRelease cert-manager: applies (creates)
- HelmRelease cert-manager → HelmChart cert-manager-cert-manager: creates (creates)
- HelmChart cert-manager-cert-manager → HelmRepository jetstack: sourceRef (references)
- Kustomization apps → OCIRepository podinfo: applies (creates)
- Kustomization apps → HelmRelease podinfo: applies (creates)
- HelmRelease podinfo → OCIRepository podinfo: chartRef (references)
- HelmRelease podinfo → Deployment podinfo: helm upgrade --install (creates)
- Alert prod-failures → Provider slack: providerRef (references)
- Alert prod-failures → Kustomization apps: eventSources (references)
- Alert prod-failures → HelmRelease podinfo: eventSources (references)

## How it works

1. **source-controller fetches Git.** Every minute the GitRepository clones `main` with the deploy key. When the commit changes, it stores a new artifact and updates `status.artifact.revision`, for example `main@sha1:4f2c…`.
2. **infra applies first.** kustomize-controller builds `./infrastructure` from the artifact and applies it with server-side apply. That creates the HelmRepository and the cert-manager HelmRelease. With `wait: true`, `infra` turns ready only when those report ready too.
3. **A HelmRelease from a HelmRepository.** helm-controller creates a HelmChart for cert-manager. source-controller picks the newest `1.19.x` from the repository index and packages it. helm-controller installs the chart and keeps upgrading as new patch releases appear.
4. **apps waits on dependsOn.** `apps` doesn't start while `infra` isn't ready. Once it is, `apps` builds `./apps/production`, replaces `${base_domain}` and `${cluster_name}` with the values from `cluster-vars`, and applies the result.
5. **A HelmRelease from an OCIRepository.** The OCIRepository pulls the podinfo chart straight from the registry, and the HelmRelease points at it with `chartRef`. helm-controller installs it, creating the Deployment. A new chart version in range triggers an upgrade without any commit.
6. **Failures reach Slack.** When a reconcile fails, the controller emits an error event. notification-controller matches it against the Alert's `eventSources` and posts it to the `deploys` channel through the Provider.

## Common mistakes

- `dependsOn` waits for the other Kustomization to be ready, and without `wait: true` or `healthChecks` ready only means "applied". The dependent then starts before the CRDs or webhooks it needs are serving.
- Changing a Kustomization's `path` or removing a whole directory with `prune: true` deletes what it applied. Rename carefully, and use `kustomize.toolkit.fluxcd.io/prune: disabled` on objects that must survive.
- A wildcard version such as `1.19.x` upgrades as soon as a new patch is published. Pin an exact version where you want upgrades to go through review.
- `flux get all -A` shows every source and applier with its ready state and last revision. `flux reconcile kustomization apps --with-source` fetches Git and applies right away instead of waiting for the interval.
- A variable that `substituteFrom` can't find is replaced with an empty string unless it has a default, such as `${base_domain:=example.com}`. A typo silently produces a broken hostname.

## The manifests you write

```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: infra
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./infrastructure
  prune: true
  wait: true
  timeout: 5m
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 10m
  dependsOn:
    - name: infra
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./apps/production
  prune: true
  postBuild:
    substituteFrom:
      - kind: ConfigMap
        name: cluster-vars
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-vars
  namespace: flux-system
data:
  cluster_name: prod-eu-1
  base_domain: example.com
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: jetstack
  namespace: flux-system
spec:
  interval: 1h
  url: https://charts.jetstack.io
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cert-manager
  namespace: cert-manager
spec:
  interval: 30m
  chart:
    spec:
      chart: cert-manager
      version: "1.19.x"
      sourceRef:
        kind: HelmRepository
        name: jetstack
        namespace: flux-system
  values:
    crds:
      enabled: true
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
  name: podinfo
  namespace: apps
spec:
  interval: 10m
  url: oci://ghcr.io/stefanprodan/charts/podinfo
  ref:
    semver: ">=6.7.0 <7.0.0"
  layerSelector:
    mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
    operation: copy
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: podinfo
  namespace: apps
spec:
  interval: 30m
  chartRef:
    kind: OCIRepository
    name: podinfo
  values:
    replicaCount: 2
    ingress:
      enabled: true
      hosts:
        - host: podinfo.${base_domain}
          paths:
            - path: /
              pathType: Prefix
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: slack
  namespace: flux-system
spec:
  type: slack
  channel: deploys
  secretRef:
    name: slack-bot-token
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: prod-failures
  namespace: flux-system
spec:
  providerRef:
    name: slack
  eventSeverity: error
  eventSources:
    - kind: Kustomization
      name: "*"
    - kind: HelmRelease
      name: "*"
      namespace: apps
```
