Deploy with Helm
The operator installs its own CRDs at startup, so a plain Helm install is enough — no separate CRD apply. The chart creates no ClusterMirror or UpgradeAnalysis; you create them after installing.
This static illustration provides a quick visual summary of the same flow:
Install
The chart is published to the GitHub Container Registry as an OCI artifact, and listed on Artifact Hub. Both are the same chart — Artifact Hub reads it from the registry — so pick whichever you browse; the command is the same. There's no helm repo add: Helm pulls OCI charts directly.
From the GitHub OCI registry
The chart lives at ghcr.io/miropshq/charts/mirops:
helm install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace --version 0.2.0
kubectl -n mirops rollout status deploy/mirops-controller-manager
To look before installing:
helm show values oci://ghcr.io/miropshq/charts/mirops --version 0.2.0 # every value and its default
helm pull oci://ghcr.io/miropshq/charts/mirops --version 0.2.0 # download mirops-0.2.0.tgz
From Artifact Hub
The chart is listed on Artifact Hub under the mirops organization, with every value, the CRDs the operator installs, the images (with their security scan), and each version's changes.
Its Install button shows Artifact Hub's generic command:
helm install my-mirops oci://ghcr.io/miropshq/charts/mirops --version 0.2.0
Name the release mirops and add the namespace before you run it — it's the command above. Every name the chart creates starts with the release name, so my-mirops gives you my-mirops-controller-manager instead of mirops-controller-manager, and without --namespace it lands in your current namespace. The rest of these docs — the IRSA and Workload Identity trust, the in-cluster report URL, the commands — expect mirops in mirops.
Upgrade analysis: on or off
The operator always runs the ClusterMirror controller. The UpgradeAnalysis controller runs only with upgrade.enabled=true — it's off by default, and while it's off an UpgradeAnalysis is never processed:
# install with upgrade analysis on
helm install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace --version 0.2.0 \
--set upgrade.enabled=true
# ...or turn it on (or off) later
helm upgrade mirops oci://ghcr.io/miropshq/charts/mirops --namespace mirops \
--reuse-values --set upgrade.enabled=true
Let Helm create the namespace with --create-namespace. That's the only namespace setting you need, and it works for every case (including compatMatrix, whose pre-install hook needs the namespace to exist first). The chart doesn't create the namespace itself by default (namespace.create=false), so nothing collides.
Install with Terraform
The same install with the helm provider. create_namespace is the Terraform equivalent of --create-namespace — it creates the namespace before the pre-install hook, so it's the single namespace knob here too. Any --set becomes a set block.
resource "helm_release" "mirops" {
name = "mirops"
repository = "oci://ghcr.io/miropshq/charts"
chart = "mirops"
version = "0.2.0"
namespace = "mirops"
create_namespace = true # == --create-namespace (the only namespace setting you need)
# Optional add-ons — each is one `--set`:
# Upgrade analysis (off by default)
set {
name = "upgrade.enabled"
value = "true"
}
# Compatibility matrix (pin a date in production)
set {
name = "compatMatrix.enabled"
value = "true"
}
set {
name = "compatMatrix.version"
value = "v2026.09.03"
}
# Azure Blob via Workload Identity — dots in the key are escaped with \\,
# and --set-string maps to type = "string"
set {
name = "podLabels.azure\\.workload\\.identity/use"
value = "true"
type = "string"
}
set {
name = "serviceAccount.annotations.azure\\.workload\\.identity/client-id"
value = "<client-id>"
}
}
The block form above is hashicorp/helm v2. On v3, set is a list attribute instead: set = [{ name = "compatMatrix.enabled", value = "true" }, ...].
Cloud credentials (for remote reports)
Skip this if you write reports to a local file or a pvc.
AWS S3 via IRSA:
helm install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::<acct>:role/mirops-s3-role
Azure Blob via Workload Identity:
helm install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace \
--set-string podLabels."azure\.workload\.identity/use"=true \
--set serviceAccount.annotations."azure\.workload\.identity/client-id"=<client-id>
The tenant-id is optional — Workload Identity uses the cluster's default OIDC tenant. Set it only for cross-tenant identities: --set serviceAccount.annotations."azure\.workload\.identity/tenant-id"=<tenant-id>.
Persistent report storage (PVC)
helm install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace \
--set reportPVC.enabled=true
Then set spec.source.type: pvc on your ClusterMirror and analyses.
Compatibility matrix version (optional)
By default the operator uses its embedded matrix. To update it without rebuilding the operator, pull a published version into the override ConfigMap:
# newest (non-prod)
helm upgrade --install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace \
--set compatMatrix.enabled=true --set compatMatrix.version=latest
# pinned date (reproducible; production)
helm upgrade --install mirops oci://ghcr.io/miropshq/charts/mirops \
--namespace mirops --create-namespace \
--set compatMatrix.enabled=true --set compatMatrix.version=v2026.09.03
The pull runs as a pre-install hook (before the chart's normal resources), so the namespace must already exist — --create-namespace lets Helm create it first. See the compatibility matrix for details.
Verify
kubectl -n mirops get pods
kubectl -n mirops logs deploy/mirops-controller-manager
After installing
Create your ClusterMirror — nothing is mirrored until you do:
kubectl apply -f - <<'YAML'
apiVersion: mirops.mirops.io/v1
kind: ClusterMirror
metadata:
name: default
spec:
refresh:
interval: 5m
YAML
With upgrade analysis on, create an UpgradeAnalysis when a version upgrade is coming.
Upgrade & uninstall
helm upgrade mirops oci://ghcr.io/miropshq/charts/mirops --namespace mirops --version 0.2.0 --reuse-values
helm uninstall mirops --namespace mirops
In 0.1.0 the operator always analysed upgrades; from 0.2.0 that's off unless upgrade.enabled=true. If you already run UpgradeAnalysis resources, add --set upgrade.enabled=true to the upgrade, then create a ClusterMirror. The new operator installs the ClusterMirror CRD itself.
--reuse-values--set key={} or --set key=null doesn't remove a nested key from the values Helm reuses. To drop one, write the current values to a file (helm get values mirops -n mirops -o yaml > values.yaml), edit it, and run helm upgrade -f values.yaml without --reuse-values.
Next
Set up your first mirror and analysis, or configure AI scoring.