Configuration
Installation shape comes from Helm values. Anything a person changes while running it lives in the console, so a policy edit is not a redeploy.
An upgrade may be run with helm upgrade --reuse-values, which keeps the values you installed with and does not merge in the new chart's defaults. Every setting added since your installed version then takes the default documented here, so the flag is supported across versions — see Upgrade.
Identity and scale
| Value | Default | What it does |
|---|---|---|
clusterName | "" | Names this installation in notifications and evidence. Never a hostname — nothing topological leaves the cluster. |
replicaCount | 2 | Webhook replicas, spread by anti-affinity. One serves, two survive a node. |
scanner.replicas | 1 | Scan workers. Raising this with a ReadWriteOnce cache is refused at install. |
image.repository | ghcr.io/attestkeep/attestkeep-k8s | Point at your mirror for an air-gapped install. |
image.tag | chart appVersion |
Database
| Value | Default | What it does |
|---|---|---|
postgresql.enabled | true | Install a PostgreSQL alongside. Turn off to use your own. |
postgresql.persistence.size | 20Gi | |
postgresql.auth.password | "" | Generated when empty. |
externalDsnExistingSecret.name | "" | Preferred for an external database. |
externalDsn | "" | A DSN inline. Avoid: it lands in your Helm release history and stays there. |
Scanning
| Value | Default | What it does |
|---|---|---|
scan.workers | 2 | Concurrent scans per scanner pod. |
scan.timeout | 10m | Per scan. |
scan.rescanInterval | 24h | How often a known digest is re-evaluated against a refreshed database. |
scan.insecureRegistries | [] | Registries served over plain HTTP. Must be named — silently downgrading a connection is not a decision a security tool makes for you. |
scan.cosignOffline | false | Verify against the key and skip the transparency log. See air-gapped for what that costs. |
kev.source | CISA's catalogue URL | Where the Known Exploited Vulnerabilities catalogue is fetched from, on the same six-hour cadence as the vulnerability database. A path or a file:// URL reads a mirror inside the perimeter instead; kev.existingConfigMap below mounts one. An empty string refreshes nothing: the stored catalogue stays exactly as it is and findings keep being marked against it. What admission does about a marked finding is the policy's choice: spec.knownExploited. |
kev.existingConfigMap | "" | The name of a ConfigMap in the release namespace holding a mirrored copy of the catalogue, mounted read-only at /etc/kev on the scanner. The copy has to be gzipped: a ConfigMap holds at most 1 MiB and the published catalogue is 1.7 MB, which the API server refuses outright; gzipped it is under 200 KB. Point kev.source at file:///etc/kev/<key>, the key being the name of the file the ConfigMap was created from — gzip -k known_exploited_vulnerabilities.json and kubectl -n attestkeep create configmap kev-catalog --from-file=known_exploited_vulnerabilities.json.gz is read as file:///etc/kev/known_exploited_vulnerabilities.json.gz. The scanner decides whether a file is compressed by its first two bytes, not its name. Empty mounts nothing. |
reconcile.interval | 1h | How often what is actually running — each container's observed imageID — is reconciled against the admission ledger, catching workloads that entered before the install, during an outage, or under failurePolicy: Ignore. 0 disables the sweep. What a finding does is the policy's choice: spec.runtimeReconciliation. |
rbac.allowEnforce | false | Whether the operator may evict pods at all. Enforcement is armed with two keys: the policy chooses enforce, this grants the pods/eviction permission it needs. An audit-only install carries no eviction right. |
Admission
See Admission — webhook.failurePolicy and webhook.coldImagePolicy are posture decisions rather than tuning, and they are explained there rather than listed here.
Runtime events (Falco)
Attestkeep does not ship, install or run Falco, and never writes to it. Your own Falco sends its alerts to the operator over mutual TLS, from its own http_output or through Falcosidekick (integrations.falco.connection): the operator issues a client certificate and accepts events only from it. An event never changes an admission decision; a runtime policy can send it on, open an issue and stop the pod it came from. The Falco side, the certificates and what the events then do are on Runtime events.
| Value | Default | What it does |
|---|---|---|
integrations.falco.enabled | false | Turns the integration on: the integrations listener, the Falco client CA and client certificate (Secret <release>-falco-mtls), and the console's Falco section. Off, there is no listener and no CA. |
integrations.port | 8444 | The TLS port Falco posts to, on the pod and the Service. It requires a client certificate from the Falco CA and serves only POST /api/v1/integrations/falco. Only used when the integration is on. |
runtime.retentionDays | 90 | Every event is stored; stored events older than this are removed hourly. Only events — issue history, audit entries and rule revisions stay. 0 keeps them. |
What an event leads to is not a chart value: it is decided by the runtime rules written in the console's Falco section. integrations.falco.existingSecret and integrations.falco.minPriority from earlier releases are no longer read.
Licence
| Value | Default | What it does |
|---|---|---|
license.existingCertificateSecret | "" | Name of a Secret holding an offline certificate under the key certificate, for installs that never open the console. The operator reads it at start and activates from it if it holds no certificate, or if this one runs later than the one it holds, so a Secret left behind after a console renewal is ignored. The certificate comes from the licence page at attestkeep.com, issued against an activation request this cluster produced, so it is valid for this cluster and no other. Empty everywhere else: activation is a console screen, not a chart value. See offline activation. |
nodes.pollInterval | 5m | How often the operator counts the cluster's Ready nodes against the licence's node ceiling. The count stays in the cluster. |
nodes.graceDays | 14 | Days the cluster may stay over its node ceiling before image names never seen this period are denied at admission; everything already counted keeps running. Neither value loosens the ceiling, which is in the certificate. See the node ceiling. |
The data key
The key that encrypts the credentials this installation stores for you — today the issue-tracker tokens that can open and close issues in your own GitHub, GitLab or Jira. Those tokens live in the product database, so without this key a backup or a dump handed to support would carry a working write credential for your forge.
| Value | Default | What it does |
|---|---|---|
api.dataKey.existingSecret.name | "" | Empty means the chart generates <release>-data-key on first install and preserves it across upgrades. Point this at your own Secret to supply the key from a secret manager instead. |
api.dataKey.existingSecret.key | data-key | The key inside that Secret. |
kubectl -n attestkeep create secret generic attestkeep-data-key \
--from-literal=data-key="$(openssl rand -hex 32)"
It is never written into the product database — a key stored beside the data it protects is not a key — and there is no database fallback: with no key, saving an issue-tracker channel is refused rather than storing the token in the clear, and nothing else about the product changes. Re-keying is not offered in this version; if you replace the key, the credentials already stored cannot be opened and each tracker channel needs its token entered again. Back the Secret up with everything else on Backup and restore.
Observability
Metrics are exposed in Prometheus format on the operator's metrics port, and labelled series are created at zero on start-up so a dashboard is not empty until the first event. OTLP export is available and is always additional: if the collector is unreachable, nothing about scanning or admission changes.
OpenTelemetry export switches on the moment a collector is named, and not before. Traces cover the admission path — the span is what answers "why did that pod take four seconds" — metrics mirror the Prometheus set, and logs travel with their attestkeep.log/v1 schema version.
| Value | Default | What it does |
|---|---|---|
observability.otlp.endpoint | empty | The collector, e.g. http://otel-collector.observability.svc:4317. Empty means no OTLP export at all. |
observability.otlp.protocol | grpc | grpc or http/protobuf. |
observability.otlp.insecure | true | Plain connection, for a collector on the cluster's own network. Set false the moment the endpoint leaves it. |
observability.otlp.headers | empty | key=value pairs, comma-separated — how a hosted collector takes its API key. |
observability.traces.enabled | true | Traces, once an endpoint exists. |
observability.traces.sampleRatio | 0.1 | A tenth of admission reviews. Admission runs on every pod in the cluster; a full trace of each is a bill rather than a signal. |
observability.metrics.enabled | true | Metrics over OTLP, once an endpoint exists. The Prometheus port stays either way. |
observability.metrics.intervalMillis | 30000 | Export interval, in milliseconds because the OpenTelemetry specification defines it that way. |
observability.logs.enabled | true | Logs over OTLP, once an endpoint exists. Stdout keeps its copy regardless. |
The same knobs answer to their standard environment names (OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_TRACES_SAMPLER_ARG, OTEL_METRIC_EXPORT_INTERVAL, …) for an installation that configures collectors outside Helm.
The log level is a runtime control in the console, not a deployment setting. Turning up logging to diagnose something should not require a rollout.
What lives in the console instead
- Policies, and the exceptions to them, with an owner and an expiry.
- Notification destinations.
- Users and single sign-on.
- Log level.
- Evidence packages.
The dividing line: a Helm value describes how the installation is shaped, and everything a person changes while operating it belongs where they can change it without a deploy.