01-Settings.md

July 23, 2026 · View on GitHub

The Settings CRD is one of the most important CRDs in Operator v2. It enables all the necessary adjustments so that the Operator can adapt to your usage and environment.

Settings are encoded as string, but under the hood, each setting can be unmarshalled to a specific type.

While we have some basic types (string, number, bool ...), we also have some complex structures:

  • Maps: maps are just one level dictionary with values as string. Repeat <key>=<value> pattern for each entry, while separating with comma.
  • URIs: URIs are used each time we need to address an external resource (postgres, kafka ...). URIs are convenient to encode a lot of information in a simple, normalized format.

Available settings

KeyTypeExampleDescription
aws.service-accountstringAWS Role
postgres.<module-name>.uriURIPostgres database configuration
elasticsearch.dsnURIElasticsearch connection URI
temporal.dsnURITemporal URI
temporal.tls.crtstringTemporal certificate
temporal.tls.keystringTemporal certificate key
broker.dsnURIBroker URI
opentelemetry.traces.dsnURIOpenTelemetry collector URI
opentelemetry.traces.resource-attributesMapkey1=value1,key2=value2Opentelemetry additional resource attributes
clear-databasebooltrueWhether to remove databases on stack deletion
ledger.logs.max-batch-sizeInt1024Ledger logs batching max size
ledger.api.bulk-max-sizeInt100Max bulk size
ledger.api.default-page-sizeIntDefault api page size
ledger.api.max-page-sizeIntMax page size
ledger.experimental-featuresBooltrueEnable experimental features
ledger.experimental-numscriptBooltrueEnable new numscript interpreter
ledger.experimental-numscript-flagsArrayexperimental-overdraft-function experimental-get-asset-function experimental-get-amount-function experimental-oneof experimental-account-interpolation experimental-mid-script-function-call experimental-asset-colorsEnable numscript interpreter flags
ledger.experimental-exportersBooltrueEnable new exporters feature
ledger.schema-enforcement-modeStringstrictSchema enforcement mode for the Ledger (v2.4+)
ledger.disable-ledger-scope-optimizationBooltrueAlways emit the ledger = ? predicate on read queries, disabling the alone-in-bucket optimization that skips it when a ledger is the only one in its bucket
ledger.worker.async-block-hasherMapmax-block-size=1000, schedule="0 * * * * *"Configure async block hasher for the Ledger worker (v2.3+). Fields: max-block-size, schedule
ledger.worker.bucket-cleanupMapretention-period=720h, schedule="0 0 * * *"Configure bucket cleanup for the Ledger worker (v2.4+). Fields: retention-period, schedule
ledger.worker.pipelinesMappull-interval=5s, push-retry-period=10s, sync-period=1m, logs-page-size=100Configure pipelines for the Ledger worker (v2.3+). Fields: pull-interval, push-retry-period, sync-period, logs-page-size
payments.encryption-keystringPayments data encryption key
payments.clear-temporalbooltrueWhether to clean up Temporal workflows when the Payments module is deleted (default: true). Set to false to skip the cleanup, e.g. when migrating a stack across clusters.
payments.worker.temporal-max-concurrent-workflow-task-pollersIntPayments worker max concurrent workflow task pollers configuration
payments.worker.temporal-max-concurrent-activity-task-pollersIntPayments worker max concurrent activity task pollers configuration
payments.worker.temporal-max-slots-per-pollerIntPayments worker max slots per poller
payments.worker.temporal-max-local-activity-slotsIntPayments worker max local activity slots
deployments.<deployment-name>.containers.<container-name>.resource-requirementsMapcpu=X, mem=X
deployments.<deployment-name>.containers.<container-name>.run-asMapuser=X, group=X
deployments.<deployment-name>.init-containers.<container-name>.resource-requirementsMapcpu=X, mem=X
deployments.<deployment-name>.init-containers.<container-name>.run-asMapuser=X, group=X
deployments.<deployment-name>.replicasstring2
deployments.<deployment-name>.semconv-metrics-namesBooltrueEnable semantic convention metrics names by setting SEMCONV_METRICS_NAME environment variable to true in all containers
deployments.<deployment-name>.spec.template.annotationsMapfirstannotations=X, anotherannotation=X
deployments.<deployment-name>.spec.template.spec.termination-grace-period-secondsInt30Specify the termination grace period for the deployment
deployments.<deployment-name>.topology-spread-constraintsBooltrueEnable topology spread constraints in deployments to maximize high availability of deployments
caddy.imagestringCaddy image
jobs.<owner-kind>.spec.template.annotationsMapfirstannotations=X, anotherannotations=YConfigure the annotations on specific jobs'modules
jobs.<owner-kind>.init-containers.<container-name>.run-asMapuser=X, group=XConfigure the security context for init containers in jobs by specifying the user and group IDs to run as
jobs.<owner-kind>.containers.<container-name>.run-asMapuser=X, group=XConfigure the security context for containers in jobs by specifying the user and group IDs to run as
registries.<name>.endpointstringexample.com?pullSecret=fooSpecify a custom endpoint for a specific docker repository
registries.<name>.images.<path>.rewritestringformancehq/exampleAllow to rewrite the image path
services.<service-name>.annotationsMapAllow to specify custom annotations to apply on created k8s services
services.<service-name>.traffic-distributionstringPreferSameZone, PreferSameNode, PreferCloseConfigure traffic distribution for Kubernetes services (requires Kubernetes 1.34+). See Kubernetes documentation
gateway.ingress.annotationsMapAllow to specify custom annotations to apply on the gateway ingress
gateway.ingress.hostsstring{stack}.example.com,{stack}.example.orgComma-separated list of additional hosts for the gateway ingress. Combined with hosts defined on the Gateway CRD. Supports {stack} placeholder
gateway.ingress.labelsMapAllow to specify custom labels to apply on the gateways ingress
logging.jsonboolConfigure services to log as json
modules.<module-name>.database.connection-poolMapmax-idle=10, max-idle-time=10s, max-open=10, max-lifetime=5mConfigure database connection pool for each module. See Golang documentation
orchestration.max-parallel-activitiesInt10Configure max parallel temporal activities on orchestration workers
transactionplane.worker-enabledboolfalseEnable the embedded worker inside the transactionplane server to run a single service instead of separate API and worker processes
modules.<module-name>.grace-periodstring5sDefer application shutdown
namespace.labelsMapsomelabel=somevalue,anotherlabel=anothervalueAdd static labels to namespace
namespace.annotationsMapsomeannotation=somevalue,anotherannotation=anothervalueAdd static annotations to namespace
gateway.ingress.tls.enabledbooltrueEnable TLS if not enabled at Gateway CRD level
gateway.caddyfile.trusted-proxiesstring10.0.0.0/8,192.168.0.0/16Comma-separated list of IP ranges (CIDRs) of trusted proxy servers. Caddy will parse the real client IP from HTTP headers when requests come from these proxies. Use private_ranges to match all private IPv4 and IPv6 ranges.
gateway.caddyfile.trusted-proxies-strictboolfalseEnable strict (right-to-left) parsing of the X-Forwarded-For header. Recommended when using upstream proxies like HAProxy, Cloudflare, AWS ALB, or CloudFront.
gateway.caddyfile.shutdown-delaystringDelay before Caddy starts the graceful shutdown sequence, allowing load balancers to remove the pod from rotation before in-flight requests are drained. Use Go duration format (e.g., 30s, 5m).
gateway.caddyfile.grace-periodstringMaximum time to wait for in-flight requests to complete before forcefully closing connections during shutdown. Use Go duration format (e.g., 30s, 5m).
gateway.config.idle-timeoutstring10mConfigure the idle timeout for client connections (default: 5m). Use Go duration format (e.g., 30s, 5m, 1h).
gateway.dns.private.enabledboolfalseEnable generation of private DNS endpoints for the gateway
gateway.dns.private.dns-namesstringDNS name pattern(s) for private DNS endpoints. Comma-separated list. Supports {stack} placeholder
gateway.dns.private.targetsstringTarget(s) for private DNS records. Comma-separated list
gateway.dns.private.record-typestringCNAMEDNS record type (e.g., CNAME, A, AAAA)
gateway.dns.private.provider-specificMapalias=true,aws/target-hosted-zone=same-zoneProvider-specific DNS settings for private endpoints
gateway.dns.private.annotationsMapservice.beta.kubernetes.io/aws-load-balancer-internal=trueAnnotations to add to the private DNSEndpoint resource
gateway.dns.public.enabledboolfalseEnable generation of public DNS endpoints for the gateway
gateway.dns.public.dns-namesstringDNS name pattern(s) for public DNS endpoints. Comma-separated list. Supports {stack} placeholder
gateway.dns.public.targetsstringTarget(s) for public DNS records. Comma-separated list
gateway.dns.public.record-typestringCNAMEDNS record type (e.g., CNAME, A, AAAA)
gateway.dns.public.provider-specificMapalias=true,aws/target-hosted-zone=same-zoneProvider-specific DNS settings for public endpoints
gateway.dns.public.annotationsMapAnnotations to add to the public DNSEndpoint resource
networkpolicies.enabledbooltrueEnable network micro-segmentation within a Stack namespace, including Ledger v3 Raft and migration traffic isolation

