Skip to main content

Install the DALP Helm chart

The DALP umbrella chart: what its subcharts default to, what a release gates on, and where the Gateway API CRDs come from.

The dalp Helm chart is an umbrella chart: one release that composes its subcharts into a DALP Instance, each subchart pinned to the same chart version. You install it with one helm install after you apply the Gateway API CRD bundle once per cluster, and the release waits on its database migration, not on index rebuilds. The chart needs Kubernetes 1.23 or later, because it renders autoscaling/v2 HorizontalPodAutoscalers. The Subcharts table lists the components, Topologies explains which to enable, and Values and secrets prepares the deployment settings.

Install

Apply the Gateway API CRD bundle once per cluster, before the first install. The docs subchart renders an HTTPRoute by default, and dapp (the Console), dapi (the Platform API), blockscout and investorPortal render one when you set their httpRoute.enabled. A release that renders any of them without the CRDs fails to create it:

helm template envoy-gateway-crds oci://docker.io/envoyproxy/gateway-crds-helm \
  --version 1.10.0 \
  --set crds.gatewayAPI.enabled=true \
  --set crds.gatewayAPI.channel=standard \
  --set crds.envoyGateway.enabled=true \
  | kubectl apply --server-side --force-conflicts -f -

--server-side is required for this apply. The command pulls the bundle from Docker Hub, and the bundle is not part of DALP. --force-conflicts takes over any field another tool set on these CRDs, so run it only on a cluster where nothing else manages Gateway API, or mirror the bundle and apply your own copy. Gateway API CRDs ship separately explains why the bundle is a separate step and how often to repeat it.

Install the chart from the SettleMint OCI registry, pinning the chart version you deploy:

helm install dalp oci://pkg.distribution.settlemint.com/settlemint/dalp \
  --version <chart version> \
  --namespace dalp --create-namespace \
  --values my-values.yaml

Supply your own my-values.yaml, or repeated --set key=value flags, on top of the chart's defaults. A first install fails until it sets global.datastores for PostgreSQL and Redis, the object storage settings, ENCRYPTION_MASTER_PASSWORD, BETTER_AUTH_SECRET, CUSTODY_CREDENTIAL_IDENTITY_KEY and global.dalpApp.env.DALP_GOVERNANCE_DOMAIN_SCOPE, as Values and secrets describes. The chart and its images come from SettleMint's registries, and Prerequisites lists the pull access the cluster needs. Deploying to OpenShift needs the additional overlay in OpenShift.

Once helm install returns, helm status dalp --namespace dalp reports deployed and kubectl get pods --namespace dalp shows every pod Running or Completed. With local sign-in, the first account created on the fresh instance becomes a Platform admin and every later sign-up is a member, so create the first account before the Console is reachable from outside your network. Security and compliance posture says how to restore the role when no Platform admin is left.

The path a DALP release moves through, from the Gateway API CRD prerequisite to a pod serving traffic.Five states in sequence, with three off-ramps. First, the Gateway API CRD bundle must already be applied to the cluster, once per cluster and before install. Skipping it fails HTTPRoute creation at helm install or upgrade time. Second, the migration hook runs as a pre-install or pre-upgrade Helm hook, before any workload rolls out. If it fails, the release fails immediately and the job survives only until the next helm upgrade retry reclaims it, so its logs need reading before then. Third, the workload rollout begins, with each subchart rolling on its own update strategy. Fourth, the startupProbe clears, on a boot budget sized per workload: 180 seconds for dapi, 300 seconds for dapp. Fifth, the readinessProbe clears and the pod starts serving traffic, on a budget that also differs per workload: 10 seconds for dapi's Postgres-bound check, 3 seconds for dapp's process-only check. An index rebuild or shard relocation on the indexer or runner workload runs behind an already-healthy deployment and is not part of this gate.The path a DALP release moves through, from the Gateway API CRD prerequisite to a pod serving traffic.The same figure as the desktop variant, drawn portrait for phone widths. Five states in sequence, with three off-ramps drawn inline: the Gateway API CRD bundle must already be applied to the cluster, once per cluster and before install, or HTTPRoute creation fails at install or upgrade time. The migration hook runs next and gates the release if it fails, and the failed job survives only until the next helm upgrade retry reclaims it. The workload rollout begins on each subchart's own strategy. The startupProbe clears on a per-workload boot budget of 180 seconds for dapi and 300 seconds for dapp. The readinessProbe clears and the pod serves traffic on a budget of 10 seconds for dapi's Postgres-bound check and 3 seconds for dapp's process-only check. An index rebuild or shard relocation on the indexer or runner workload runs behind an already-healthy deployment and never gates the release.

A release moves through five states, with off-ramps for a missing CRD bundle, a failed migration hook and a helm upgrade that runs out its wait.

Migrations gate a release, index rebuilds do not

