ApplicationInstance¶
Group/Version/Kind: apps.vworkspace.io/v1alpha1 ApplicationInstance
Scope: Namespaced
Status: Alpha
Last Updated: 2026-05-30
Overview¶
ApplicationInstance represents the intent "this application should exist in this namespace, deployed from this chart, with these chart parameters." The operator materializes a Flux HelmRelease (and the matching chart-source resource, an OCIRepository or HelmRepository) from the spec, watches it, and aggregates its conditions back into status.conditions. App-specific deployment logic stays in the upstream chart; the operator does not know what a Deployment or Service produced by the chart is for.
The reconciliation model is documented in ../concepts/reconciliation-model.md. The shorter conceptual tour is in ../concepts/crds.md. The reason this is a Helm-first CRD is in ../concepts/helm-first.md.
Example¶
apiVersion: apps.vworkspace.io/v1alpha1
kind: ApplicationInstance
metadata:
name: nextcloud-myteam
namespace: org-myteam
labels:
app.vworkspace.io/managed-by: control-plane
app.vworkspace.io/cluster-id: cluster-prod-1
annotations:
ops.vworkspace.io/backup: velero
ops.vworkspace.io/restore: velero
ops.vworkspace.io/upgrade: helm
spec:
appRef:
catalogId: nextcloud
chart:
sourceType: oci
url: oci://registry.example.com/charts
name: nextcloud
version: "6.6.0"
release:
name: nextcloud-myteam
namespace: org-myteam
values:
source: inline
inline:
ingress:
enabled: true
host: files.myteam.example.com
integrations:
externalSecrets:
enabled: true
certManager:
enabled: true
status:
observedGeneration: 3
conditions: []
helmReleaseRef:
name: nextcloud-myteam
The labels and annotations are documented in labels-and-annotations.md. The condition vocabulary is in conditions.md.
spec¶
appRef¶
| Field | Type | Required | Description |
|---|---|---|---|
appRef.catalogId |
string | Yes | Stable identifier for the catalog entry this instance corresponds to (e.g., nextcloud, mattermost, vaultwarden). The catalog entry lives in Odoo and carries the curated capability metadata. The operator does not look the catalog up at apply time; the value is recorded for fleet-wide queries and for the audit channel. |
chart¶
Identifies the Helm chart to deploy.
| Field | Type | Required | Description |
|---|---|---|---|
chart.sourceType |
enum | Yes | One of oci (an OCI registry hosting Helm charts) or helm (a classic Helm chart repository). The admission webhook rejects any other value. |
chart.url |
string | Yes | The base URL of the chart source. For oci, an oci:// URL pointing at the registry path (e.g., oci://registry.example.com/charts). For helm, an https:// URL pointing at the chart repository index. |
chart.name |
string | Yes | The chart name within the source. |
chart.version |
string | Yes | Exact chart version (semver). Version ranges are not allowed; the catalog is responsible for resolving "latest" or "recommended" to a concrete version before emitting the ApplicationInstance. |
release¶
Identifies the Helm release the operator will materialize.
| Field | Type | Required | Description |
|---|---|---|---|
release.name |
string | Yes | The Helm release name. Used as the metadata.name of the materialized HelmRelease and, conventionally, as the prefix for chart-generated objects. |
release.namespace |
string | Yes | The target namespace for the release. Must match metadata.namespace on the ApplicationInstance itself. The admission webhook rejects mismatches. |
values¶
Specifies how chart values are supplied to the release.
| Field | Type | Required | Description |
|---|---|---|---|
values.source |
enum | Yes | One of inline, secretRef, or configMapRef. Determines which of the sibling fields is read. |
values.inline |
object | Conditional | Required when values.source is inline. Free-form YAML mapping passed verbatim to the chart as values. |
values.secretRef |
object | Conditional | Required when values.source is secretRef. { name, key } referencing a Secret in the same namespace; the key holds the values document (YAML). |
values.configMapRef |
object | Conditional | Required when values.source is configMapRef. { name, key } referencing a ConfigMap in the same namespace; the key holds the values document (YAML). |
The operator never logs decrypted values material. When values are sourced from a Secret, the operator only reads the secret when reconciling the HelmRelease and does not include the content in events, conditions, or status fields.
integrations (optional)¶
Convenience knobs that toggle commonly used chart features. These do not replace chart values; they are syntactic sugar that the operator translates into the corresponding values fragments on a per-catalog-entry basis.
| Field | Type | Default | Description |
|---|---|---|---|
integrations.externalSecrets.enabled |
bool | false | Configure the chart to consume secrets through external-secrets. Requires external-secrets to be present on the cluster. |
integrations.certManager.enabled |
bool | false | Configure the chart's ingress to request TLS certificates via cert-manager. Requires cert-manager to be present on the cluster. |
integrations.ingress.enabled |
bool | false | Enable the chart's ingress definition. |
integrations.ingress.host |
string | — | Hostname for the ingress. Required when integrations.ingress.enabled is true. |
When both integrations.* and explicit chart values cover the same field, explicit values take precedence; the admission webhook reports a warning condition but does not reject the resource.
status¶
The operator owns status exclusively under its field manager (typically vworkspace-operator). Clients should not write to status directly; the status subresource enforces this at the API server level.
| Field | Type | Description |
|---|---|---|
status.observedGeneration |
int64 | The metadata.generation of the ApplicationInstance that produced the current status. Lets clients detect stale status. |
status.conditions[] |
[]Condition | Standard Kubernetes condition objects. The type vocabulary is Ready, Reconciling, Degraded, Suspended, Blocked, Deleting; see conditions.md. |
status.helmReleaseRef |
object | { name, namespace } of the materialized HelmRelease. Populated as soon as the HelmRelease exists. |
status.lastAppliedChart |
object | { sourceType, url, name, version } snapshot of the chart coordinates the operator most recently materialized. Useful for distinguishing "what was asked" from "what is running" during a rolling upgrade. |
status.lastReconcileTime |
string | RFC 3339 timestamp of the last reconcile. |
status.endpoints[] |
[]Endpoint | URLs and hosts derived from the chart's ingress and service outputs. Each entry has name, url, optional type (e.g., ingress, service), and optional notes. Populated when the chart's outputs are visible to the operator's informers. |
status.inventory |
object (optional) | Reference to an inventory object listing the resources the chart produced. Populated when inventory tracking is enabled at the operator level. |
Conditions¶
See conditions.md for the full list of condition types, statuses, and reasons emitted on ApplicationInstance.status.conditions[].
Validation rules¶
The operator ships a validating admission webhook (validate.applicationinstances.apps.vworkspace.io). Key rules:
- Allowed chart sources.
chart.sourceTypemust beociorhelm. Other values are rejected. The set of allowedchart.urlprefixes is configurable per cluster — the admission webhook can be told that only specific registries or repositories are allowed, in which case any URL outside the allow-list is rejected. - Version constraints.
chart.versionmust be a concrete semver string (no ranges, nolatest). Per-catalog-entry version constraints (allowed versions, forbidden versions, minimum versions) can be enforced by the webhook when the cluster has a copy of the catalog policy. - Namespace consistency.
release.namespacemust equalmetadata.namespace. The webhook rejects mismatches to avoid cross-namespace surprises. - Namespace gating. The
ApplicationInstancemust live in a namespace labeledmanaged-by=vworkspace. The operator does not act onApplicationInstanceresources in other namespaces; the webhook makes this explicit by rejecting create/update in ungated namespaces. - Values exclusivity. Exactly one of
values.inline,values.secretRef,values.configMapRefis populated, matchingvalues.source. The webhook rejects multiple or absent sources. - Immutability of release identity.
release.nameandrelease.namespaceare immutable after creation. Renaming a release in place would orphan the underlyingHelmRelease; the right move is to delete and recreate theApplicationInstance.
The webhook is strict by default. Soft warnings (deprecated chart source, deprecated integration shortcut) are surfaced as conditions on the resource rather than as admission rejections, so the operator can ship deprecations without breaking applies.