Postgres URI format

Scheme: postgresql

Query params :

NameTypeDefaultDescription
secretstringSpecify a secret where credentials are defined
secretCredentialsEncodingstringurlEncodedUse raw to let the operator URL-encode the credentials read from the secret
disableSSLModeboolfalseDisable SSL on Postgres connection

In addition to the parameters above, any extra query parameters included in the URI are passed through to the final POSTGRES_URI environment variable. This is useful for managed PostgreSQL providers (e.g. Google Cloud SQL, Azure Database) that require additional connection parameters.

For example:

postgresql://user:pass@host:5432?sslmode=require&tcpKeepAlive=true

The secret, secretCredentialsEncoding, and disableSSLMode parameters are consumed by the operator and will not appear in the resulting connection string. When disableSSLMode=true is set, it overrides any sslmode parameter with sslmode=disable.

Credentials stored in the referenced secret are assumed to be URL-encoded by default for compatibility with operator versions before v3.12.0. Set secretCredentialsEncoding=raw when the username and password keys contain raw values that the operator must encode before building the PostgreSQL URI.

ElasticSearch URI format

Scheme: elasticsearch

Query params :

NameTypeDefaultDescription
secretstringSpecify a secret where credentials are defined

Temporal URI format

Scheme : temporal

Path : Match the temporal namespace