A schema migration runs as a Helm hook on the dapp subchart, and whether it gates the release depends on dapp.migrator.hookType. The default, pre, runs it as a pre-install and pre-upgrade hook before any workload rolls out, so a failed migration fails the release before a pod ever serves traffic. Set it to post when the same Helm release creates the database, so it exists before the migration runs against it. Set it to none to run the job as a plain, unhooked Job with no release gate.

By default, a failed job survives the upgrade that ran it, and the next helm upgrade removes it, so read its logs before you retry. With the pre default, a catch-up run of the same migration also follows the rollout and can still fail the release. Upgrades and migrations states the retry limit, timeout and clean-up defaults.

An indexer index rebuild is a different kind of operation and does not block a release. It runs behind an already-healthy deployment, re-derives projections from already-ingested chain data, and a rebuild in progress does not fail or delay helm upgrade. Do not wire an index rebuild into a release gate the way you would a migration. A failed pre-upgrade migration blocks the new rollout. A projection rebuild has a separate completion signal. Affected reads can remain stale or unavailable until it completes.

Subcharts

Subchart
Condition
common
always installed
dapp (the Console)
dapp.enabled, on by default
dapi (the Platform API)
dapi.enabled, on by default
docs (this documentation site)
docs.enabled, on by default
pgbouncer
global.pgbouncer.enabled, on by default
postgres-exporter
postgres-exporter.enabled, on by default
blockscout
blockscout.enabled, off by default
investorPortal (Investor portal)
investorPortal.enabled, off by default

pgbouncer gates both the connection-pooling workload and the application URL routing, so it also decides how dapp and dapi reach PostgreSQL. investorPortal deploys the Investor portal, the standalone investor-facing web app for sign-in, funding, primary-issuance offers and secondary-market trading. The chart also carries canton, investorDocs, utilaSigningAgent and simulator blocks, all off by default.

Your deployment
Enable this
Standard EVM asset issuance, no explorer, no investor-facing portal
Keep the defaults (dapp, dapi, docs, pgbouncer, postgres-exporter). Enable Blockscout or the Investor portal only when the deployment needs it.
Needs an on-chain block explorer
blockscout.enabled: true. Blockscout ships as its own subchart with its own dedicated PostgreSQL database.
Serves investor-facing primary or secondary market access
investorPortal.enabled: true. The Investor portal is a standalone web app on the published SDK, deployed as its own workload.

Every subchart pins the same chart version as the umbrella chart, so a dalp release at one version installs dapp, dapi, docs and the rest at that same version. Mixing subchart versions inside one release is not supported.

Gateway API CRDs ship separately

The docs subchart creates an HTTPRoute by default, and dapp, dapi, blockscout and investorPortal do when their httpRoute is enabled, but the dalp chart does not bundle the Gateway API custom resource definitions. The bundle exceeds the size Helm allows inside a release's stored secret, and Helm cannot upgrade CRDs that ship inside a subchart's own crds/ directory anyway, so a chart that bundled them could install once and never refresh them again.

Apply the bundle as a separate step, with the command in Install: gateway-crds-helm, applied server-side once per cluster. The controller that serves the routes is yours to supply and is not part of the dalp chart.

The CRD bundle's version is the one in that install command, not the dalp chart version, so a dalp upgrade alone never tells you the CRDs also need a refresh.

Kubernetes validates a resource's kind against installed CRDs before it stores it. A release started without the bundle applied fails to create its HTTPRoute resources, and that surfaces as a helm install or helm upgrade error.

Values to set

  • global.pgbouncer.enabled, default true. It also decides how dapp and dapi reach PostgreSQL. See Subcharts.
  • dapp.migrator.hookType, default pre. Sets whether a failed migration gates the release at all. See Migrations gate a release, index rebuilds do not.
  • dapp.migrator.timeout, default 600 seconds. It bounds all attempts together.
  • overprovisioning.enabled, default false. Reserves standing rollout headroom so an upgrade does not stall on a tight scheduler pool.
  • Chart version. Pin it on every install and upgrade so that each release is a reviewed change.
  • Gateway CRD bundle version, set by the --version of the CRD command. It changes independently of the dalp chart version, so check both. See Gateway API CRDs ship separately.

What goes wrong

A helm upgrade times out before the last workload reports ready

Run helm upgrade with a --timeout sized to your deployment's worst case, such as 30 to 40 minutes, and adjust for your cluster. Helm's own default wait is 5 minutes. A timeout abort does not roll back completed steps by itself, so check helm history and the workload status before you retry. progressDeadlineSeconds is 1500 seconds (25 minutes) on both dapi and dapp, above the Kubernetes default of 600. A single surge pod can spend up to its boot budget plus relaunch headroom before /readyz reports ready, so a tighter number needs its own margin. On the Kubernetes default of 600 seconds, dapp would trip before the PgBouncer pooler it waits on, which needs up to 900, and that reads as a Console failure when the fault is the pooler. dapp's 1500 seconds outlasts it. The API fleets roll with maxSurge: 1 and maxUnavailable: 0, one pod at a time, so a tight timeout can fire before the last workload reports ready. A tight scheduler pool adds its own stall: see overprovisioning.enabled.

