Observability
Structured output
Use --json for machine-readable sync reports:
ocync sync -c config.yaml --json
Reports include per-image results and aggregate statistics: blobs transferred, bytes moved, mounts performed, cache hits, and errors.
A mapping that could not be resolved at all — unreachable registry, denied tag listing, bad repository name — never reaches the engine and so produces no per-image entry. Those mappings are listed separately under unresolved_mappings, which is omitted entirely when every mapping resolved. The rest of the run still proceeds; one failing mapping does not cancel the others.
A mapping that resolved but lost one of several targets is not unresolved: it synced to the targets that worked. The ones it could not use appear under dropped_targets, also omitted when empty, and each is summarised in the cycle tail as | N targets dropped. A dropped target moves the exit code off success, because images did not reach a registry the config named.
Abbreviated example:
{
"run_id": "019713a2-...",
"images": [
{
"image_id": "019713a2-...",
"source": "cgr.dev/chainguard/nginx:latest",
"target": "123456789012.dkr.ecr.us-east-1.amazonaws.com/nginx:latest",
"status": "synced",
"bytes_transferred": 31457280,
"blob_stats": { "transferred": 3, "skipped": 1, "mounted": 2 },
"duration": { "secs": 4, "nanos": 210000000 }
}
],
"stats": {
"images_synced": 1,
"images_skipped": 0,
"images_failed": 0,
"blobs_transferred": 3,
"blobs_skipped": 1,
"blobs_mounted": 2,
"bytes_transferred": 31457280,
"discovery_cache_hits": 0,
"discovery_cache_misses": 1,
"discovery_head_failures": 0,
"discovery_target_stale": 0
},
"duration": { "secs": 4, "nanos": 210000000 },
"unresolved_mappings": [
{
"from": "cgr.dev/chainguard/private",
"error": "mapping 'cgr.dev/chainguard/private': registry error: 403 Forbidden"
}
],
"dropped_targets": [
{
"from": "cgr.dev/chainguard/nginx",
"registry": "backup-ecr",
"error": "mapping 'cgr.dev/chainguard/nginx': target registry 'backup-ecr' is unavailable: ECR auth setup for '...': 403 Forbidden"
}
]
}
analyze
ocync analyze --json reports images_analyzed, images_partial (recorded, but missing at least one platform of a multi-arch image), images_failed (could not be read at all), plus unresolved_mappings and dropped_targets in the same shape sync uses, so one consumer reads both. An analysis that could not read everything exits non-zero, because the totals are short by whatever it missed.
Progress indicators
ocync auto-detects the output environment:
- TTY: real-time progress bars with per-image and aggregate stats
- Non-TTY / CI: periodic heartbeat lines with summary counts
At the default verbosity, per-image lines are suppressed, so a long run would otherwise be silent from start to finish. Two periodic lines fill that gap:
preparing syncevery five seconds before the engine starts, carryingphase(registries,batch checkers, ormappings),done,total, andelapsed_secs. This window is sequential network work: a token mint per registry, then a tag listing per mapping.sync in progressevery 30 seconds while discovery or transfers are still in flight, carryingin_discovery,pending,in_flight,completed, andelapsed_secs
Both fire on wall-clock rather than on item boundaries, so a single slow mapping still reports, and a run that finishes inside one interval emits neither.
Disable all progress output with --quiet.
Logging
Control verbosity with -v flags:
| Level | Flag | Output |
|---|---|---|
| Info | (default) | Sync progress and results |
| Debug | -v | Auth events, cache decisions, per-image detail |
| Trace | -vv | HTTP requests, detailed internals |
| Error | -q / --quiet | Errors only |
-v also uncaps the per-reason sample list in --dry-run output (default cap: 5 tags per drop reason and 5 names in the literal include path).
Log format
# Human-readable (default everywhere, including Kubernetes)
ocync sync -c config.yaml -vv
# JSON for log-aggregation pipelines that parse structured fields
ocync sync -c config.yaml -vv --log-format json
When deployed via the chart, set logging.format: json in helm values to opt into JSON output.
Override with the RUST_LOG environment variable for fine-grained filter directives.
Health endpoints
In watch mode, ocync exposes HTTP health endpoints:
| Endpoint | Purpose | Healthy |
|---|---|---|
/healthz | Liveness probe | Process is running |
/readyz | Readiness probe | At least one successful sync completed |
Configure the port via --health-port (default 8080) or in Helm values:
mode: watch
watch:
healthPort: 8080
See CLI reference for all watch flags including --interval and --json.