Query params :

NameTypeDefaultDescription
secretstringSpecify a secret where temporal certificates are defined
encryptionKeySecretstringSpecify a secret where temporal encryption key is defined
initSearchAttributesstringfalseInitialize search attributes on temporal namespace

Broker URI format

Scheme : nats | kafka

Broker URI format (nats)

Scheme: nats

Query params :

NameTypeDefaultDescription
replicasnumber1Specify the number of replicas to configure on newly created nats streams

Broker URI format (kafka)

Scheme: kafka

Query params :

NameTypeDefaultDescription
saslEnabledboolfalseSpecify is sasl authentication must be enabled
saslUsernamestringUsername on sasl authentication
saslPasswordstringPassword on sasl authentication
saslMechanismstringMechanism on sasl authentication
saslSCRAMSHASizestringSCRAM SHA size on sasl authentication
tlsboolfalseWhether enable ssl or not

The process is always the same: you create a YAML file, submit it to Kubernetes, and the Operator takes care of the rest. All the values present in the Metadata section are not used by the Operator. Conversely, the Spec section is used to define the Operator's parameters. You will always find 3 parameters there:

  • stacks: defines the stacks that should use this configuration (you can put a * to indicate that all stacks should use this configuration)
  • key: defines the key of the configuration (you can put a * so that it applies to all services)
  • value: defines the value of the configuration

Examples

Define PostgreSQL clusters

In this example, you will set up a configuration for a PostgreSQL cluster that will be used only by the formance-dev stack but will apply to all the modules of this stack. Thus, the different modules of the Stack will use this PostgreSQL cluster while being isolated in their own database.

