GitOps (Argo CD & Flux)
Generate the NextApp CR with knext, commit it, and let Argo CD or Flux sync it, with the strict-validation caveat and how to guard against it.
knext's cluster contract is a single NextApp custom resource. That makes it a natural fit for
GitOps: CI builds and pushes the image and renders the CR; git holds the CR; Argo CD or Flux
applies it; the operator reconciles it. The knext CLI never needs cluster access in this flow.
1. Render the CR without touching the cluster
knext deploy --dry-run prints the NextApp CR to stdout and changes nothing. There is no
output-file flag; redirect stdout. Set LOG_LEVEL=silent so stdout carries only the YAML (see the
note under knext deploy).
LOG_LEVEL=silent knext deploy --dry-run \
--image registry.example.com/shop/storefront@sha256:<digest> \
-n shop > deploy/storefront/nextapp.yaml--image takes a pre-built, digest-pinned image, so nothing is built or pushed and the CR points at
exactly the image your pipeline produced. Without --image, a dry run renders the mutable tag
(fine for a preview, not for git).
The rendered CR reflects your knext.config.ts. If your app does not serve /api/health, set
healthCheckPath there; otherwise the revision never becomes ready.
Review the diff, commit it, and open a pull request like any other change.
Commit digests, never tags. The CR's spec.image must end in @sha256:…. The operator rejects
:latest and any unpinned reference, and a tag can change underneath a commit that did not change.
2. Sync it
Point your GitOps controller at the directory holding the CR. The namespace in the CR
(metadata.namespace) must already exist, or let the controller create it.
Argo CD
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: storefront
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.com/acme/deploy.git
targetRevision: main
path: deploy/storefront
destination:
server: https://kubernetes.default.svc
namespace: shop
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueFor many apps, an ApplicationSet with a git directory generator creates one Application per
folder:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: knext-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://git.example.com/acme/deploy.git
revision: main
directories:
- path: deploy/*
template:
metadata:
name: "{{path.basename}}"
spec:
project: default
source:
repoURL: https://git.example.com/acme/deploy.git
targetRevision: main
path: "{{path}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{path.basename}}"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueFlux
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: storefront
namespace: flux-system
spec:
interval: 5m
prune: true
sourceRef:
kind: GitRepository
name: deploy
path: ./deploy/storefront
targetNamespace: shop
healthChecks:
- apiVersion: apps.kn-next.dev/v1alpha1
kind: NextApp
name: storefront
namespace: shopThe Argo CD and Flux manifests above are standard, current-API examples. The generated CR itself
was applied and reconciled to Ready on the reference cluster; the Argo CD and Flux sync steps
were not verified on the reference cluster, which has neither controller installed.
3. The strict-validation caveat
knext's own CLI applies the CR with kubectl apply --validate=strict, so a field the cluster's
NextApp CRD does not know is rejected with a clear error. Argo CD and Flux do not apply
with strict validation. If your CR was rendered by a newer CLI than the operator running in the
cluster, an unknown field can be silently pruned: the sync succeeds, the app comes up, and the
setting you wrote simply is not there.
Two things keep you safe:
-
Upgrade order: operator and CRD first, then the CLI. A newer CRD understands every field an older CLI emits. See Upgrading knext.
-
Check in CI, before merge. Run
knext doctoragainst the target cluster in the pipeline that renders the CR. Its CRD schema check reports any field your CLI emits that the installed CRD does not define:shell knext doctor --jsonFail the pipeline if that check is not green. You can also reproduce the strict check yourself on the rendered file:
shell kubectl apply --dry-run=server --validate=strict -f deploy/storefront/nextapp.yaml
4. Image digests
The image in the committed CR is the source of truth for what runs. Pin it by digest
(registry/name@sha256:…) and update it through a pull request: the digest changes, git records
who changed it and when, and rolling back is a git revert. Tag-only references and :latest are
refused by the operator.