Configuration
Everything is configured through a Helm values file. Below is a working
starting point — copy it, change the marked values, and pass it with -f.
The values reference documents every setting the chart accepts.
A starting values file
Section titled “A starting values file”env: production
images: # The chart's own defaults already point at the registry your role can pull # from, with a tag pinned per app. The pull secret is the one thing it # cannot supply — ECR allows no anonymous pull. Omit it only if you use a # kubelet credential provider. pullSecret: edisyl-ecr
config: appEnv: production nodeEnv: production # One line. Every public URL derives from it. Each app is served at # <app>.<domain>, so this value gives api.example.com, auth.example.com, # and the web application at edisyl.example.com. Do not include an app # label here — "edisyl.example.com" would put the web app at # edisyl.edisyl.example.com. domain: example.com
bootstrap: # No owner exists until at least one admin email is set. With none, the # freshly-seeded database has no owner and nothing owner-gated can be # created. Use an address you will actually sign in as. adminEmails: ["admin@example.com"]
# Your own PostgreSQL 16. It must have the `vector` extension AVAILABLE: the# first migration runs CREATE EXTENSION vector, and a server without it fails# minutes into the install rather than at render.database: source: managed
secrets: provider: k8s-native managed: # Keep the real URL in a gitignored values file passed with a second -f, # not in the file you commit — see Secrets and signing keys. edisyl-database: data: DATABASE_URL: "postgresql://user:pass@host:5432/edisyl" # PROVIDER is an enum: r2 | s3 | s3compatible. `s3` means REAL AWS — # using it for anything else produces storage that renders cleanly, # starts healthy and writes nowhere. Use `s3compatible` for MinIO, Ceph # or any other S3 API, and `r2` for Cloudflare R2. # # For real AWS S3: set REGION, leave ENDPOINT empty, and prefer a pod IAM # role over the two keys. For anything else: ENDPOINT and both keys are # required — there is no instance-role fallback outside AWS. edisyl-object-storage: data: OBJECT_STORAGE_PROVIDER: "s3" OBJECT_STORAGE_BUCKET: "your-bucket" OBJECT_STORAGE_REGION: "us-east-1" OBJECT_STORAGE_ENDPOINT: "" OBJECT_STORAGE_ACCESS_KEY_ID: "" OBJECT_STORAGE_SECRET_ACCESS_KEY: ""
# The bundled Inngest subchart does not read the install namespace. If you# install anywhere other than a namespace called "edisyl", this must match it.inngest: namespace: { create: false, name: edisyl }
edisyl: # Run the platform's own auth service and point the applications at it. # Both lines are needed — see Authentication. authSource: selfhosted auth: enabled: trueNothing stateful is bundled
Section titled “Nothing stateful is bundled”PostgreSQL and object storage are both off by default. A chart that stands up a database and an object store unless told otherwise hands you a production datastore you never chose, on whatever StorageClass happens to be default, holding the only copy of your data.
So the file above names what the chart cannot guess. It refuses to render until each is coherent, and every refusal names the key to set.
Authentication is not a bundle at all: chart 0.28.0 removed the vendored Supabase stack. The platform runs its own auth service, as the file above turns on, and your users sign in through your SAML identity provider, which you register with that service after the install — see Authentication and the configurator, which writes the registration commands for you.
Bundled Redis and Inngest are still on. Neither holds durable state you would mourn, nor needs a credential you have to go and get. If you replace Redis with your own, it must have RediSearch and RedisJSON.
To use the bundled datastores instead — an evaluation install, say:
cnpg: enabled: true # Your cluster's StorageClass. Leave "" to use the cluster's default. A name # that does not exist leaves the PVCs Pending forever, so set this only if # you mean a specific class. The default provisions 348Gi across three # instances. storageClass: ""database: source: cnpgminio: enabled: truecnpg.enabled and database.source are separate on purpose: the cluster
existing and the applications being pointed at it are separate decisions. The
chart refuses the combinations that disagree rather than rendering one that
half-works.
What you must change
Section titled “What you must change”| Value | Why |
|---|---|
images.pullSecret |
ECR allows no anonymous pull; unless you use a kubelet credential provider |
config.domain |
Every public URL derives from it, as <app>.<domain> |
bootstrap.adminEmails |
Empty means no owner exists in the database |
database.source and edisyl-database |
Nothing stateful is bundled — name your Postgres or turn the bundled one on |
edisyl-object-storage |
Same; the provider enum and bucket have no default |
inngest.namespace.name |
Must match your install namespace |
edisyl.authSource / edisyl.auth.enabled |
Run and use the platform’s auth service |
Per-app image tags
Section titled “Per-app image tags”A release does not always build every app from the same commit, so the chart
pins a tag per app and no app inherits a global one. images.tag is a
fallback nothing currently uses, which means setting it alone changes nothing.
To pin a different build than the chart’s default, override per app:
edisyl: api: image: { tag: prd-b857899 } auth: image: { tag: prd-04c5744 } edisyl: image: { tag: prd-b857899 } worker: image: { tag: prd-b857899 } lattice: image: { tag: prd-ff6f14d }The image matrix publishes this block ready to copy for each release. The registry and the tags move together — these tags exist under the chart’s default registry and nowhere else, so overriding one without the other fails the pull.
Labels
Section titled “Labels”commonLabels puts your own Kubernetes labels on every resource the chart
renders, pod templates included — so they are what a cost report, a network
policy or an admission controller sees:
commonLabels: team: platform cost-center: "4210"Selectors never include them, so adding or changing one later is an ordinary
rolling update. The chart’s own keys (app, app.kubernetes.io/*,
helm.sh/chart, platform.edisyl.com/env) cannot be overridden; setting one
fails the render.
The bundled subcharts render their own resources and never see this value.
To label those too, repeat the map under the keys each one reads:
inngest.commonLabels for Inngest, and both minio.additionalLabels (the
workload) and minio.podLabels (its pods) for MinIO. The configurator writes
all of them for you.
The LLM gateway
Section titled “The LLM gateway”The chart can run Bifrost, an LLM gateway that holds your provider credentials so no application does. It is off by default, and turning it on moves no traffic:
bifrost: enabled: true database: source: cnpg # or existing-secret — see Using your own infrastructureThe apps keep sending inference to the URL in
secrets.managed.edisyl-llm-gateway until you change it. Standing the gateway
up, adding providers through Edisyl’s settings and proving them answer, then
cutting over, are separate deployments on purpose.
Three things to know before enabling it:
- One replica, always. Bifrost’s open-source build does not synchronize budgets or rate-limit counters between processes, so the chart runs a single-replica StatefulSet and exposes no replica count. Every upgrade has a short window with no gateway.
- Its own database. The gateway migrates its own schema and never shares the application’s Postgres. With the bundled cluster the chart provisions it; with your own Postgres you do.
- The encryption key cannot be rotated. Every stored provider credential
is encrypted under
BIFROST_ENCRYPTION_KEY, and Bifrost cannot re-encrypt them. The chart generates it once and reads it back; back up theedisyl-bifrost-encryptionSecret.
The management console is not published unless you name an ingress class under
bifrost.console — and that class must be a private one, which the chart
cannot check for you. Console and inference share one port, so publishing it
publishes provider administration with it; on a public class the only thing
left guarding every vendor credential is one shared password. See the
values reference for the full
block.
Administering a gateway you don’t run
Section titled “Administering a gateway you don’t run”A deployment can administer a gateway another deployment runs — a staging
install pointed at production’s, say — without running one of its own. Set
bifrost.external.url to that gateway’s management address and leave
bifrost.enabled false:
bifrost: enabled: false external: url: http://bifrost.internal.example:8080The API gets the management address and the edisyl-bifrost credential, which
is what Edisyl’s gateway settings administer it with; no gateway pods, database
or encryption key render. The two are exclusive — a deployment either
runs its gateway or points at someone else’s — and the render refuses both at
once, or a URL without an http or https scheme.
The credential is the external gateway’s own, so the chart cannot generate it:
see Secrets and signing keys.
Inference still goes wherever edisyl-llm-gateway points, as with the bundled
gateway.
Bringing your own infrastructure
Section titled “Bringing your own infrastructure”Every bundled dependency — PostgreSQL, Redis, object storage, the job runner — can be turned off and replaced with a managed service you already run. If you are installing on a cloud, read Using your own infrastructure before you install: it is easier to start there than to migrate a database out of the cluster later.
Third-party components lists what is on by default.
If you run Argo CD
Section titled “If you run Argo CD”Read this before installing. The chart generates secrets once and reuses
them by looking up the existing Secret in the cluster. Argo CD normally
renders with helm template against no cluster, so that lookup returns empty
on every sync and every generated value is regenerated — including
ENCRYPTION_KEY, which orphans any data encrypted under the previous one, and
BIFROST_ENCRYPTION_KEY if you run the LLM gateway, which orphans every
provider credential it holds.
Under Argo CD you must supply these values explicitly, through
secrets.managed.*.data or secrets.provider: external-secrets, rather than
relying on generation.
Was this page helpful?
Thanks for the feedback.