:::info This database is created following the format: {stackName}-{module} :::

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-postgres-uri
spec:
  key: postgres.*.uri
  stacks:
    - 'formance-dev'
  value: postgresql://formance:formance@postgresql.formance-system.svc:5432?disableSSLMode=true

Use AWS IAM Role

In this example, you'll use an AWS IAM role to connect to the database. The formance-dev stack will use this configuration.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: aws-rds-access-role
  namespace: formance-system
  labels:
    formance.com/stack: any
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::AWS_ACCOUNT_ID:role/AWS_ROLE_NAME
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-postgres-uri
spec:
  key: postgres.*.uri
  stacks:
    - 'formance-dev'
  value: postgresql://formance@postgresql.formance-system.svc:5432

Define module resource requests

In this example, you'll set up a configuration for the resource requests of the formance-dev stack. This configuration will apply to all the modules of this stack.

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-resource-requests
spec:
  key: deployments.*.containers.*.resource-requirements.requests
  stacks:
    - 'formance-dev'
  value: cpu=10m,memory=100Mi

Define a Broker

In this example, you'll set up a configuration for the Broker of the formance-dev stack. This configuration will apply to all the modules of this stack.

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-broker
spec:
  key: broker.dsn
  stacks:
    - 'formance-dev'
  value: nats://nats.formance-system.svc:4222?replicas=3

Define a OpenTelemetry Collector

In this example, you'll set up a configuration to send traces and metrics to an OpenTelemetry collector. This configuration will apply to all modules in this stack.

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: stacks-otel-collector
spec:
  key: opentelemetry.*.dsn
  stacks:
    - "formance-dev"
  value: grpc://opentelemetry-collector.formance-system.svc:4317?insecure=true

Configure Database Jobs Security context - Run As

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-database-create-run-as
spec:
  key: jobs.database.containers.create-database.run-as
  stacks:
    - 'formance-dev'
  value: user=1234,group=1234
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-database-drop-run-as
spec:
  key: jobs.database.containers.drop-database.run-as
  stacks:
    - 'formance-dev'
  value: user=1234,group=1234

Configure ledger migrate job security context - Run As

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-ledger-migrate-run-as
spec:
  key: jobs.ledger.containers.migrate.run-as
  stacks:
    - 'formance-dev'
  value: user=1234,group=1234

Configure DNS Endpoints

The operator can automatically generate externaldns.k8s.io/v1alpha1 DNSEndpoint resources for your Gateway components. This allows you to configure DNS records that will be managed by the external-dns operator.

DNS endpoints are linked to Gateway components and are created in the stack namespace. You can configure both private and public DNS endpoints independently.

Private DNS Endpoint Example

In this example, you'll configure a private DNS endpoint for the formance-dev stack. The DNS endpoint will be created with the name {gateway-name}-private and will point to the specified targets.

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-private-enabled
spec:
  key: gateway.dns.private.enabled
  stacks:
    - 'formance-dev'
  value: "true"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-private-dns-names
spec:
  key: gateway.dns.private.dns-names
  stacks:
    - 'formance-dev'
  value: "{stack}-eks-euw1-01.dev.acme.frmnc.net,{stack}.dev.acme.frmnc.net"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-private-targets
spec:
  key: gateway.dns.private.targets
  stacks:
    - 'formance-dev'
  value: "rp-01-eks-euw1-01.dev.acme.frmnc.net"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-private-provider-specific
spec:
  key: gateway.dns.private.provider-specific
  stacks:
    - 'formance-dev'
  value: "alias=true,aws/target-hosted-zone=same-zone"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-private-annotations
spec:
  key: gateway.dns.private.annotations
  stacks:
    - 'formance-dev'
  value: "service.beta.kubernetes.io/aws-load-balancer-internal=true"

Public DNS Endpoint Example

In this example, you'll configure a public DNS endpoint for the formance-dev stack. The DNS endpoint will be created with the name {gateway-name}-public.

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-public-enabled
spec:
  key: gateway.dns.public.enabled
  stacks:
    - 'formance-dev'
  value: "true"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-public-dns-names
