Skip to content
Kubernetes GitOpsUsing ApplicationGroup

Using ApplicationGroup

See the ApplicationGroup resource reference for the API, example, and field definitions.

DeploymentTarget.spec.applicationGroupSelector.matchLabels matches the group’s static metadata labels. An empty target selector matches every group. After selection, spec.enabled may be structurally templated per target.

A group that declares neither projectRef nor projectTemplate owns an implied AppProject named after the group. It admits only what the group’s Applications deploy: the target publication repository, the target workload Cluster with the group’s Release namespaces, and the resource kinds the group renders. Nothing outside the group needs to exist for it to render.

projectRef retains a reusable AppProjectDefinition contract. A Rendered definition is copied into the target catalog once; an External definition only supplies the project name.

projectTemplate generates the group’s AppProject. name, labels, annotations, and finalizers set its metadata, and the remaining fields mirror the Argo CD AppProject spec: description, sourceRepos, sourceNamespaces, destinations, clusterResourceWhitelist, clusterResourceBlacklist, namespaceResourceWhitelist, namespaceResourceBlacklist, roles, syncWindows, orphanedResources, signatureKeys, permitOnlyProjectScopedClusters, and destinationServiceAccounts. Declared fields are kept as written. Every omitted field is derived from the group, so a bare template only renames the implied project:

projectTemplate:
name: workloads
sourceRepos:
- https://git.example.com/deploy.git
destinations:
- server: https://kubernetes.default.svc
namespace: workloads
- server: https://kubernetes.default.svc
namespace: 'preview-*'
clusterResourceWhitelist:
- group: apiextensions.k8s.io
kind: CustomResourceDefinition
Omitted fieldGenerated value
sourceReposThe target publication repository
destinationsThe target workload Cluster with each Release destination and additional namespace
clusterResourceWhitelistThe cluster-scoped kinds the group renders, including Namespaces Nyl creates for it
namespaceResourceWhitelistThe namespaced kinds the group renders

The whitelists need each rendered kind’s scope, which comes from a CRD rendered in the same target or from the Cluster’s recorded capabilities. Recapture a Cluster with nyl capture cluster when rendering reports an unknown scope, or declare both whitelists.

The name defaults to the ApplicationGroup name. Nyl always adds applicationNamespace to sourceNamespaces. Each destinations entry names a server or name pattern and a namespace pattern. A fixed destinationNamespace not covered by an entry for the target workload Cluster is added for that Cluster. Every effective Release destination and additionalNamespaces entry must match an entry that admits the target workload Cluster.

A declared clusterResourceWhitelist, including [], admits only its patterns; when namespace creation is enabled, Nyl then adds Namespace permissions for the target Cluster’s admitted namespace patterns. Argo CD’s AppProject admission remains the authorization boundary.

Each Release produces one workload Application named after the Release. When several targets place Applications into the same Argo CD namespace on the same control-plane Cluster, set applicationNameTemplate so their names differ. Targets on implicit per-target instances of one Cluster share the argocd namespace, so they need it too. Clusters count as one control plane when they share an Argo CD destination, and nyl validate compares the names every target generates, so a template without the target, or Releases of the same name in different groups, are reported as well:

spec:
applicationNameTemplate: '${ target.metadata.name }-${ release.metadata.name }'

The field is a template value: the structural pass leaves ${ … } untouched, and Nyl evaluates each expression once per Release with the target context and release. Expressions and filters work as in {{ … }}; blocks do not. An undefined variable or attribute is an error. Write $${ for a literal ${. All other text is literal, including {{ … }}. The field must contain at least one ${ … } expression; the former {% raw %}{{ … }}{% endraw %} form is no longer expanded and fails validation, so rewrite it with ${ … }.

spec.source is optional. Without it, a central group derives applications/<group-name>, while _application-group.yaml derives its containing directory.

repositoryRef and repository are mutually exclusive. A repository makes the source remote and requires both revision and commit. Remote renderer mode requires a remote source. Run nyl update source-locks to refresh commit locks.

Source selectors identify candidate entry files. Nyl renders only candidates containing a literal, parseable k8s.nyl/v1 Release document; other files are ignored. It warns for each candidate that has no literal Release and is not claimed by another Release’s spec.include. Use Release.spec.include to attach additional relative files or glob matches to that release.

Foreground and Background add the corresponding Argo CD resources finalizer. Orphan omits it. For Namespace policy, Confirm writes Prune=confirm or Delete=confirm; Retain writes Prune=false or Delete=false; Automatic adds no restriction.

For a namespace consumed by one workload Application, the Namespace object is part of that Application’s rendered resources. Nyl synthesizes a missing destination Namespace and every Namespace listed in Release.spec.additionalNamespaces when spec.namespace.create is enabled.

The Kubernetes bootstrap namespaces default, kube-system, kube-public, and kube-node-lease implicitly use owner.kind: External. Releases may use them when their namespace scope allows it, but Nyl neither synthesizes nor accepts an authored Namespace object. An explicit sharedNamespaces owner overrides this default when the platform deliberately delegates ownership.

Every other namespace consumed by more than one workload Application must have an identical spec.sharedNamespaces.<namespace>.owner declaration in every contributing ApplicationGroup. The owner kinds are:

Release ownership rejects Namespace objects rendered by other Releases. Dedicated and External ownership reject Namespace objects rendered by any workload Release. These checks prevent two Argo CD Applications from claiming the same resource instead of silently discarding an authored Namespace.

spec.releaseCustomization.allowedPaths and deniedPaths are dotted glob lists applied to Release.spec.argocd.applicationOverride. * matches one path segment and ** matches multiple segments. Deny wins.

Every generated directory Application defaults to ApplyOutOfSyncOnly=true and ServerSideApply=true. Set an option to false in the ApplicationGroup sync options to override that generated default. A resource can opt out of server-side apply with the argocd.argoproj.io/sync-options: ServerSideApply=false annotation.

spec.releaseCustomization.allowedSyncOptions is an exact allow-list for sync options that a Release may merge with spec.syncPolicy.+syncOptions in its Application override:

apiVersion: k8s.nyl/v1
kind: Release
metadata:
name: api
namespace: api
spec:
argocd:
applicationOverride:
spec:
syncPolicy:
+syncOptions:
- RespectIgnoreDifferences=false

Every merged value must appear verbatim in allowedSyncOptions. Nyl replaces an existing option with the same key, so an approved ApplyOutOfSyncOnly=false value overrides the generated default without emitting conflicting entries. A plain syncOptions key attempts replacement and remains forbidden.

Core identity, finalizers, project, sources, destination, and sync policy other than explicitly allowed sync-option additions are platform-owned and cannot be customized, even when an allowed path pattern matches. With no allowed paths, ordinary release overrides are rejected.

The ApplicationGroup spec can vary per target or render to no document to omit the group. Its API version, kind, and metadata name remain static. Remote source coordinates and commit locks must remain statically parseable for nyl update source-locks.