Git, CI/CD & GitOpsLXXVIII · FluxSource
GitRepository and source-controller — the source of desired state
What you'll learn
- Describe what source-controller does and which CRDs it owns
- Read a GitRepository spec and identify the URL, branch, interval, and auth fields
- Recognise the four source kinds - GitRepository, OCIRepository, HelmRepository, Bucket - and when each applies
- Use `flux create source git` and `flux reconcile source git` to manage a GitRepository
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
source-controller is the first link in the Flux chain. It owns the four CRDs that describe where desired state lives and produces a tarball artifact that the rest of the controllers consume. Without a ready source artifact, no Kustomization reconciles and no HelmRelease installs.
What source-controller does
The controller watches its CRDs, fetches their referenced content at a declared interval, validates the content, packages it as a tarball, and writes the artifact reference into the CRD’s status section. The four CRDs:
GitRepository- a Git repository, cloned at a ref (branch, tag, commit SHA, semver range).OCIRepository- an OCI image pulled at a tag or digest.HelmRepository- a Helm chart repository (HTTP-servedindex.yamlplus chart tarballs).Bucket- an S3-compatible bucket polled for a key prefix.
flowchart LR
GR["GitRepository"] --> SC["source-controller"]
HR["HelmRepository"] --> SC
OR["OCIRepository"] --> SC
BK["Bucket"] --> SC
SC --> ART[("in-cluster artifact Secret")]
ART --> KC["kustomize-controller"]
ART --> HC["helm-controller"]
Every successful reconcile produces the same shape: a Secret
containing the artifact, and a status section pointing at it.
Downstream controllers read status.artifact.url, fetch over
HTTP using a service-account token, and never touch the
original source.
The GitRepository spec
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: apps
namespace: flux-system
spec:
url: https://github.com/example/apps
interval: 1m0s
ref:
branch: main
secretRef:
name: github-deploy-key
ignore: |
/*.md
/docs/**
timeout: 60s
suspend: false
spec.url- the clone URL. HTTPS is preferred for production; SSH is supported via akubernetes.io/ssh-authsecretRef.spec.interval- how often the controller re-fetches. Production default is1m0s.spec.ref- the reference to clone:branch,tag,semver, orcommit. Pinning to a commit SHA is the production rule for branch-tracked repos.spec.secretRef- a Secret in the same namespace holding credentials (kubernetes.io/basic-auth,ssh-auth, ortls).spec.ignore- paths to exclude from the artifact.spec.suspend- pauses reconciliation; the escape hatch for incident containment.
Creating and inspecting
The CLI command that produces the manifest above:
flux create source git apps \
--url=https://github.com/example/apps \
--branch=main \
--interval=1m0s \
--secret-ref=github-deploy-key \
--export
--export prints the CRD to stdout instead of applying it.
The lifecycle from create to observe:
flux create source git apps --url=... --branch=main
flux get source git apps
flux reconcile source git apps
flux reconcile source git apps writes an annotation that
forces the next reconcile out of cycle.
Status and the artifact
The status section is populated by the controller:
status.artifact.revision- the SHA resolved fromspec.refat fetch time. Pinningspec.refto a commit SHA returns the same SHA every fetch.status.artifact.url- the in-cluster HTTP URL of the tarball. Downstream controllers fetch from here.status.conditions[Ready]- the condition other controllers wait on.reason=GitOperationFailedmeans the clone failed;reason=AuthenticationFailedmeans the secret was rejected.
Under the hood
source-controller is small on purpose. It does not render manifests, does not apply resources, and does not know what Kustomize or Helm is.
Production discipline
spec.refis pinned to a SHA or semver range, not a branch head. Branch heads move; the audit chain breaks. Use CI to bump the SHA.spec.intervalis sized to the change rate. The interval is the upper bound on time-to-reconcile.spec.suspendis used during incident containment.kubectl patch gitrepository apps -p '{"spec":{"suspend":true}'freezes every Kustomization that consumes the source.
Cross-course references
- Kubernetes for Production Sysadmins - Part XI (CRD Status) covers the condition model the source CRDs use.
- Git, CI/CD & GitOps for Infrastructure Engineers - Part LXXV (Pull-based Deployment) is the operational pattern this controller implements; Part LXXVI (GitOps Repo Architecture) is the repository layout the GitRepository points at.
Quiz
Knowledge check · 4 questions
Q1. Which field in a GitRepository status section identifies the resolved commit SHA that downstream controllers consume?
Q2. Setting `spec.ref.branch: main` on a GitRepository guarantees the controller will deploy the same commit SHA every reconcile.
Q3. Name the four CRDs source-controller owns and identify which one is used for pulling a classic Helm chart from a chart repository.
Q4. Diagnose why every Kustomization consuming a GitRepository has been OutOfSync for an hour and identify the field to inspect first.
A team runs `flux get kustomizations` and sees that every Kustomization referencing the `apps` GitRepository is reporting Ready=False, reason=DependencyNotReady. The team runs `flux get source git apps` and sees status: Ready=False, reason=GitOperationFailed, message='authentication required'. The team's GitHub deploy key was rotated the previous day but the Secret in the cluster was not updated.
Passing score: 75%. Answers are checked in this browser.