# Install the Kubernetes Operator

> **For AI agents:** the complete documentation index is at [llms.txt](https://questdb.com/docs/llms.txt). Every page is available as markdown by appending `.md` to its URL, or by sending an `Accept: text/markdown` request header.

Install the QuestDB Enterprise Kubernetes Operator from its supported OCI Helm chart.

# Installation

The QuestDB Enterprise Operator is available to named design partners on these
tested combinations:

| Platform   | Kubernetes | QuestDB Enterprise |
| ---------- | ---------- | ------------------ |
| Amazon EKS | 1.31–1.36  | 4.0.1              |
| Azure AKS  | 1.33–1.36  | 4.0.1              |

Other Kubernetes distributions and versions are untested. Only the latest
release receives fixes; obtain its `<operator-version>` and private registry
credentials or AWS account grant from your QuestDB design-partner contact.

## Before you install

The installer needs cluster-admin-equivalent access. The chart creates CRDs,
ClusterRoles, ClusterRoleBindings, a validating webhook, and resources in
`questdb-operator-system`. The controller and its RBAC are cluster-scoped so one
installation can reconcile tenants in multiple namespaces. `QuestDBCluster`,
`QuestDBObjectStore`, their referenced Secrets, Pods, PVCs, and Services remain
namespaced.

Check these prerequisites:

- `kubectl` configured for the target cluster, Helm 3.10 or later, and the cloud
  CLI used by your onboarding guide.
- A CSI driver and a `ReadWriteOnce` StorageClass that applies pod `fsGroup` and
  has `allowVolumeExpansion: true`. Do not use `ReadWriteOncePod`.
- Enough schedulable CPU, memory, and zonal disk capacity for every database
  instance. QuestDB memory request and limit must be equal.
- Cluster DNS and network paths for the Kubernetes API server to reach the
  operator webhook on TCP 9443; the operator to reach tenant pods on TCP 8812
  and 9003; and nodes/pods to reach the private image registry, object store,
  and cloud identity endpoints over HTTPS.
- Pull access for two private images. The **operator** pull credential belongs
  in `questdb-operator-system`; each **database** pull credential belongs in its
  tenant namespace and is named by `spec.imagePullSecrets`. Do not reuse a
  short-lived ECR login token as a long-lived Secret.

Follow the complete cloud checklist before installing:

- [Amazon EKS onboarding](/docs/enterprise-kubernetes-operator/getting-started/aws/)
- [Azure AKS onboarding](/docs/enterprise-kubernetes-operator/getting-started/azure/)

## Operator permissions

The chart binds the operator's ServiceAccount to the
`questdb-operator-manager-role` ClusterRole with a ClusterRoleBinding, so its
permissions apply in every namespace. There is no namespace-scoped install mode:
one operator watches `QuestDBCluster` objects in all namespaces.

The full list of rules is in the chart's `templates/rbac/role.yaml`.

The chart also creates a namespaced Role in the operator's namespace for leader
election (Leases, ConfigMaps, Events), and, when metrics are enabled, a
ClusterRole to create TokenReviews and SubjectAccessReviews so it can check who
is reading `/metrics`.

### Secrets

The operator reads Secrets you point it at: the admin password
(`spec.auth.adminSecret`), object-store credentials, and PGWire TLS
certificates. It creates and updates Secrets of its own: a generated admin
password if you don't supply one, its own database login, the object-store
connection strings handed to the pods, and the webhook certificate in
self-signed mode.

It watches Secrets in every namespace so that a renewed TLS certificate takes
effect, but it keeps only their metadata (names and labels) in memory, never the
contents. It reads contents directly from the Kubernetes API when it needs them.

The role has no `delete` on Secrets, on purpose. The operator never deletes a
Secret. The ones it creates for a cluster are owned by that `QuestDBCluster`,
and Kubernetes removes them when you delete the cluster. The webhook certificate
Secret in the operator's namespace is never removed by the operator.

### Object store

The operator never touches your object store and has no cloud permissions. It
does not create or delete buckets, containers, or IAM roles, and never reads or
writes your data there. The QuestDB pods do all object-store I/O, with the
identity or credentials you configure.

## Canonical Helm install

The OCI chart on GHCR is public; its operator image is private. Replace the
placeholders first. Your QuestDB contact supplies the current operator version
and, off AWS, static credentials for `registry.distribution.questdb.io`.

```bash
export OPERATOR_VERSION='<operator-version>'
export REGISTRY_USER='<registry-user>'

kubectl create namespace questdb-operator-system
read -r -s -p 'Registry password: ' REGISTRY_PASSWORD; echo
kubectl create secret docker-registry questdb-operator-registry \
  --namespace questdb-operator-system \
  --docker-server=registry.distribution.questdb.io \
  --docker-username="$REGISTRY_USER" \
  --docker-password="$REGISTRY_PASSWORD"
unset REGISTRY_PASSWORD

helm install questdb-operator oci://ghcr.io/questdb/charts/questdb-operator \
  --namespace questdb-operator-system \
  --version "$OPERATOR_VERSION" \
  --set controllerManager.container.image.repository=registry.distribution.questdb.io/questdb-enterprise-operator \
  --set-json 'controllerManager.imagePullSecrets=[{"name":"questdb-operator-registry"}]'
```

On EKS with QuestDB's cross-account ECR repository grant, kubelets pull through
the worker-node IAM role. Omit the Secret and both `--set` flags; do not use
IRSA for image pulls.

Verify the deployment and APIs:

```sh
kubectl rollout status deployment/questdb-operator-controller-manager \
  --namespace questdb-operator-system --timeout=5m
kubectl get crd questdbclusters.questdb.io \
  questdbobjectstores.questdb.io questdbpromotions.questdb.io
```

Expected result: the Deployment reports `successfully rolled out`, and all three
CRDs are listed.

## Common safe values

| Value                        | Default         | Guidance                                                                                                                                                                                                |
| ---------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `controllerManager.replicas` | `1`             | Keep at `1` unless QuestDB advises otherwise.                                                                                                                                                           |
| `crd.enable` / `crd.keep`    | `true` / `true` | Install CRDs and retain them on Helm uninstall.                                                                                                                                                         |
| `webhook.enable`             | `true`          | Keep admission validation enabled. Reconcile-time validation remains a backstop.                                                                                                                        |
| `webhook.certMode`           | `self-signed`   | The operator creates and rotates its serving certificate; cert-manager is not required.                                                                                                                 |
| `webhook.failurePolicy`      | `Ignore`        | Fail-open avoids blocking cluster writes during a webhook outage. Use `Fail` only after accepting that availability trade-off.                                                                          |
| `metrics.enable`             | `true`          | Exposes authenticated HTTPS metrics on 8443.                                                                                                                                                            |
| `prometheus.enable`          | `false`         | Requires Prometheus Operator `ServiceMonitor` CRDs. Bind the scraper identity to `questdb-operator-metrics-reader`.                                                                                     |
| `certmanager.enable`         | `false`         | When combined with `prometheus.enable=true`, the chart issues and mounts a metrics serving certificate and renders a ServiceMonitor with certificate verification. Install cert-manager first.          |
| `networkPolicy.enable`       | `false`         | Selects the manager only. It restricts metrics ingress to namespaces labeled `metrics=enabled` and leaves webhook 9443 open for managed API servers. It does not create database/tenant ingress policy. |

## Operator scheduling and availability

The chart can place and annotate the manager independently of database
`spec.scheduling`. Use `controllerManager.nodeSelector`, `tolerations`,
`affinity`, `topologySpreadConstraints`, and `priorityClassName` for the manager
Pod. Use `controllerManager.pod.annotations`, `controllerManager.pod.labels`,
`controllerManager.serviceAccount.annotations`, and
`controllerManager.container.env` for integration metadata and environment.

The default manager has one replica and leader election enabled, so only one
reconciler is active. The manager PDB is disabled by default because
`replicas=1`; enable it only after running multiple replicas across failure
domains, for example:

```yaml
controllerManager:
  replicas: 2
  podDisruptionBudget:
    enabled: true
    minAvailable: 1
```

The manager PDB accepts integer and percentage `minAvailable` values. The
database CRD PDB uses an integer only.

Treat upgrades, rollback, and removal as separate lifecycle procedures; use the
[operator operations runbook](/docs/enterprise-kubernetes-operator/operations/operator/)
rather than inferring them from installation commands.
