Problem
The generated Helm chart covers common app resources. Teams still need things Deployah does not generate (NetworkPolicy, PrometheusRule, app-owned CRDs, and similar). Today there is no first-class place for that YAML, so users fall back to out-of-band kubectl or custom charts.
Goals
- Users can ship arbitrary Kubernetes manifests with a Deployah release
- Extra CRDs can be installed before the chart, with a clear create vs create-replace policy
deployah plan shows extras in the diff and reports pending CRDs
- DX-first layout under
.deployah/; no backward-compatibility constraint
Non-goals
- Helm templating or environment-variable substitution inside extras
- Deleting or pruning CRDs on uninstall
- A catalog / dependency manager for third-party charts (separate proposal)
- Full GitOps reconciliation or replacing Helm
Proposed design
Layout
.deployah/
manifests/
common.yaml # all environments
prod/
extra.yaml # only for prod (and prod/* prefix matches)
crds/
my-crd.yaml # shared; no per-env subdirs
deployah init scaffolds .deployah/manifests/ and .deployah/crds/ with short READMEs.
- Only
*.yaml / *.yml load; README/markdown and dotfiles are skipped; other visible files are errors.
- Subdirectories under
manifests/ must be declared environment keys. Nested dirs under an env dir (or under crds/) are not allowed.
- CRDs must use
apiVersion: apiextensions.k8s.io/v1 and live under .deployah/crds/ only.
Behavior
- Manifests: loaded literally (no Helm templating, no env substitution). Identity labels/annotations are merged into
metadata only; reserved deployah.dev/* keys the user does not own are stripped. Empty namespace is filled with the release namespace; a different namespace is an error. Objects are appended to the Helm release via a post-renderer.
- CRDs: applied before the Helm install/upgrade; wait for
Established (bounded by --timeout). Policy via --crds create (default) or --crds create-replace (server-side apply). Never pruned or deleted on uninstall.
- Plan: extra manifests appear in the diff; CRDs are reported as pending, not applied.
- Idle Helm plan: if the chart is unchanged but
.deployah/crds/ is non-empty, still apply CRDs.
- Safety: collide with a generated object -> fail; duplicate identities -> fail; unknown CR kinds need an in-repo CRD or cluster discovery (small offline allowlist for cert-manager / prometheus-operator);
plan --offline allows unknown kinds for preview.
Labels and annotations
| Object |
Labels |
Annotations |
| Generated (from the spec) |
deployah.dev/project, deployah.dev/environment, deployah.dev/component, ... |
deployah.dev/source=spec, ... |
| Extra manifests |
project + environment |
deployah.dev/source=manifests |
| Extra CRDs |
project only (no environment) |
deployah.dev/source=crds |
Alternatives considered
- Embed extras in
deployah.yaml: worse for large YAML and for copy-paste from existing manifests.
- Require a custom Helm chart: higher friction than a Bitnami-style escape hatch next to the spec.
- Apply everything with raw kubectl outside Helm: splits ownership; extras would not share the release lifecycle.
Compatibility and migration
New feature. No migration path required. Existing projects without .deployah/ are unchanged.
Test plan
Checklist
Acceptance criteria
Problem
The generated Helm chart covers common app resources. Teams still need things Deployah does not generate (
NetworkPolicy,PrometheusRule, app-owned CRDs, and similar). Today there is no first-class place for that YAML, so users fall back to out-of-bandkubectlor custom charts.Goals
deployah planshows extras in the diff and reports pending CRDs.deployah/; no backward-compatibility constraintNon-goals
Proposed design
Layout
deployah initscaffolds.deployah/manifests/and.deployah/crds/with short READMEs.*.yaml/*.ymlload; README/markdown and dotfiles are skipped; other visible files are errors.manifests/must be declared environment keys. Nested dirs under an env dir (or undercrds/) are not allowed.apiVersion: apiextensions.k8s.io/v1and live under.deployah/crds/only.Behavior
metadataonly; reserveddeployah.dev/*keys the user does not own are stripped. Empty namespace is filled with the release namespace; a different namespace is an error. Objects are appended to the Helm release via a post-renderer.Established(bounded by--timeout). Policy via--crds create(default) or--crds create-replace(server-side apply). Never pruned or deleted on uninstall..deployah/crds/is non-empty, still apply CRDs.plan --offlineallows unknown kinds for preview.Labels and annotations
deployah.dev/project,deployah.dev/environment,deployah.dev/component, ...deployah.dev/source=spec, ...deployah.dev/source=manifestsdeployah.dev/source=crdsAlternatives considered
deployah.yaml: worse for large YAML and for copy-paste from existing manifests.Compatibility and migration
New feature. No migration path required. Existing projects without
.deployah/are unchanged.Test plan
--crdsdeployah initscaffolds the directoriesChecklist
Acceptance criteria
.deployah/manifests/and.deployah/crds/load with the rules abovedeployah plan/deployah deploy(manifests in release; CRDs applied first)--crds create|create-replaceworks; idle Helm + CRDs still applies CRDsdeployah initscaffolds the directories--crds