App charts
An app chart is the top-level Helm chart a project deploys. It owns the main workload and lists the components it pulls in as dependencies in its Chart.yaml.
Common shape
Every app chart has roughly the same structure:
my-app-chart/
├── Chart.yaml ← name, version, dependencies on components/
├── values.yaml ← defaults
└── templates/
├── deployment-app.yaml ← the main workload (Deployment or StatefulSet)
└── service-app.yaml ← the matching Service
The CronJobs, worker Deployments, and manual jobs come from component-tasks, the ingress from component-traefik-ingress, secrets from component-external-secret, etc. App chart templates do not duplicate those resources.
Service and pod labels
service-app.yaml exposes the main workload as a ClusterIP Service. Its selector is two labels, not one:
selector:
app: <global.nameOverride>
component: web
The main workload's pod template carries both app and component: web. The Deployment/StatefulSet .spec.selector deliberately stays on app alone — that field is immutable, so the component: web discriminator lives only on the pod template (an additive label change is a normal rolling update; changing the selector would require recreating the workload).
Cron, worker, and manual job pods from component-tasks share the same app label but are not web servers and never get component: web. The two-label selector is what keeps the Service pointed only at web pods — a bare app: selector also matches queue workers, routing a share of HTTP requests to pods with nothing listening on the web port (intermittent 502s / half-loaded pages).
Available app charts
symfony— Symfony PHP applications, StatefulSet, expects/health+/readyphp-generic— Generic PHP apps, same workload shape as symfony, with toggleable probesnode-generic— Generic Node.js apps on port 3000sveltekit— SvelteKit on port 3000, Deployment (not StatefulSet)