skip to content

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?

level: middleimportance: should knowfreq 50%

answer

  1. one rule per policy, three to choose from
  2. semver knows 1.10 beats 1.9
  3. string ordering breaks on 9 versus 10
  4. a regex decides who competes
  5. a capture group decides what is compared

basics

~20 s

A 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 s

An `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 lines
yaml
apiVersion: 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: asc

go deeper

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context