App of apps: one bad child in the fleet
The situation
One Application was a pet (10.1). Real platforms run fleets — and the fleet dashboard is a single kubectl:
kubectl -n argocd get applications
# NAME SYNC STATUS HEALTH STATUS
# shop-backend Synced Healthy
# shop-frontend Unknown Healthy ← there
# shop-reports Synced Healthy
Three Applications deploy the shop, ordered by sync waves — check the annotations:
kubectl -n argocd get applications -o custom-columns='NAME:.metadata.name,WAVE:.metadata.annotations.argocd\.argoproj\.io/sync-wave,REV:.spec.source.targetRevision'
# shop-backend 0 HEAD
# shop-frontend 1 stable
# shop-reports 2 HEAD
Waves order work within a sync: wave 0 must be Synced+Healthy before wave 1 starts — dependency ordering (databases before apps before dashboards) expressed as data, not pipeline scripts.
shop-frontend pins targetRevision: stable. The lesson-1 reflex:
kubectl -n argocd get application shop-frontend -o jsonpath='{.status.conditions}'
# ComparisonError … ambiguous argument 'stable': unknown revision or path…
The repo has no stable branch — someone imported a convention from a
different repo. The child can't render, so it can never be Synced.
The pattern this drill is a slice of
In production these three children wouldn't be applied by hand — a parent Application points at a git directory containing Application manifests, so Argo CD manages Argo CD:
# the parent — "app of apps"
spec:
source:
repoURL: https://github.com/your-org/platform
path: apps/ # a directory of Application YAMLs like these three
destination:
server: https://kubernetes.default.svc
namespace: argocd # children land in argocd's own namespace
Adding an app to the platform = committing one Application manifest to
apps/. The parent syncs it into existence; deleting the file (with a
prune policy) retires it. The failure mode you're debugging — one child
red, fleet stuck — reads identically whether children came from a parent
or a pipeline; the triage below is the same.
┌─────────────────┐
│root Application │
│ │
└─────────────────┘
│
sync first
│
▼
┌─────────┐
│ wave 0 │
│ │
└─────────┘
│
when healthy
│
▼
┌─────────┐
│ wave 1 │
│ │
└─────────┘
Your task
- Confirm the diagnosis on
shop-frontend(.status.conditions). - Fix the revision:
kubectl -n argocd patch application shop-frontend --type=merge \ -p '{"spec":{"source":{"targetRevision":"HEAD"}}}' - Watch the fleet converge — all three Synced/Healthy:
kubectl -n argocd get applications -w
Hint
HEAD follows the repo's default branch; a real platform pins children to
tags or SHAs (that's the point of targetRevision) — but the pin must
name something that exists. git ls-remote <repo> lists what does.
Solution
Fix
kubectl -n argocd patch application shop-frontend --type=merge \
-p '{"spec":{"source":{"targetRevision":"HEAD"}}}'
Fleet triage, the repeatable order
kubectl -n argocd get applications— the one-line fleet view; find the non-green rows.- Per red app:
.status.conditions(can it compare?) →.status.sync(does live match git?) →.status.health(is it up?). Same ladder as 10.1, applied N times. - Revision errors (
unknown revision), path errors (app path does not exist), and credential errors (authentication required) are the three ComparisonError classics — all fixed in the child'sspec.source.
Prevention / takeaway
- Sync waves encode dependency order declaratively; combine with the parent pattern and "bring up the whole platform in order" is one sync. (Resource-level hooks/waves exist too — same annotation on any object.)
- Pin children to tags/SHAs, not branches — but validate pins in CI
(
git ls-remoteis a one-line check) so a typo'd revision dies in review, not on the fleet dashboard. - The parent pattern means Application YAML lives in git too — the platform's shape gets code review, history, and rollback like everything else. That's the actual product of GitOps: the audit trail.
- ApplicationSets are the industrial version (generate children from cluster lists, monorepos, PR previews) — same status ladder when they break.
- Previous lesson
- Argo CD: the app that refuses to sync