The readiness check keeps failing and the database looks fine

Kubernetes' readiness and startup probes differ by workload in what they check and how long they wait, so confirm which one is failing with kubectl describe pod before assuming Postgres is the problem. dapi's /readyz probes Postgres through a dedicated health session every 15 seconds. The probe has a 10-second timeout and allows three failures. An unavailable database removes the pod from service after roughly 45 seconds, or up to about 55 seconds including reporting lag. dapi's startup probe answers on /healthz instead, with its own 180-second boot budget. dapp's startup and readiness probes both answer on /readyz. Its liveness probe answers on /healthz. The startup probe budgets 300 seconds (5 minutes) for slow first-boot SSR and SDK warmup, and only once that clears does the readiness probe start applying its own 3-second, process-only budget. A pod stuck in that 300-second startup window looks identical to a hung readiness probe in kubectl get pods output, so check which probe is failing before assuming either.

An indexer or runner rollout looks stuck when the api workload rolled fine

dapi renders each role as its own Deployment: the /api/v1 and /api/v2 fleets, a runner, a scheduler, and an indexer with a separate rebuild Deployment. The API fleets and the runner have their own HorizontalPodAutoscaler. The indexer, its rebuild pods and the scheduler run a fixed replica count. The API fleets roll maxSurge: 1 and maxUnavailable: 0, the same one-at-a-time pattern as dapp. The indexer rolls the other way, maxUnavailable: 1 and maxSurge: 0, and holds a replacement back for a 90-second minReadySeconds before it counts as ready. The runner and the scheduler use the Recreate strategy, so their old pods stop before the new ones start. The indexer and the runner each have a PodDisruptionBudget that keeps at least one pod up. Expect the indexer, runner and scheduler to take an old pod down before a new one is ready. This protects capacity in those roles and is not a stuck rollout.

Redis restarts and pods keep serving

Redis does not gate startup. Cache and notification connections are supervised per pod, so bringing the platform up before Redis is ready is supported: pods serve from their in-process cache and reconnect on a jittered exponential backoff capped at 30 seconds, with no attempt limit. Reconnection checks that Redis is serving requests rather than only accepting connections, so an endpoint still loading a snapshot is retried instead of treated as healthy. While disconnected, cached reads stay correct but remain private to each pod, and change notifications use each listener's own reconcile interval. Watch dalp_redis_client_unavailable_seconds, labelled per redis_role, so you can tell which of the two clients, cache or notifications, is the one that dropped. The RedisClientLatchedOffline alert (severity warning) confirms roughly six minutes into a sustained outage, identifying a pod that has not recovered from more than a brief restart.

An indexer's head socket drops and live indexing waits for the reconnect

The Ledger Data indexer learns about new blocks from a standard newHeads subscription on the chain node's WebSocket. It sends eth_blockNumber on the same socket every 10 seconds. It reconnects when no reply arrives, or when the reply is more than three blocks past the last head the subscription delivered. It reads the head over HTTP on every new subscription, so blocks mined during an outage are indexed after the reconnect. A connection-state gauge reports three values (connected, disconnected, or circuit broken), and a separate dalp_didx_ws_unavailable_seconds series tracks how long the socket has been down, because the state gauge alone cannot distinguish a two-second blip from a standing outage. The IndexerWebSocketHeadLatchedOffline alert (severity warning) confirms roughly six minutes into a sustained outage, keyed per network, long enough that an ordinary reconnect never pages. Live indexing does not advance while the socket is down, because no poll runs beside the subscription. Once the socket reconnects, the indexer reads the current head and indexes every block it missed.

Frequently asked questions

Does turning off a subchart delete its data?

Disabling a subchart removes its rendered resources from the release on the next upgrade. External databases are separate, but PVC retention and underlying volume deletion depend on the workload and storage policies. Inspect the rendered change and confirm a recoverable backup before disabling a stateful component.

How often do the Gateway API CRDs need reapplying?

Only when you move to a newer bundle version. A dalp upgrade alone does not need it. Gateway API CRDs ship separately explains why.

Does each Platform API role scale on its own?

Yes. By default the /api/v2 fleet ranges 2 to 24 replicas and the /api/v1 fleet 1 to 4, both at 50% CPU utilisation, and the runner ranges 2 to 12 replicas at 75%. The indexer does not autoscale: it runs dapi.indexerDeployment.replicaCount pods, 2 by default, next to its rebuild pods. The scheduler runs one pod. A replica-count change on one role leaves the other roles, and each role's own rollout strategy, untouched.

Does the migration job still run if I disable dapp?

No. The migration job is a dapp subchart template, so dapp.enabled: false skips the whole subchart, migration hook included, the same way it skips every other dapp object. A deployment without dapp must arrange the required database migration separately before starting services against the new schema.

Was this page helpful?