Prerequisites
The chart installs the Edisyl platform into a namespace on a cluster you run. You do not need a copy of the chart repository — the chart is pulled from our OCI registry, and everything below is configured through Helm values.
Cluster
Section titled “Cluster”| Kubernetes | 1.28 or later |
| Helm | 3.8 or later, for OCI registry support |
| AWS CLI | To obtain a registry token |
| Docker | Only to verify a pull by hand |
| Ingress | An NGINX ingress controller, if you want the apps reachable from a browser |
| DNS | A zone you control, for one wildcard record |
| TLS | cert-manager, or a certificate you supply yourself |
| Storage | A StorageClass that can actually provision — the LLM gateway always claims a volume, and so does any bundled datastore |
| PostgreSQL | 16, with pgvector available — bundled, or your own (RDS, Cloud SQL, self-run) |
| Redis | With RediSearch and RedisJSON — bundled, or your own |
| Object storage | Bundled MinIO, AWS S3, or another S3-compatible store (Cloudflare R2 through its S3 endpoint) |
| Identity provider | A SAML 2.0 IdP your users sign in through, with its metadata as an HTTPS URL or an XML file |
| A sender address for the platform’s mail |
PostgreSQL, Redis and object storage are covered below: each can be bundled with the chart or supplied by you, and only Redis is bundled by default.
The chart installs everything into the single namespace you pass to
helm install -n, and it does not create or label namespaces.
One exception. The bundled Inngest subchart takes its namespace from its
own value, which defaults to edisyl. If you install into a namespace with
any other name you must override it to match, or Inngest lands somewhere its
consumers and their Secrets are not:
inngest: namespace: { create: false, name: <your-namespace> }Pod Security Standards
Section titled “Pod Security Standards”The chart’s own pods — the applications and the install hooks — satisfy the
restricted Pod Security Standard: non-root, no privilege escalation, all
capabilities dropped, seccompProfile: RuntimeDefault.
The bundled third-party subcharts do not. Their pod specs do not satisfy
restricted as shipped. If you enable bundled object storage into a namespace
labelled pod-security.kubernetes.io/enforce: restricted, those pods are
rejected outright. Label the namespace baseline instead, or exempt the
workload through whatever mechanism your cluster provides.
Reaching it from a browser
Section titled “Reaching it from a browser”Three things have to line up before anyone can sign in: something to route the traffic, a name that resolves to it, and a certificate for that name.
An ingress controller
Section titled “An ingress controller”The chart renders an Ingress per public app, all conventionally class nginx.
Either NGINX controller works. The auth ingress emits both F5’s
nginx.org/rewrites and the community controller’s use-regex and
rewrite-target, and each reads its own. On chart versions before 0.26.0 the
F5 syntax was the only one, and on the community controller every auth route
404’d with the pod Ready and the Ingress admitted.
Every public URL derives from config.domain: api.<domain>, auth.<domain>,
and the web application at edisyl.<domain>, not app.<domain>.
One wildcard record pointed at your ingress load balancer covers all of them:
| Type | Name | Target |
|---|---|---|
| CNAME | *.<your-domain> |
your load balancer’s hostname |
Two requirements, whoever hosts your zone:
- No proxy or CDN mode. An internal load balancer resolves to private addresses a proxy cannot reach, and the failure hits every hostname at once and reads like the cluster is down. On Cloudflare that means a grey cloud, not orange.
- A CNAME to the load balancer’s hostname, not an A record to its addresses. Those addresses are not stable; the hostname is.
Nothing further works until it resolves, so confirm before installing:
dig +short api.<your-domain>TLS certificates
Section titled “TLS certificates”Every app’s Ingress ships annotated for cert-manager, alongside its own
tlsSecret name:
cert-manager.io/cluster-issuer: letsencrypt-prodkubernetes.io/tls-acme: "true"So out of the box the chart expects cert-manager installed, and a
ClusterIssuer named exactly letsencrypt-prod. The name is a value rather
than a hardcode — edisyl.<app>.ingress.annotations is yours to override — but
an issuer by any other name is never consulted, no certificate is ever
requested, and the browser gets a default self-signed certificate on a host
that otherwise works.
If your load balancer is internal, the issuer must solve DNS-01, not HTTP-01. Let’s Encrypt cannot reach a private address to answer an HTTP challenge; proving control of the zone works however private the endpoints are.
If you cannot give cert-manager credentials for your DNS at all, skip it:
issue a wildcard certificate yourself, load it as a TLS Secret in the install
namespace, and point each app’s ingress.tlsSecret at it. You then own
renewal.
TLS is not optional for a production install. The web application marks its
session cookies Secure whenever config.appEnv is anything but local, and
a browser drops a Secure cookie over plain http — so sign-in completes, lands
back on the login screen with no session, and logs nothing.
Datastores
Section titled “Datastores”The platform needs a database, a Redis and an S3-compatible object store. Each can be bundled with the chart or supplied by you — and only Redis is bundled by default. Using your own infrastructure has the full settings for each; this is what you need to have decided before you start. The configurator asks each of these decisions in turn and lists the prerequisites your answers imply.
Database — PostgreSQL 16
Section titled “Database — PostgreSQL 16”| Option | How |
|---|---|
| Bundled CloudNativePG | cnpg.enabled: true and database.source: cnpg |
| Your own PostgreSQL, including AWS RDS or Cloud SQL | database.source: managed, with the URL in secrets.managed.edisyl-database |
| A connection Secret already in the namespace | database.source: existing-secret, named in database.existingSecret |
A managed service is the second row — the chart only needs a connection URL, so RDS, Cloud SQL and a Postgres on a VM are the same case.
pgvector is required. The first migration runs CREATE EXTENSION vector,
so the extension must be available on the server. The chart cannot check
this, and a server without it fails minutes into the install rather than at
render time. The bundled operand image carries it.
Two more databases on the same server. The auth service and the LLM gateway, which run in every install, each need a database and a role of their own, never the application’s. The gateway reads its connection as discrete keys — host, port, user, password, database — not a URI. Only the bundled cluster can create these itself; with your own Postgres you provision both — see Using your own infrastructure.
Both of the following apply only if you enable the bundled cluster.
The CloudNativePG operator must already be installed
Section titled “The CloudNativePG operator must already be installed”The chart renders a Cluster custom resource; it does not install the
CloudNativePG operator that acts on it. Install
the operator first, or the resource is created and nothing ever reconciles it.
The operand image carries pgvector
Section titled “The operand image carries pgvector”cnpg.imageName defaults to CloudNativePG’s own public, multi-arch
ghcr.io/cloudnative-pg/postgresql:16-standard-bookworm, which carries the
pgvector extension the platform’s first
migration needs. Earlier chart versions defaulted to an image that was never
published, and the failure was late: CloudNativePG starts happily without
pgvector and the migration fails minutes later.
Override it only if you mirror images internally. Whatever you point at must include pgvector.
Upgrading an existing cluster onto this default changes the base OS from bullseye to bookworm, and the glibc change can reorder non-C collations. Read the chart README’s note on that before bumping a cluster that already holds data.
| Option | How |
|---|---|
| Bundled Redis | redis.enabled: true — this one is on by default |
| Your own | redis.enabled: false, with the connection details supplied |
RediSearch and RedisJSON are both required, so a plain managed Redis will not work. Check that your provider actually offers both modules before choosing it; Redis Cloud does, and the bundled server runs Redis 8, which ships both. This is the requirement most likely to rule out a managed Redis you already run.
Your own Redis is named by host, port, user and TLS setting under
config.redis*, with its password as a credential — a single REDIS_URL is
refused. It also needs a password: the API refuses to start on a blank one. A
Redis behind Sentinel works, with limits on the job runner’s connection — see
Using your own infrastructure.
Object storage — S3-compatible
Section titled “Object storage — S3-compatible”| Option | config.objectStorageProvider |
|---|---|
| Bundled MinIO | — (minio.enabled: true) |
| AWS S3 | s3 |
| Another S3-compatible store — MinIO you run yourself, Ceph, Wasabi, or Cloudflare R2 through its S3 endpoint | s3compatible |
s3 means real AWS. Using it for anything else is the mistake that
produces storage which renders cleanly, starts healthy and writes nowhere. For
a non-AWS store use s3compatible, which requires config.objectStorageUrl
and both access keys — there is no instance-role fallback outside AWS. For R2
the URL is https://<account-id>.r2.cloudflarestorage.com; the provider value
r2 was removed in chart 0.42.0 and is refused.
Whichever you choose, config.objectStorageBucket is required.
Credentials for any of these belong in your gitignored values file or a secret manager — see Secrets and signing keys.
Signing in and sending mail
Section titled “Signing in and sending mail”A SAML identity provider. SAML through your IdP is the only way anyone signs in — Okta, Entra ID, Google Workspace or any SAML 2.0 IdP. You declare it in values before the install: its metadata, as an HTTPS URL or the XML document, and the email domains that sign in through it. Create the SAML application in your IdP first, with the auth service’s entity ID and ACS URL, and an assertion that carries the user’s email address. A metadata URL is fetched from inside the cluster, so the cluster needs egress to the IdP’s host. See Authentication.
A sender address. config.emailFrom is required in every install, even
with outbound mail off: the API reads it at boot.
What we give you
Section titled “What we give you”Registry access is granted per customer. Once you have sent us the IAM principal that will pull, you receive a client ID, a role ARN built from it, an external ID, and the list of repositories your role can read. If you have not, contact us before going further — nothing below works without it. Registry access has the whole exchange.
Was this page helpful?
Thanks for the feedback.