spec:
  key: gateway.dns.public.dns-names
  stacks:
    - 'formance-dev'
  value: "{stack}.acme.frmnc.net"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-public-targets
spec:
  key: gateway.dns.public.targets
  stacks:
    - 'formance-dev'
  value: "rp-01-eks-euw1-01.dev.acme.frmnc.net"
---
apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: formance-dev-dns-public-provider-specific
spec:
  key: gateway.dns.public.provider-specific
  stacks:
    - 'formance-dev'
  value: "alias=true,aws/target-hosted-zone=same-zone"

DNS Settings Details

  • DNS Names: The dns-names setting supports the {stack} placeholder which will be replaced with the actual stack name. You can specify multiple DNS names by separating them with commas. Each DNS name will create a separate endpoint in the DNSEndpoint resource.

  • Targets: Multiple targets can be specified by separating them with commas. All targets will be added to each DNS record endpoint.

  • Record Type: Defaults to CNAME if not specified. Common values include CNAME, A, AAAA, TXT, etc.

  • Provider-Specific Settings: These are provider-specific DNS configurations. For AWS Route53, common settings include:

    • alias=true: Enable alias records
    • aws/target-hosted-zone=same-zone: Use the same hosted zone for the target
  • Annotations: Annotations added to the DNSEndpoint resource. Useful for provider-specific configurations or metadata.

:::info DNS endpoints are created per Gateway component. If you have multiple Gateways in a stack, each will have its own DNS endpoints named {gateway-name}-private and {gateway-name}-public. :::

:::warning The external-dns operator must be installed in your cluster for DNS endpoints to be processed. The operator only creates the DNSEndpoint resources; the external-dns operator is responsible for actually creating the DNS records. :::

Configure Network Policies

The operator can create Kubernetes NetworkPolicies to enforce network micro-segmentation within a Stack namespace. When enabled, the following rules are applied:

  • All ingress traffic is denied by default to all pods in the namespace
  • The Gateway is accessible by everyone (it is the entry point)
  • All other services (Ledger, Payments, Auth, etc.) are only accessible from the Gateway
  • Ledger v3 replicas can communicate with each other on the Raft and service gRPC ports
  • Ledger v3 can read Ledger v2 only in its own Stack namespace

Egress traffic is not restricted. This is required for public Auth issuers and external monitoring endpoints. The Ledger v3 mirror must use the internal http://ledger:8080 Service; ingress isolation on the destination ensures that only a Ledger v3 pod from the same Stack namespace can reach it directly.

NetworkPolicies are owned by the Stack and are automatically garbage-collected when the Stack is deleted.

Enable Network Policies

apiVersion: formance.com/v1beta1
kind: Settings
metadata:
  name: enable-networkpolicies
spec:
  key: networkpolicies.enabled
  stacks:
    - '*'
  value: "true"

Created NetworkPolicies

When enabled, 6 NetworkPolicies are created in the Stack namespace:

NameEffect
default-deny-ingressDenies all ingress traffic to all pods
allow-gateway-ingressAllows all ingress traffic to pods labeled app.kubernetes.io/name: gateway
allow-from-gatewayAllows ingress traffic from gateway pods to all other pods
allow-ledger-v3-clusterAllows Raft and service gRPC traffic between direct Ledger v3 replicas on ports 7777 and 8888
allow-ledger-v3-preview-clusterAllows the same cluster traffic for preview Ledger v3 replicas while preserving their historical pod selector
allow-ledger-v2-from-v3Allows Ledger v3 to reach Ledger v2 on port 8080 within the same Stack namespace

Since Kubernetes NetworkPolicies are additive, the Gateway receives both deny-all and allow-all, making it fully accessible. Other services receive deny-all and allow-from-gateway, restricting access to Gateway only.

:::warning A CNI plugin that supports NetworkPolicies (Calico, Cilium, etc.) must be installed in your cluster. The default CNI in some environments (e.g., Flannel) does not enforce NetworkPolicies. :::

:::info If no Gateway module is deployed, the policies are still created. The deny-all policy protects all services, and the allow-from-gateway rule has no matching source — resulting in all ingress being blocked, which is the safest default. :::