Configuration
Every setting below is a Helm value on charts/ptah-operator. Three of them
have no default and the chart refuses to render without them, because each
names something the operator must not guess.
The three values with no default
Section titled “The three values with no default”image.digest is the manager’s registry manifest digest. A tag is not
accepted, and a Docker image ID is not a registry manifest digest.
execution.executorImage is the Ptah build the operator runs, by digest.
execution.runnerImage is the runner beside it, also by digest. The executor
is bound into every plan and approval; the runner is built from the operator’s
own source, so its digest is recorded and its enforcement is bound through the
runner protocol version instead. A runner image from a release that speaks
another protocol refuses every Job before the executor starts, and the resource
reports RunnerProtocolMismatch. Each release
names the Ptah release image it was tested with by digest in the release
manifest, as the executor describes;
the chart still takes it only as an explicit value.
execution.ptahVersion is the identity of the build inside that executor
digest. It is verified from the image’s own provenance and never inferred from
a tag, so the chart has no default to fall back on. For a release’s executor,
the manifest carries it as executor-ptah-version. Which build has actually
been run against this operator is Ptah compatibility.
All three are recorded in every plan, approval, Job and applied status, so a reader of a converged schema can see exactly what produced it.
Every value
Section titled “Every value”| Value | Default | What it does |
|---|---|---|
replicaCount |
2 |
Manager replicas. Scaling this release is how the operator runs highly available; a second release in the same cluster is not supported. More than one replica requires leaderElection. |
image |
The image every runtime component runs from: the manager, the certificate rotator and the CRD manager are three commands in one image. | |
image.repository |
ghcr.io/stokaro/ptah-operator |
Repository the manager image is pulled from. |
image.tag |
"0.2.5" |
Recorded for a reader. The digest below is what is actually pulled. |
image.digest |
"" |
Required registry manifest digest, including for local development. A Docker image ID is not a registry manifest digest. |
image.pullPolicy |
IfNotPresent |
Pull policy for the manager image. AlwaysPullImages in the cluster rewrites this, which is what admission.alwaysPullImagesEnabled declares. |
imagePullSecrets |
[] |
Pull Secrets added to every Pod this chart creates. |
nameOverride |
"" |
Replaces the chart name in generated object names. |
fullnameOverride |
"" |
Replaces the complete generated name, release prefix included. |
execution |
The data plane: the images a database operation runs in, and the Ptah build they carry. The manager dispatches Jobs from these and runs no SQL itself. | |
execution.executorImage |
"" |
Both execution images are required and must include @sha256:<64 hex>. ptahVersion is also required, is limited to 128 bytes, and must identify the Ptah build verified for the exact executorImage digest; the chart deliberately has no default. |
execution.runnerImage |
"" |
The runner image, built from this operator’s source, which supervises the executor inside a task Pod and returns its result frame. |
execution.ptahVersion |
"" |
The Ptah build the executorImage digest carries. A plan records it, and an apply is refused when it no longer matches. |
admission |
What the cluster’s own admission plugins do to the Pods this operator creates. The chart’s guards assert the resulting Pod rather than the intent, so these declare what the API server is configured to do. | |
admission.defaultTolerationsEnabled |
true |
These values must match kube-apiserver’s enabled admission plugins and DefaultTolerationSeconds flags. Zero means an exact zero-second NoExecute toleration; it does not disable the plugin. |
admission.defaultNotReadyTolerationSeconds |
300 |
The DefaultTolerationSeconds value for node.kubernetes.io/not-ready. |
admission.defaultUnreachableTolerationSeconds |
300 |
The DefaultTolerationSeconds value for node.kubernetes.io/unreachable. |
admission.extendedResourceTolerationEnabled |
false |
Whether ExtendedResourceToleration is enabled, which adds a toleration for every extended resource a Pod requests. |
admission.alwaysPullImagesEnabled |
false |
Whether AlwaysPullImages is enabled, which rewrites every imagePullPolicy to Always. The guards accept that rewrite only when this says so. |
approvals |
Approval policy the installer owns rather than any PtahSchema or PtahMigration author. | |
approvals.requireDistinctApprover |
false |
The four-eyes control: refuse an approval whose approver is exactly the identity that last created or last changed the spec of the resource it approves, or refuse it outright when no such identity was recorded. A desired-state author cannot reach this from their own resource’s spec, so turning it on binds every approval this installation admits rather than binding one resource an author could edit back off. It defaults to false, so an existing installation that never sets it keeps admitting the approvals it always did. The mutating webhook that records the identity exists only while this is on. Off, a PtahSchema or PtahMigration write never depends on the manager being reachable for it; a webhook with failurePolicy Fail would otherwise block every write, including across an upgrade under Recreate, to pay for a record nothing reads. A resource written before this was turned on carries no recorded writer, so its approvals are refused until its spec is next changed and the now-installed webhook has a write to stamp. |
applyPolicyGuard |
Who may turn the approval requirement off. spec.policy.apply: Always applies plans with no approval, and the field is on the desired-state resource, so whoever may edit a PtahSchema or a PtahMigration may select it. RBAC on approvals does not decide that, because the bypass is not an approval; this ValidatingAdmissionPolicy does. It refuses a create that sets Always and an update that moves apply to Always unless the requester is in one of the groups below. A write that leaves Always where it already was is admitted, so the operator’s own finalizer patches and an author’s ordinary edits of a resource an administrator set to Always keep working, and so is a move back to OnApproval or Never. | |
applyPolicyGuard.enabled |
true |
Whether to install the guard. On by default: an installation that wants unattended apply for everyone turns it off knowingly, and the security model says what that gives up. Off, the policy and its binding are not rendered, and an upgrade that turns it off removes them. |
applyPolicyGuard.exemptGroups |
["system:masters"] |
The groups whose members may select Always. The default is system:masters alone: it is the one group Kubernetes itself puts beyond authorization, its members could delete this policy anyway, so exempting them opens nothing, and it keeps a way to choose Always that needs no Helm upgrade. No other group is guessed. cluster-admin is a ClusterRole, bound to whichever groups a distribution chose, and a chart that named one of them would exempt a different set of people on every distribution. Add the group that owns apply policy in this cluster, and the group of a GitOps controller that creates resources with Always (for a ServiceAccount in namespace flux, system:serviceaccounts:flux). An empty list exempts nobody. system:authenticated is refused: exempting every authenticated identity is turning the guard off, which is enabled: false. |
leaderElection |
true |
Required whenever replicaCount is greater than one. Disable only for an isolated single-replica installation. Exactly one Helm release is supported per cluster; scale this release for high availability. |
coordination |
Where the operator’s Leases live. | |
coordination.namespace |
"" |
Leave empty to use the Helm release namespace. This namespace contains both database target Leases and the manager leader-election Lease for the cluster’s one supported operator release. |
releaseNamespace |
The release namespace is part of the operator’s trusted computing base: whoever can create a workload there can run it as the operator’s ServiceAccounts. When Helm can read the cluster, the chart refuses to install into default or a kube-* namespace, or into one that runs Pods or workload controllers without this release’s app.kubernetes.io/instance label. helm template, and a GitOps tool that renders with it, cannot read the cluster and skip the check. | |
releaseNamespace.allowSharedNamespace |
false |
Install into such a namespace anyway. Set it only when everyone who can create workloads there is trusted to administer Ptah. |
serviceAccount |
The identity the runtime components run as. | |
serviceAccount.create |
true |
The controller runs as one ServiceAccount in every release. With create=false, pre-create the ServiceAccount named below; the chart binds its roles to it and never deletes it. |
serviceAccount.name |
"" |
The controller ServiceAccount’s name. Empty means the generated release name. |
serviceAccount.annotations |
{} |
Annotations added to the ServiceAccount the chart creates, which is where a cloud identity binding goes. |
podAnnotations |
{} |
Annotations added to the manager Pods. |
podLabels |
{} |
Labels added to the manager and rotator Pods, beside the chart’s own. |
resources |
Manager container resources. The manager reconciles and dispatches; the database work happens in task Pods with resources of their own. | |
resources.requests |
What the manager container is guaranteed. | |
resources.requests.cpu |
50m |
CPU request for the manager container. |
resources.requests.memory |
96Mi |
Memory request for the manager container. |
resources.limits |
The ceiling. CPU is deliberately unlimited: throttling a leader is how a Lease is lost. | |
resources.limits.memory |
256Mi |
Memory limit for the manager container. |
nodeSelector |
{} |
Node labels the manager and rotator Pods are scheduled onto. |
tolerations |
[] |
Taints the manager and rotator Pods tolerate, beside the ones admission.defaultTolerations describes. |
affinity |
{} |
Affinity for the manager and rotator Pods. Replicas already spread across nodes by default; this is for anything narrower. |
priorityClassName |
"" |
Assign this class to the runtime Deployments and to the CRD reconcile hook. |
metrics |
The manager’s metrics endpoint. | |
metrics.bindAddress |
":8080" |
Address the manager serves metrics on. The manager Pod declares port 8080, so change the Service below rather than this. |
metrics.service |
A Service in front of the metrics port, for a scraper that needs one. | |
metrics.service.enabled |
true |
Whether to create the metrics Service. |
metrics.service.port |
8080 |
Port the metrics Service listens on. |
monitoring |
Objects for a Prometheus run by the Prometheus Operator: how it scrapes the manager, and the rules it alerts on. | |
monitoring.serviceMonitor |
A ServiceMonitor, which is how Prometheus reaches each manager Pod behind the metrics Service rather than one replica behind its address. | |
monitoring.serviceMonitor.enabled |
false |
Whether to create a ServiceMonitor. Off by default: the object needs the Prometheus Operator CRDs, and an install into a cluster without them fails on a kind the API server does not serve. |
monitoring.serviceMonitor.labels |
{} |
Labels the Prometheus instance selects ServiceMonitors by. Empty means the object is created and selected by nothing, which is why a deployment that turns this on usually has to set them. |
monitoring.serviceMonitor.interval |
"" |
How often to scrape. Unset leaves the interval to the Prometheus configuration rather than pinning it here. |
monitoring.serviceMonitor.scrapeTimeout |
"" |
How long a scrape may take. Unset leaves it to Prometheus. |
monitoring.prometheusRule |
Alerting rules for Apply runs nobody accounted for. Off by default for the same reason as the ServiceMonitor: the object needs the Prometheus Operator CRDs. | |
monitoring.prometheusRule.enabled |
false |
Whether to create the PrometheusRule. When on, viewUnsyncedFor below has to be set as well. |
monitoring.prometheusRule.labels |
{} |
Labels the Prometheus instance selects rules by. |
monitoring.prometheusRule.viewUnsyncedFor |
"" |
How long the view behind the unresolved counts may report itself unsynchronized before that is an alert. Required when the rules are on, and deliberately without a default: how long a manager takes to synchronize after a restart or a leader change depends on the cluster it runs in. The operations guide says how to measure it there. |
monitoring.prometheusRule.runbookBaseURL |
"https://operator.ptah.run/edge/use/operations/" |
Where each rule’s runbook_url points. The page and its anchor are fixed; this is the guide they are read from. |
monitoring.prometheusRule.overdueAfterSeconds |
null |
The rules below render only where their threshold is set. Each one is a number about a particular cluster – its intervals, its Job start times, its tolerance – so the chart ships none; the operations guide says how to read each one off a running installation. Seconds a resource may be past its own status.nextReconciliationTime. |
monitoring.prometheusRule.operationStalledAfterSeconds |
null |
Seconds one operation may stay in flight. |
monitoring.prometheusRule.lockReleaseOwedFor |
"" |
How long a database-realm Lease release may stay owed. |
monitoring.prometheusRule.certificateExpiresWithinSeconds |
null |
Seconds before the admission serving certificate expires that are too few. |
monitoring.prometheusRule.planStoreBytesAbove |
null |
Bytes of retained schema plans that are too many. The operator prunes no plan, so this is the budget the installation set for them in etcd. |
monitoring.prometheusRule.failures |
Operation failures in a window that are too many. Set both or neither. | |
monitoring.prometheusRule.failures.window |
"" |
The window, as a duration. |
monitoring.prometheusRule.failures.count |
null |
The count in that window. |
monitoring.prometheusRule.admissionFailingFor |
"" |
How long the API server may fail to reach the admission webhooks. The alert reads the API server’s metrics, so it needs the API server scraped. |
webhook |
The admission webhook server inside the manager, which is what refuses an approval that does not name an exact plan. | |
webhook.port |
9443 |
Port the manager serves admission on. |
webhook.timeoutSeconds |
5 |
Timeout the API server applies to the fast admission paths. |
webhook.controllerWriteTimeoutSeconds |
30 |
The manager-write validator may read one schema, one plan, and up to 16 immutable plan projections of 512 KiB each. Kubernetes caps admission timeouts at 30 seconds; keep this fail-closed path at that bound independently of the fast paths. |
webhook.existingSecret |
"" |
Name of a pre-provisioned kubernetes.io/tls Secret. The Secret must also contain ca.crt. Set caBundle to render the chart without cluster lookup. |
webhook.caBundle |
"" |
PEM-encoded CA certificate for existingSecret. Leave empty during a connected Helm install to read ca.crt from that Secret. |
certificateRotation |
The serving certificates the webhook presents, and the component that issues and replaces them without anybody holding a private key. | |
certificateRotation.enabled |
true |
Built-in management is automatically omitted when webhook.existingSecret is set. The manager never receives Secret-read permission. |
certificateRotation.recreateMissingSecret |
false |
Opt in to recreating a deleted chart-generated Secret. This necessarily grants the rotator namespace-wide Secret CREATE in RBAC; a fail-closed admission policy narrows its use to the exact generated TLS Secret. |
certificateRotation.interval |
"6h" |
How long the rotator waits after a reconciliation that changed nothing. |
certificateRotation.caSwitchDelay |
"" |
How long every webhook entry trusts both the old and the new CA before a planned CA renewal moves the serving certificate to the new one. At least 1m; empty means the interval. |
certificateRotation.operationTimeout |
"15m" |
Ceiling on one reconciliation, including the probes that prove every endpoint serves the new certificate. |
certificateRotation.retryInitial |
"5s" |
First backoff delay after a failed reconciliation. |
certificateRotation.retryMax |
"5m" |
Ceiling on the backoff after repeated failures. |
certificateRotation.healthPort |
8081 |
Port the rotator serves its own health and readiness probes on. |
certificateRotation.renewalThreshold |
"720h" |
Rotate a certificate once no more than this much validity remains. At least 48h, the life of the bootstrap CA the chart renders. |
certificateRotation.servingCertificateValidity |
"2160h" |
Validity of a newly issued serving certificate. |
certificateRotation.caCertificateValidity |
"26280h" |
Validity of a newly issued CA certificate. |
certificateRotation.probeTimeout |
"5m" |
Ceiling on waiting for every webhook endpoint to serve a replacement certificate before the rotation is abandoned. |
certificateRotation.probeInterval |
"2s" |
How often those endpoints are probed while waiting. |
certificateRotation.leaseDuration |
"10m" |
Duration of the Lease that makes one rotator the one doing the work. |
certificateRotation.leaseAcquireTimeout |
"30s" |
How long a rotator waits for that Lease before giving up this pass. |
certificateRotation.resources |
Rotator container resources. It sleeps between passes and does a few seconds of work in each. | |
certificateRotation.resources.requests |
What the rotator container is guaranteed. | |
certificateRotation.resources.requests.cpu |
10m |
CPU request for the rotator container. |
certificateRotation.resources.requests.memory |
32Mi |
Memory request for the rotator container. |
certificateRotation.resources.limits |
The ceiling, memory only, for the same reason as the manager. | |
certificateRotation.resources.limits.memory |
64Mi |
Memory limit for the rotator container. |
approverClusterRole |
A ClusterRole for the people who approve changes: read schemas, migrations, their plans, the chunks a schema plan’s SQL is stored in, and approvals, create an approval of either kind, and create the acknowledgment that settles a migration run nobody accounted for. It reads no ConfigMap and no Pod log. Bind it yourself; the chart binds nobody. | |
approverClusterRole.create |
true |
Whether to create that ClusterRole. |
podDisruptionBudget |
A PodDisruptionBudget for the manager, so a drain cannot take every replica at once. | |
podDisruptionBudget.enabled |
true |
Whether to create the PodDisruptionBudget. |
podDisruptionBudget.minAvailable |
1 |
Replicas that must stay available during a voluntary disruption. |
resultDelivery |
Durable runner results are persisted independently of container logs. Requires the built-in certificate rotator and generated webhook trust. | |
resultDelivery.enabled |
true |
Send new runner results to the authenticated receiver instead of container logs. |
resultDelivery.networkPolicy |
Ingress restrictions for receiver, webhook, health, and metrics ports. | |
resultDelivery.networkPolicy.enabled |
false |
Requires a CNI that enforces NetworkPolicy. Supply peers allowed to reach webhook, health, and metrics ports; the chart cannot infer API server source addresses. Empty peers are refused when this policy is enabled. |
resultDelivery.networkPolicy.infrastructurePeers |
[] |
Kubernetes NetworkPolicy peers allowed to reach the manager infrastructure ports. |
The table is generated from charts/ptah-operator/values.yaml, which is where
each of these is declared. Run npm run values:write in docs/site after
changing a value, and the documentation gate refuses a stale table.