How does a Flux ImagePolicy decide which tag in a scanned repository is the latest, and what do you do when the tags are not semver?
answer
- one rule per policy, three to choose from
- semver knows 1.10 beats 1.9
- string ordering breaks on 9 versus 10
- a regex decides who competes
- a capture group decides what is compared
basics
~20 sA Flux ImagePolicy applies exactly one rule under spec.policy — semver with a range, numerical with an order, or alphabetical with an order. For non-semver tags you first narrow and reshape them with spec.filterTags, whose regex capture group feeds extract.
solid answer
~50 sAn `ImagePolicy` references a scanned `ImageRepository` and ranks its tags with one rule. `semver` takes a `range` such as `>=1.0.0 <2.0.0` and understands version precedence, so `1.10.0` correctly beats `1.9.0` and prereleases are excluded unless the range asks for them. `numerical` sorts by number with `order: asc` or `desc`, which suits build numbers and Unix timestamps. `alphabetical` sorts as strings, which is right for date-shaped tags like `2026-08-21` and wrong for anything where `9` should beat `10`. When tags carry more than a version — the common CI pattern `main-<sha>-<timestamp>` — you add `spec.filterTags` with a `pattern` containing a named capture group and an `extract` such as `$ts`; the policy then orders on the extracted value while still selecting the full original tag. The selected image appears in the policy's status, and `flux get image policy` is how you confirm your rule matched what you expected.
code
yaml · 14 linesapiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: app-staging
namespace: flux-system
spec:
imageRepositoryRef:
name: app
filterTags:
pattern: '^main-[a-fA-F0-9]+-(?P<ts>[0-9]+)$'
extract: '$ts'
policy:
numerical:
order: ascgo deeper
Know that a policy picks one tag out of the scanned list using a rule you configure, and that semver ranges are the usual choice for released versions.
Explain all three rules and their failure modes, and show how filterTags narrows candidates while extract changes what they are compared on. Be able to read a policy's selected image with the Flux CLI.
Reason about a policy that looks broken but is behaving correctly — a range too narrow, a pattern that matches nothing, prereleases excluded — and distinguish those from a scan that never saw the tag.
Set the convention: which environments track ranges versus build tags, whether prereleases are ever admitted, and what tag shape CI must produce so ordering is unambiguous across every team's repository.
## What the object is for An `ImagePolicy` is pure selection. It reads the tag list an `ImageRepository` has already scanned, orders it by one rule, and publishes the winning image in its status. It writes nothing to Git and touches nothing in the cluster — that is `ImageUpdateAutomation`'s job. This separation is why several policies can share one scan: production and staging can rank the same tag list differently. ```yaml apiVersion: image.toolkit.fluxcd.io/v1beta2 kind: ImagePolicy metadata: name: app namespace: flux-system spec: imageRepositoryRef: name: app policy: semver: range: '>=1.0.0 <2.0.0' ``` ## The three rules **semver** is the one to reach for when your releases are versioned. The `range` uses standard semver range syntax, and ordering is by version precedence rather than string order, so `1.10.0` ranks above `1.9.0` — the single most common reason to prefer it over alphabetical. Ranges also encode policy: `>=1.0.0 <2.0.0` accepts minors and patches but never a major; `~1.4.0` accepts patches only. Prerelease tags such as `1.5.0-rc.1` are not selected by an ordinary range; you must ask for them explicitly. **numerical** sorts tags as numbers with `order: asc` (highest wins) or `order: desc`. It fits monotonically increasing build numbers and Unix timestamps. It is also the rule you normally pair with `filterTags`, because raw CI tags are rarely bare numbers. **alphabetical** sorts as strings. It is correct for tags that are lexically ordered by construction — ISO dates like `2026-08-21`, or zero-padded counters like `000042`. It is a trap everywhere else, because `9` sorts after `10` as a string, so an unpadded build counter silently freezes on `9` forever. ## filterTags: narrowing and reshaping Real pipelines rarely push clean version tags. A typical repository holds `latest`, `main-a1b2c3d-1724169600`, `pr-482`, and a handful of release tags all at once. `spec.filterTags` solves this in two steps: ```yaml spec: imageRepositoryRef: name: app filterTags: pattern: '^main-[a-fA-F0-9]+-(?P<ts>[0-9]+)$' extract: '$ts' policy: numerical: order: asc ``` The `pattern` is a regular expression; any tag that does not match is removed from consideration entirely. That alone eliminates `latest` and the PR tags. The named capture group `(?P<ts>...)` plus `extract: '$ts'` then says "order on this substring, not the whole tag". The policy ranks by the extracted timestamp but still selects — and reports — the complete original tag, which is what ends up in your manifest. This two-part behaviour is the piece candidates most often get wrong. Filtering is not the same as extraction: `pattern` decides which tags compete, `extract` decides what they are compared on. ## Reading the result `flux get image policy` prints each policy and the image it currently selects. This is your ground truth when automation appears stuck: if the policy already shows the tag you expect, the problem is downstream in the automation or the markers; if it shows an older tag or nothing at all, the problem is here or in the scan. ## The traps worth naming in an interview - **The range excludes what you wanted.** A `~1.4.0` range never picks up `1.5.0`, and the automation stays silent forever. This looks like a broken automation and is actually correct behaviour. - **Prereleases.** Teams tag `2.0.0-rc.1`, expect it to be picked up in staging, and are surprised when a plain range ignores it. - **Alphabetical on numbers.** Works for months while build numbers are single-digit, then stops advancing at `9`. - **A pattern that matches nothing.** Tighten a regex, and the candidate set becomes empty; the policy then selects nothing, which is quiet rather than loud. - **The tag exists but was never scanned.** The policy can only rank what the ImageRepository cached at its last scan, so a tag pushed 30 seconds ago may simply not be there yet. ## Choosing per environment Because policies are cheap and share a scan, the idiomatic setup is one policy per environment against the same ImageRepository: staging filters CI build tags and takes the newest by timestamp, production takes a narrow semver range. The environments then differ by a few lines of YAML rather than by a different pipeline, and the difference itself is reviewable in Git.
- Your CI pushes tags like main-a1b2c3d-1724169600. Which rule and configuration would you use?Filter first, then order numerically. A `filterTags.pattern` anchored on the `main-` prefix removes `latest` and PR tags, and a named capture group around the trailing timestamp with `extract: '$ts'` gives the policy a comparable number. Set `numerical` with `order: asc` so the highest timestamp wins. The full original tag is still what gets selected and written.
- Why is alphabetical ordering risky for build numbers?It compares tags as strings, so `10` sorts before `9` and the policy stops advancing once the counter reaches double digits. Nothing errors — the automation simply never proposes a newer tag. Use `numerical` for counters, or zero-pad the tags at build time if you must keep string ordering.
- Does an ImagePolicy pick up a prerelease tag like 2.0.0-rc.1?Not with an ordinary semver range. Semver precedence treats a prerelease as lower than the corresponding release and standard ranges exclude them, so `>=1.0.0 <3.0.0` will not select `2.0.0-rc.1`. If you want release candidates in staging, write a range that admits prereleases explicitly, and keep production on one that does not.
saying these in an interview costs you the question
- Thinks the newest pushed tag automatically wins
- Uses alphabetical ordering for numeric build counters
- Confuses filterTags pattern with extract
- Expects prerelease tags to match a plain semver range
- Believes a policy can combine semver and numerical at once