costgraph-selfhosted and costgraph-operator) share one
configuration contract. Every component in them is declared the same way, is
enabled the same way, and is sized the same way, whichever Kubernetes object it
ends up rendering. This page describes that contract so you can configure any
component without learning a new shape for each one.
The component pattern
A component is a top-level key invalues.yaml. Under it, the workload lives in
a block named after the Kubernetes kind it produces (deployment:,
statefulSet: or daemonSet:), and the inner keys of that block are the same
in every case:
replicas, image, resources, env, envFrom, ports, volumes,
volumeMounts, securityContext, containerSecurityContext,
imagePullSecrets, automountServiceAccountToken, priorityClassName,
command, args, strategy, nodeSelector, tolerations, affinity,
topologySpreadConstraints, podAnnotations and podLabels mean the same
thing under deployment:, under statefulSet: and under daemonSet:. The kind
changes; the vocabulary does not.
The differences are only the ones Kubernetes itself imposes:
So the bundled Postgres is sized exactly the way the API is:
env, volumes and volumeMounts are maps, not lists:
env.LOG_LEVEL.value: info, or env.DB_URL.valueFrom.secretKeyRef. A map
means --set can override one entry without clobbering its siblings.
Probes take an enabled key alongside the usual probe fields.Images
Every image is a literal on its own component’s workload block:Enabling and disabling
<component>.enabled gates the component. A disabled component renders nothing
at all rather than rendering and being filtered.
<component>.deployment.enabled (or .statefulSet.enabled /
.daemonSet.enabled) is a second, narrower switch: it keeps the component’s
Service and configuration but drops the pods.
The three bundled datastores are the exception. postgres, redis and
victoriametrics are switched on by global.postgres.bundled.enabled,
global.redis.bundled.enabled and global.metricsStore.bundled.enabled rather
than by an enabled key of their own. Everything else about their shape lives
in their own top-level block, like every other component.
The three value tiers
global.* holds facts about the deployment, not about any one component:
connection strings, credentials, base URLs. Anything more than one component
reads has to live there. In
costgraph-selfhosted that is global.postgres, global.metricsStore,
global.redis, global.controlPlane, global.appBaseURL,
global.corsAllowedOrigins, global.pricingApiKey, global.analytics and
global.imagePullSecrets.
Per-component blocks hold what differs: the component’s own image, replica
count, resources, environment, probes and scheduling. There are no global
scheduling or pod-annotation defaults: nodeSelector, tolerations,
affinity and podAnnotations are set on the component you want to move.
global.podSecurityContext and global.securityContext are narrower than
they look: they apply to the pre-install and pre-upgrade Jobs, which are not
components and have no block of their own. Components carry their own
securityContext and containerSecurityContext.
Using your own registry credentials
The supported way to use your own credentials, which is required when you setglobal.controlPlane.existingSecret:
Deploying or sizing a single component
Because every component reads the same keys, one component is changed without touching the rest. Run only part of the stack:Install path: use helm upgrade, not Argo CD
This is verifiable: two consecutivehelm template runs of the same chart, with
the same values, produce different values for api-key-secret. Two consecutive
helm upgrade runs do not.
The supported install path is helm install / helm upgrade.
If you must deploy through Argo CD, pick one of:
-
Pre-create the Secrets out of band. Create
costgraph-selfhosted-generated(keysapi-key-secretandintegrations-encryption-key) and, if you use the bundled database,costgraph-selfhosted-postgres(keypassword) before the first sync, and let the chart adopt them. Better still, do not use the bundled database: pointglobal.postgres.existingSecretat a Secret you manage, and the generated password stops being part of the problem. -
Tell Argo to ignore the drift.
ignoreDifferenceson its own only changes what Argo compares, not what it applies, so a sync still overwrites the Secret. Pair it withRespectIgnoreDifferences=true:This protects an existing Secret, never one Argo is creating for the first time.costgraph-selfhosted-generatedmust already exist before the first sync, so this option is a complement to pre-creating it, not an alternative.
helm.sh/resource-policy: keep, so it survives a
helm uninstall. That protects it from deletion, not from a re-render that overwrites it.
Credentials and external services
Every credential the charts accept has anexistingSecret form. Prefer it: a
value passed inline is stored in values and in Helm release history.
string
Deployment API key issued during onboarding. Also authenticates image pulls
from
registry.costgraph.ai.string
Name of a Secret you manage holding
control-plane-api-key and
pricing-api-key. Copy the control-plane key into pricing-api-key unless
you were issued a separate pricing key; the backend does not start without
it. Takes precedence over apiKey. Requires global.imagePullSecrets, since the chart cannot read
a Secret to build the registry pull secret from.string
CostGraph control plane endpoint. Defaults to
https://api.costgraph.ai.string
CostGraph’s published public key, shipped with the chart. Not a secret. Do not
remove it; the render fails without it.
string
Connection string for an external Postgres.
string
Name of a Secret you manage holding
postgres-url and postgres-password.
Takes precedence over url and password.boolean
Apply pending database migrations on start. Leave on unless you apply them as
a separate step.
boolean
Run the Postgres described by the
postgres component in-cluster instead of
using an external database. Sized with postgres.storage,
postgres.storageClass and postgres.statefulSet.resources. Not recommended
for production, and see the Argo CD caveat above: its password is a generated
value.string
URL of an external VictoriaMetrics or Prometheus-compatible store.
boolean
Run the VictoriaMetrics described by the
victoriametrics component.
Retention is victoriametrics.retentionPeriod (months); storage is
victoriametrics.storage and victoriametrics.storageClass.string
Redis connection URL. Use
global.redis.existingSecret (key redis-url)
instead when the URL contains a password, so it is not stored in a ConfigMap
in plain text.boolean
Run the Redis described by the
redis component. This is the condition on
the redis component, so leaving it off renders nothing.string
required
The URL your users open CostGraph on. Must be the address people actually
reach in a browser, not an internal Service DNS name, and must start with
http:// or https://. Also the default allowed browser origin.string
Browser origins allowed to call the API, comma-separated. Defaults to
appBaseURL.string
Key used to download the pricing catalog. Leave empty to reuse
global.controlPlane.apiKey, which is what most installs do.boolean
Anonymous product analytics. Set to
false to send nothing.array
Pull secrets for the components this chart renders itself. Does not reach
every component; see the render-time check above.
Integration sign-in
Connect with for DigitalOcean, Akamai Cloud (Linode), Convex, and PlanetScale is configured as environment entries underbackend.deployment.env, not as chart values.
Register your own OAuth app with each vendor and set its redirect URL to
{APP_BASE_URL}/integrations/oauth/callback. See
Connect with OAuth for an example.
A provider with no client ID and secret shows the token form instead.
Slack
Connects Graph AI to a Slack app you create. Slack is on whenclientId or existingSecret is set, and needs costgraphAi.enabled. See Slack for the setup.
string
default:"socket"
socket opens an outbound connection to Slack. webhook has Slack call your CostGraph URL and adds three Slack paths to the chart’s ingress.string
Name of a Secret you manage, with keys
SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_SIGNING_SECRET, and SLACK_APP_TOKEN (socket mode only). Takes precedence over the four values that follow, so no credential is stored in your values file.string
Client ID from your Slack app’s Basic Information > App Credentials.
string
Client secret from the same page. Required with
clientId.string
Signing secret from the same page. Required in both modes.
string
App-level token with the
connections:write scope, starting with xapp-. Required in socket mode.Ingress
The Ingress fans one host out to several Services by port number.boolean
Render the Ingress. The render fails if this is on and
ingress.hosts is
empty.string
Ingress class.
object
Annotations on the Ingress object.
array
Hosts, each with its own
paths. The API and remote-write paths are added
automatically for whichever of backend, dashboard and ingestionApi are
enabled.array
Standard Kubernetes Ingress TLS blocks.
Client addresses behind a proxy
The backend reads the client’s address from theX-Forwarded-For header only
when the request comes from a trusted proxy. Sign-in rate limits and request
logs use that address. By default, every private network range is trusted. If
your ingress controller or load balancer forwards from a narrower range, set it
with the TRUSTED_PROXIES environment entry, as comma-separated CIDRs:
Preflight
boolean
A preflight Job that runs before install and upgrade and fails the release
with the real reason if the database cannot support the schema. Turn it off
only if the database is provisioned after the release.
Component blocks
object
The API, and the component that creates the ServiceAccount the rest of the
release runs as (
backend.rbac.serviceAccount). Configure it through
backend.deployment.replicas, backend.deployment.resources,
backend.deployment.image, backend.deployment.env and
backend.service.ports.object
The web UI.
dashboard.enabled, dashboard.deployment.image, and
dashboard.apiBaseURL when the API is served from a different host than
global.appBaseURL. dashboard.cognitoOAuthDomain and
dashboard.graphAiBaseURL are empty unless CostGraph has set those up for
your deployment.object
Receives metrics from the CostGraph operator running in your clusters.
ingestionApi.enabled, ingestionApi.logLevel, and
ingestionApi.maxBodyBytes. Raise the last if clusters with many nodes
report remote-write bodies rejected as too large.object
Turns raw samples into per-workload costs. Without it, metrics arrive but cost
breakdowns stay empty.
aggregator.features toggles the cost dimensions
computed: ENABLE_GPU, ENABLE_HPA, ENABLE_NETWORK_POLICY,
ENABLE_EPHEMERAL_STORAGE, ENABLE_DISK_IOPS, and ENABLE_NETWORK_TRACE.
Turning one off drops it from the dashboard. ENABLE_NETWORK_TRACE is on by default. Set
it to "false" to stop turning
network flow tracing into traffic, so
Context shows no traffic lines.object
Evaluates the recording rules the cost maths reads. It renders two
Deployments from
vmalert.deployment, one per rule set: long-window rules
feed the daily rollups, short-window rules the recent views. The rules
themselves come from the aggregator image, so
aggregator.deployment.image.tag is required whenever vmalert is enabled.It has no service: or rbac: block. A Service is rendered for each of the
two Deployments, so /api/v1/rules is reachable even when a rule produces no
samples, and vmalert runs with no ServiceAccount of its own.object
The bundled Redis, gated by
global.redis.bundled.enabled. It has no
enabled key of its own. Configure an external Redis under global.redis
instead.object
The bundled Postgres StatefulSet, gated by
global.postgres.bundled.enabled.
postgres.database, postgres.username, postgres.password (generated per
install when empty), postgres.storage, postgres.storageClass, and the
workload under postgres.statefulSet.object
The bundled metrics store, gated by
global.metricsStore.bundled.enabled.
victoriametrics.retentionPeriod (months), victoriametrics.storage,
victoriametrics.storageClass, and the workload under
victoriametrics.statefulSet.string
Label for this deployment in the CostGraph dashboard, so several of them can
be told apart. Defaults to the Helm release name.
string
Overrides the chart name used in the generated labels.
Migrating an existing values file
Old paths still parse. Helm does not warn about a key nothing reads, so a values file written against an earlier chart installs cleanly and quietly falls back to the chart defaults. Check every override you rely on against these tables.costgraph-selfhosted
global.postgres.bundled.enabled, global.redis.bundled.enabled and
global.metricsStore.bundled.enabled are unchanged: they are still the switches
for the bundled datastores.
costgraph-operator
All four CostGraph components (operatorKubernetes, operatorPrometheus,
aiGatewayScraper and flowtrace) now take the same shape: enabled,
applicationName, a workload block, and a component-specific config.
flowtrace.maxUnavailable and operatorPrometheus.scrapeTargets stay at
component level. enabled stays at component level on all four;
<component>.deployment.enabled / .daemonSet.enabled is the narrower switch
that drops the pods.
Object names lose the release prefix
These are live objects being renamed, so plan the upgrade. Incostgraph-operator:
fullnameOverride no longer reaches these. It now affects only the ClusterRole
(<fullname>-view), the ClusterRoleBinding (<fullname>) and the
ServiceAccount. Each component’s objects are named from its applicationName.
In costgraph-selfhosted, objects were <release>-costgraph-selfhosted... and
are now costgraph-selfhosted....
Install
Prerequisites, a starting values file, and what to point at your own address.
Operator configuration
The same contract, applied to the chart that runs in each measured cluster.