6.9 KiB
08 — Adding a new app
This is the payoff for the app-of-apps structure: adding something new is "drop a file here," not a special procedure.
The pattern
Every app in this repo is two things sitting side by side in
apps/:
apps/
demo.yaml ← the Application (tells Argo CD where to look)
demo/ ← the actual manifests (what gets deployed)
To add a new app called, say, whoami:
-
Create
apps/whoami/and put its Kubernetes manifests in there — a Deployment, a Service, whatever it needs. Useapps/demo/as a template for structure and comment style. -
Create
apps/whoami.yaml, copied fromapps/demo.yamlwith the names changed:metadata.name,spec.source.repoURL(still this cluster's public Gitea repository — seedocs/04-app-of-apps.mdfor why every Application needs this set individually, not inherited),spec.source.path(→apps/whoami), andspec.destination.namespace. -
Commit and push both. That's it — no
kubectl apply. The app-of-apps (already watchingapps/— seedocs/04-app-of-apps.md) notices the newwhoami.yaml, creates thewhoamiApplication, which in turn deploys everything inapps/whoami/. -
Confirm:
argocd app listshould showwhoamialongsidedemo,metallb, andlonghorn.
If your new app needs storage, request it with a plain
PersistentVolumeClaim and no storageClassName — Longhorn's chart set
itself as the cluster's default StorageClass, so it's used automatically.
See docs/06-longhorn.md.
If your new app needs hostNetwork, hostPath volumes, or privileged
containers (uncommon, but real for things like storage or networking
addons), Kubernetes' Pod Security Admission will reject it by default —
see apps/metallb.yaml and apps/longhorn.yaml for the working pattern:
an Application-level syncPolicy.managedNamespaceMetadata.labels opting
just that namespace into the privileged profile. Ordinary apps like
whoami never need this.
Manifest-based vs. Helm-based apps
apps/demo/ is plain Kubernetes manifests — no templating, nothing to
render — which is why it's the easiest one to read start to finish while
you're still learning what each field does.
An app's folder can be a Helm chart instead. Argo CD detects this
automatically: if it finds a Chart.yaml at the source path, it treats
that app as a Helm chart rather than plain manifests. This matters most
when you want to pull in a third-party chart — a database, an ingress
controller, anything with a public Helm chart already maintained by
someone else — without copying their manifests into your repo by hand. You
declare it as a dependency and let Helm (via Argo CD) resolve it.
You don't have to imagine this one — it's already running.
apps/metallb/ is exactly this pattern, for real: its
Chart.yaml declares a dependency on the
upstream MetalLB chart, and it even goes a step further by also carrying
its own templates (apps/metallb/templates/)
for two custom resources that configure MetalLB once it's installed —
useful to see if you ever need an app that's mostly someone else's chart
but with a couple of your own resources layered on top. See
docs/05-metallb.md for the full walkthrough, and
apps/metallb.yaml for the matching Application —
notice it's structurally identical to apps/demo.yaml; Argo CD figures out
it's a Helm chart on its own from the presence of Chart.yaml, no new
syntax required in the Application resource itself.
Ask Claude: "walk me through how I'd bring in [some real chart you have in mind] as a Helm-based app in this structure" whenever you're ready to try one of your own —
apps/metallb/is a real template to copy from.
Reaching a new app: Ingress first, dedicated address only if you must
Now that docs/11-ingress.md is set up, that's the
default way a new app should become reachable — not its own
LoadBalancer Service. Give it a plain ClusterIP Service (the default
if you omit type entirely, as in apps/demo/service.yaml),
add an Ingress for a real hostname (see apps/demo/ingress.yaml),
and add the cert-manager.io/cluster-issuer: letsencrypt-staging annotation
to get it a real HTTPS certificate — proven against staging first, then
switched to letsencrypt-prod once confirmed (see
docs/12-cert-manager.md). Record the new hostname
in the hostnames table in docs/network.md.
A dedicated type: LoadBalancer Service (see
docs/05-metallb.md) is still the right choice for an app
that genuinely can't speak HTTP — a raw TCP or UDP service, for example,
since Ingress only routes HTTP(S). If you do need one, pick its address
from the free addresses listed in docs/network.md, and add
a row for it there once it's up — that file only stays useful if every
app that gets an address shows up in it.
Removing an app
The reverse of adding one: delete apps/<name>.yaml and apps/<name>/,
commit, and push. The app-of-apps notices the missing Application manifest
and prunes the Application object itself — but that alone does not
delete what that Application deployed. Argo CD only cascades an
Application's deletion into deleting its managed resources (namespace,
Deployment, Service, everything) if that Application carries this
finalizer:
metadata:
finalizers:
- resources-finalizer.argocd.argoproj.io
Without it, the resources are simply orphaned — still running, but no
longer tracked by Argo CD or described anywhere in Git. apps/demo.yaml
and apps/metallb.yaml both carry this finalizer. apps/longhorn.yaml
deliberately does not — see the comment in that file for why cascading
a storage layer's deletion is a different, much higher-stakes decision than
cascading a stateless app's.
Ask Claude: "what would happen right now if I deleted
apps/longhorn.yamland it had this finalizer?" before you ever add one to an app that holds real data.
Checklist
- New app's manifests live in
apps/<name>/ - New
apps/<name>.yamlApplication created, modeled ondemo.yaml repoURLset to this cluster's public Gitea repository, not left as a placeholder- Pushed to Git
argocd app listshows the new app,Synced/Healthy- Reachable through Ingress at a real hostname (recorded in
docs/network.md's hostnames table), unless it genuinely can't speak HTTP - If it does need its own dedicated
LoadBalancerService instead, its address is recorded indocs/network.md
Continue to docs/09-growing-up.md for what to add
next as this cluster grows beyond a learning setup.