Files
k8s-starter-pack/docs/08-adding-new-apps.md
T

5.8 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:

  1. Create apps/whoami/ and put its Kubernetes manifests in there — a Deployment, a Service, whatever it needs. Use apps/demo/ as a template for structure and comment style.

  2. Create apps/whoami.yaml, copied from apps/demo.yaml with the names changed: metadata.name, spec.source.repoURL (still this cluster's public Gitea repository — see docs/04-app-of-apps.md for why every Application needs this set individually, not inherited), spec.source.path (→ apps/whoami), and spec.destination.namespace.

  3. Commit and push both. That's it — no kubectl apply. The app-of-apps (already watching apps/ — see docs/04-app-of-apps.md) notices the new whoami.yaml, creates the whoami Application, which in turn deploys everything in apps/whoami/.

  4. Confirm: argocd app list should show whoami alongside demo, metallb, and longhorn.

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.

If the new app has its own LoadBalancer Service (see docs/05-metallb.md), 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.yaml and 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>.yaml Application created, modeled on demo.yaml
  • repoURL set to this cluster's public Gitea repository, not left as a placeholder
  • Pushed to Git
  • argocd app list shows the new app, Synced/Healthy
  • If it has a LoadBalancer Service, its address is recorded in docs/network.md

Continue to docs/09-growing-up.md for what to add next as this cluster grows beyond a learning setup.