sync
The sync section defines the list of upstream images to mirror.
Structure
Section titled “Structure”sync: hooks: post_sync: - cmd: notify-team.sh timeout: 30s continue_on_error: true
entries: - source: docker.io/library/alpine tag_regex: '^3\.\d+\.\d+$' keep_last: 3 target: registry.example.com/mirrors/alpine lifecycle: product: alpine track: minorTop-level fields
Section titled “Top-level fields”| Field | Type | Required | Default | Description |
|---|---|---|---|---|
hooks.post_sync |
[]Hook | no | — | Commands to run after all sync entries complete |
platforms |
[]string | no | — | Default platforms to mirror for every entry (override per entry) |
Entry fields
Section titled “Entry fields”| Field | Type | Required | Description |
|---|---|---|---|
source |
string | yes | Upstream image reference (e.g. docker.io/library/alpine) |
target |
string | yes | Destination image reference in your registry |
tag |
string | one of tag/tag_regex | Sync a single fixed tag |
tag_regex |
string | one of tag/tag_regex | Sync all tags matching this regex |
keep_last |
integer | no | Keep only the N most recent matching tags |
os_normalize |
bool | no | Rename OS-variant tags (e.g. alpine3.21 → 1.22-alpine3.21) |
check_digest |
bool | no | Re-sync even if the tag already exists (digest check) |
platforms |
[]string | no | Restrict the mirrored platforms (overrides sync.platforms) |
lifecycle |
Lifecycle | no | EOL tracking for this image |
hooks |
Hooks | no | Per-entry hooks |
Tag selection
Section titled “Tag selection”Fixed tag
Section titled “Fixed tag”- source: docker.io/library/alpine tag: "3.21.3" target: registry.example.com/mirrors/alpineSyncs exactly one tag. Useful when you want to pin to a known-good version.
Tag regex
Section titled “Tag regex”- source: docker.io/library/alpine tag_regex: '^3\.\d+\.\d+$' keep_last: 3 target: registry.example.com/mirrors/alpineimglife queries Docker Hub for all tags matching the regex, sorts them by version (semantic or date fallback), and syncs the keep_last most recent. For Docker Hub images, dates are fetched in a single API call.
Common regex patterns
Section titled “Common regex patterns”| Pattern | Matches |
|---|---|
^3\.\d+\.\d+$ |
3.21.3, 3.20.6 — Alpine 3.x |
^1\.\d+\.\d+-alpine3\.\d+$ |
1.22.3-alpine3.21 — Go on Alpine |
^\d+\.\d+$ |
22.04, 24.04 — Ubuntu LTS |
^\d+\.\d+\.\d+$ |
Any semver |
Version lines
Section titled “Version lines”Several entries may share the same target. Each one is then an independent version line, identified by its tag / tag_regex:
- source: docker.io/library/python tag_regex: '^3\.12\.\d+-bookworm$' target: registry.example.com/mirrors/python keep_last: 2 lifecycle: { product: python, track: minor }
- source: docker.io/library/python tag_regex: '^3\.13\.\d+-bookworm$' target: registry.example.com/mirrors/python keep_last: 1 lifecycle: { product: python, track: minor }sync, status, cleanup, check, images and eol all resolve tags line by line — the pattern is anchored (^(?:…)$) against the destination tag, after normalisation when os_normalize is set. The displayed tag, the EOL cycle and keep_last never leak from one line to another: cleanup keeps keep_last tags per line, so the 3.13 line no longer evicts the 3.12 one. Tags of a mirror repository matching no line stay ranked together under retention.keep_last.
Regexes are compiled when the config file is loaded — an invalid pattern fails the command instead of silently matching nothing.
The same applies to build.images blocks sharing a folder.
Lifecycle tracking
Section titled “Lifecycle tracking”Add lifecycle tracking to connect EOL data to a sync entry:
- source: docker.io/library/alpine tag_regex: '^3\.\d+\.\d+$' target: registry.example.com/mirrors/alpine lifecycle: product: alpine # product slug on endoflife.date track: minor # how to derive the cycle from the tagtrack value |
Example tag | Resolved cycle |
|---|---|---|
minor |
3.21.3 |
3.21 |
major |
22.04 |
22 |
Digest re-sync
Section titled “Digest re-sync”By default imglife skips tags that already exist in the target registry. Enable check_digest: true to re-sync if the upstream digest has changed (e.g. a mutable tag like latest):
- source: docker.io/library/alpine tag: latest target: registry.example.com/mirrors/alpine check_digest: truePlatform selection
Section titled “Platform selection”By default imglife copies the upstream image as-is, preserving multi-arch
manifest lists (OCI image indexes). Set platforms to mirror only a subset.
The list resolves per entry: the per-entry platforms overrides the global
sync.platforms default; an empty list copies everything.
sync: platforms: - linux/amd64 # global default for every entry entries: - source: docker.io/library/alpine tag_regex: '^3\.\d+\.\d+$' target: registry.example.com/mirrors/alpine - source: docker.io/library/node tag: "22-alpine" target: registry.example.com/mirrors/node platforms: # per-entry override - linux/amd64 - linux/arm64Behaviour by list size:
| Platforms | Result |
|---|---|
| empty | Full copy — preserves the source manifest list / image as-is |
| one platform | A single-platform image (no index) |
| several | An image index reduced to that subset |
sync: entries: - source: docker.io/library/alpine tag_regex: '^3\.\d+\.\d+$' target: registry.example.com/mirrors/alpine hooks: post_sync: - cmd: curl -s https://hooks.slack.com/... -d '{"text":"alpine synced"}' timeout: 10s continue_on_error: trueHooks are also available at the top level of sync: and run after all entries complete. See Hooks reference for injected environment variables.
Parallelism
Section titled “Parallelism”Control how many sync entries run concurrently:
export IMGLIFE_SYNC_PARALLELISM=4 # default: 4Private source registries
Section titled “Private source registries”For sources that require authentication, provide credentials via environment variables using the normalised hostname:
export IMGLIFE_QUAY_IO_TOKEN=mytoken
# Source: ghcr.io/owner/imageexport IMGLIFE_GHCR_IO_USERNAME=myuserexport IMGLIFE_GHCR_IO_PASSWORD=mytokenSee Credentials for the full convention.