[{"data":1,"prerenderedAt":764},["ShallowReactive",2],{"navigation_docs":3,"-configure-data-services":136,"-configure-data-services-surround":759},[4,26,47,68,89,110,123],{"title":5,"path":6,"stem":7,"children":8,"page":25},"Getting Started","\u002Fgetting-started","1.getting-started",[9,13,17,21],{"title":10,"path":11,"stem":12},"What you are running","\u002Fgetting-started\u002Fwhat-you-are-running","1.getting-started\u002F1.what-you-are-running",{"title":14,"path":15,"stem":16},"Architecture","\u002Fgetting-started\u002Farchitecture","1.getting-started\u002F2.architecture",{"title":18,"path":19,"stem":20},"Prerequisites","\u002Fgetting-started\u002Fprerequisites","1.getting-started\u002F3.prerequisites",{"title":22,"path":23,"stem":24},"Quickstart","\u002Fgetting-started\u002Fquickstart","1.getting-started\u002F4.quickstart",false,{"title":27,"path":28,"stem":29,"children":30,"page":25},"Install","\u002Finstall","2.install",[31,35,39,43],{"title":32,"path":33,"stem":34},"Install the chart","\u002Finstall\u002Finstall-the-chart","2.install\u002F1.install-the-chart",{"title":36,"path":37,"stem":38},"Activation","\u002Finstall\u002Factivation","2.install\u002F2.activation",{"title":40,"path":41,"stem":42},"DNS and TLS","\u002Finstall\u002Fdns-and-tls","2.install\u002F3.dns-and-tls",{"title":44,"path":45,"stem":46},"Verify your install","\u002Finstall\u002Fverify-your-install","2.install\u002F4.verify-your-install",{"title":48,"path":49,"stem":50,"children":51,"page":25},"Configure","\u002Fconfigure","3.configure",[52,56,60,64],{"title":53,"path":54,"stem":55},"Data services","\u002Fconfigure\u002Fdata-services","3.configure\u002F1.data-services",{"title":57,"path":58,"stem":59},"Secrets","\u002Fconfigure\u002Fsecrets","3.configure\u002F2.secrets",{"title":61,"path":62,"stem":63},"Registry and air-gapped installs","\u002Fconfigure\u002Fregistry-and-airgap","3.configure\u002F3.registry-and-airgap",{"title":65,"path":66,"stem":67},"Integrations","\u002Fconfigure\u002Fintegrations","3.configure\u002F4.integrations",{"title":69,"path":70,"stem":71,"children":72,"page":25},"Licensing","\u002Flicensing","4.licensing",[73,77,81,85],{"title":74,"path":75,"stem":76},"How licensing works","\u002Flicensing\u002Fhow-licensing-works","4.licensing\u002F1.how-licensing-works",{"title":78,"path":79,"stem":80},"Your key never leaves","\u002Flicensing\u002Fyour-key-never-leaves","4.licensing\u002F2.your-key-never-leaves",{"title":82,"path":83,"stem":84},"What phones home","\u002Flicensing\u002Fwhat-phones-home","4.licensing\u002F3.what-phones-home",{"title":86,"path":87,"stem":88},"Revoke and recover","\u002Flicensing\u002Frevoke-and-recover","4.licensing\u002F4.revoke-and-recover",{"title":90,"path":91,"stem":92,"children":93,"page":25},"Operate","\u002Foperate","5.operate",[94,98,102,106],{"title":95,"path":96,"stem":97},"Upgrade","\u002Foperate\u002Fupgrade","5.operate\u002F1.upgrade",{"title":99,"path":100,"stem":101},"Backup and restore","\u002Foperate\u002Fbackup-and-restore","5.operate\u002F2.backup-and-restore",{"title":103,"path":104,"stem":105},"Observability","\u002Foperate\u002Fobservability","5.operate\u002F3.observability",{"title":107,"path":108,"stem":109},"High availability","\u002Foperate\u002Fhigh-availability","5.operate\u002F4.high-availability",{"title":111,"path":112,"stem":113,"children":114,"page":25},"Troubleshooting","\u002Ftroubleshooting","6.troubleshooting",[115,119],{"title":116,"path":117,"stem":118},"Install failures","\u002Ftroubleshooting\u002Finstall-failures","6.troubleshooting\u002F1.install-failures",{"title":120,"path":121,"stem":122},"Licensing failures","\u002Ftroubleshooting\u002Flicensing-failures","6.troubleshooting\u002F2.licensing-failures",{"title":124,"path":125,"stem":126,"children":127,"page":25},"Reference","\u002Freference","9.reference",[128,132],{"title":129,"path":130,"stem":131},"Helm values","\u002Freference\u002Fhelm-values","9.reference\u002F1.helm-values",{"title":133,"path":134,"stem":135},"Environment variables","\u002Freference\u002Fenvironment","9.reference\u002F2.environment",{"id":137,"title":53,"body":138,"description":752,"extension":753,"links":754,"meta":755,"navigation":756,"path":54,"seo":757,"stem":55,"__hash__":758},"docs\u002F3.configure\u002F1.data-services.md",{"type":139,"value":140,"toc":741},"minimark",[141,145,162,223,227,241,300,314,340,354,361,364,415,425,464,467,479,525,553,565,582,588,609,623,626,632,635,665,675,681,720,729,737],[142,143,53],"h1",{"id":144},"data-services",[146,147,148,149,153,154,157,158,161],"p",{},"The platform needs a Postgres database, a Redis cache, and (optionally) a Kafka broker. Each one is configured on its own, through ",[150,151,152],"code",{},"postgres.mode",", ",[150,155,156],{},"redis.mode",", and ",[150,159,160],{},"kafka.mode",". You can mix them freely: a managed Postgres alongside a bundled Redis is a normal shape.",[163,164,165,184],"table",{},[166,167,168],"thead",{},[169,170,171,175,178,181],"tr",{},[172,173,174],"th",{},"Mode",[172,176,177],{},"Postgres",[172,179,180],{},"Redis",[172,182,183],{},"Kafka",[185,186,187,209],"tbody",{},[169,188,189,193,199,204],{},[190,191,192],"td",{},"default",[190,194,195,198],{},[150,196,197],{},"operator",", CloudNativePG",[190,200,201],{},[150,202,203],{},"bundled",[190,205,206,208],{},[150,207,197],{},", Strimzi",[169,210,211,216,219,221],{},[190,212,213],{},[150,214,215],{},"external",[190,217,218],{},"yes",[190,220,218],{},[190,222,218],{},[224,225,177],"h2",{"id":226},"postgres",[146,228,229,232,233,236,237,240],{},[150,230,231],{},"postgres.mode=operator"," is the default. The chart renders a CloudNativePG ",[150,234,235],{},"Cluster",", and CNPG (a cluster-scoped prerequisite you install once, see ",[238,239,18],"a",{"href":19},") reconciles it into a running Postgres instance with real failover.",[242,243,248],"pre",{"className":244,"code":245,"language":246,"meta":247,"style":247},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","--set postgres.instances=3 \\\n--set postgres.storageSize=100Gi \\\n--set postgres.storageClass=fast-ssd \\\n--set postgres.imageName=ghcr.io\u002Fcloudnative-pg\u002Fpostgresql:18\n","bash","",[150,249,250,271,282,292],{"__ignoreMap":247},[251,252,255,259,263,267],"span",{"class":253,"line":254},"line",1,[251,256,258],{"class":257},"sBMFI","--set",[251,260,262],{"class":261},"sfazB"," postgres.instances=",[251,264,266],{"class":265},"sbssI","3",[251,268,270],{"class":269},"sTEyZ"," \\\n",[251,272,274,277,280],{"class":253,"line":273},2,[251,275,276],{"class":269},"--set ",[251,278,279],{"class":261},"postgres.storageSize=100Gi",[251,281,270],{"class":269},[251,283,285,287,290],{"class":253,"line":284},3,[251,286,276],{"class":269},[251,288,289],{"class":261},"postgres.storageClass=fast-ssd",[251,291,270],{"class":269},[251,293,295,297],{"class":253,"line":294},4,[251,296,276],{"class":269},[251,298,299],{"class":261},"postgres.imageName=ghcr.io\u002Fcloudnative-pg\u002Fpostgresql:18\n",[146,301,302,305,306,309,310,313],{},[150,303,304],{},"postgres.instances"," defaults to ",[150,307,308],{},"1",", which renders and reconciles fine but gives you no failover. Three instances survive losing a node. ",[150,311,312],{},"postgres.storageSize"," is per instance, so three instances cost three times this value. Kubernetes will not shrink a volume later and most storage classes make growing one a manual job, so size it for the year ahead.",[146,315,316,317,320,321,324,325,328,329,332,333,335,336,339],{},"CNPG normally invents its own database password on first reconcile, which is too late for this chart: the platform Secret's ",[150,318,319],{},"DB_DSN"," is composed at ",[150,322,323],{},"helm install"," time, before the operator has run. The chart goes the other way. It generates the passwords itself and pre-seeds CNPG with them through two small Secrets (",[150,326,327],{},"\u003Crelease>-platform-pg-app"," and ",[150,330,331],{},"\u003Crelease>-platform-pg-auth-role","), which the ",[150,334,235],{}," references for the application role and for ",[150,337,338],{},"supabase_auth_admin",". Nothing for you to configure here; it explains the two extra Secrets you will see in your namespace.",[146,341,342,343,345,346,349,350,353],{},"Service DNS, if you need to reach it directly: the CNPG ",[150,344,235],{}," is ",[150,347,348],{},"\u003Crelease>-platform-postgres",", with a read-write Service at ",[150,351,352],{},"\u003Crelease>-platform-postgres-rw:5432",".",[355,356,358],"h3",{"id":357},"postgresmodeexternal",[150,359,360],{},"postgres.mode=external",[146,362,363],{},"Point it at a managed Postgres instead, and you no longer need CNPG in the cluster at all:",[242,365,367],{"className":244,"code":366,"language":246,"meta":247,"style":247},"--set postgres.mode=external \\\n--set postgres.external.dsn='postgres:\u002F\u002Fsolvegio:pw@pg.acme.internal:5432\u002Fsolvegio?sslmode=require' \\\n--set postgres.external.authAdminPassword=\u003Csupabase_auth_admin-password>\n",[150,368,369,378,396],{"__ignoreMap":247},[251,370,371,373,376],{"class":253,"line":254},[251,372,258],{"class":257},[251,374,375],{"class":261}," postgres.mode=external",[251,377,270],{"class":269},[251,379,380,382,385,389,392,394],{"class":253,"line":273},[251,381,276],{"class":269},[251,383,384],{"class":261},"postgres.external.dsn=",[251,386,388],{"class":387},"sMK4o","'",[251,390,391],{"class":261},"postgres:\u002F\u002Fsolvegio:pw@pg.acme.internal:5432\u002Fsolvegio?sslmode=require",[251,393,388],{"class":387},[251,395,270],{"class":269},[251,397,398,400,403,406,409,412],{"class":253,"line":284},[251,399,276],{"class":269},[251,401,402],{"class":261},"postgres.external.authAdminPassword=",[251,404,405],{"class":387},"\u003C",[251,407,408],{"class":261},"supabase_auth_admin-passwor",[251,410,411],{"class":269},"d",[251,413,414],{"class":387},">\n",[146,416,417,420,421,424],{},[150,418,419],{},"postgres.external.dsn"," is passed through to the API untouched, so whatever you put in it, including ",[150,422,423],{},"sslmode=require",", is what the platform connects with.",[146,426,427,430,431,433,434,436,437,440,441,443,444,447,448,451,452,455,456,459,460,463],{},[150,428,429],{},"postgres.external.authAdminPassword"," is required whenever ",[150,432,360],{},", and it exists because of how GoTrue is built. GoTrue does not share the platform's own database role: it connects as ",[150,435,338],{}," and owns a separate ",[150,438,439],{},"auth"," schema. In operator mode the chart creates that role and schema for you as part of bringing up the CNPG ",[150,442,235],{},". On a managed Postgres the chart has no such hook, so you create the role and schema yourself, before you install, and hand the chart that role's password so it can compose GoTrue's connection string. ",[150,445,446],{},"backend\u002Fscripts\u002Ftenant-db-reset.sql"," in the platform repository is the reference for exactly what that role needs: the schema, ",[150,449,450],{},"USAGE"," on it, ",[150,453,454],{},"EXECUTE"," on its functions, ",[150,457,458],{},"BYPASSRLS",", and the default privileges that let the app role read ",[150,461,462],{},"auth.users",". Get the password wrong and the auth pods restart on a failed connect while the rest of the platform looks healthy.",[224,465,183],{"id":466},"kafka",[146,468,469,472,473,328,476,478],{},[150,470,471],{},"kafka.mode=operator"," is the default. The chart renders a Strimzi ",[150,474,475],{},"KafkaNodePool",[150,477,183],{},", and Strimzi (also a cluster-scoped prerequisite) reconciles them into a running cluster.",[242,480,482],{"className":244,"code":481,"language":246,"meta":247,"style":247},"--set kafka.replicas=3 \\\n--set kafka.storageSize=50Gi \\\n--set kafka.replicationFactor=3 \\\n--set kafka.minInsync=2\n",[150,483,484,495,504,515],{"__ignoreMap":247},[251,485,486,488,491,493],{"class":253,"line":254},[251,487,258],{"class":257},[251,489,490],{"class":261}," kafka.replicas=",[251,492,266],{"class":265},[251,494,270],{"class":269},[251,496,497,499,502],{"class":253,"line":273},[251,498,276],{"class":269},[251,500,501],{"class":261},"kafka.storageSize=50Gi",[251,503,270],{"class":269},[251,505,506,508,511,513],{"class":253,"line":284},[251,507,276],{"class":269},[251,509,510],{"class":261},"kafka.replicationFactor=",[251,512,266],{"class":265},[251,514,270],{"class":269},[251,516,517,519,522],{"class":253,"line":294},[251,518,276],{"class":269},[251,520,521],{"class":261},"kafka.minInsync=",[251,523,524],{"class":265},"2\n",[146,526,527,305,530,532,533,328,536,539,540,542,543,545,546,328,549,552],{},[150,528,529],{},"kafka.replicas",[150,531,308],{},", no redundancy. ",[150,534,535],{},"kafka.replicationFactor",[150,537,538],{},"kafka.minInsync"," also default to ",[150,541,308],{},"; set them alongside ",[150,544,529],{}," or a 3-broker cluster still writes with no redundancy at all. With ",[150,547,548],{},"replicationFactor=3",[150,550,551],{},"minInsync=2",", a write survives one broker going down and the cluster keeps accepting writes.",[146,554,555,558,559,561,562,564],{},[150,556,557],{},"kafka.version"," defaults to empty, and that is deliberate. Strimzi is a prerequisite you install yourself, so the chart does not control which version of it you are running, and each Strimzi release only accepts a narrow window of Kafka versions. Leaving ",[150,560,557],{}," empty lets Strimzi pick a version it supports. Pin it only if you have a reason to, and only to a version your installed Strimzi actually accepts; a version outside that window fails at reconcile, not at render, so you will not find out until the ",[150,563,183],{}," object refuses to converge. If you are not sure what your Strimzi supports, do not set this.",[146,566,567,568,345,570,573,574,577,578,581],{},"Service DNS: the Strimzi ",[150,569,183],{},[150,571,572],{},"\u003Crelease>-platform-kafka",", with a bootstrap Service at ",[150,575,576],{},"\u003Crelease>-platform-kafka-kafka-bootstrap:9092",". The doubled ",[150,579,580],{},"kafka-kafka"," is Strimzi's own naming convention, not a typo.",[355,583,585],{"id":584},"kafkamodeexternal",[150,586,587],{},"kafka.mode=external",[242,589,591],{"className":244,"code":590,"language":246,"meta":247,"style":247},"--set kafka.mode=external \\\n--set kafka.external.brokers=kafka-0.acme.internal:9092\n",[150,592,593,602],{"__ignoreMap":247},[251,594,595,597,600],{"class":253,"line":254},[251,596,258],{"class":257},[251,598,599],{"class":261}," kafka.mode=external",[251,601,270],{"class":269},[251,603,604,606],{"class":253,"line":273},[251,605,276],{"class":269},[251,607,608],{"class":261},"kafka.external.brokers=kafka-0.acme.internal:9092\n",[146,610,611,612,615,616,619,620,622],{},"You no longer need Strimzi in the cluster. ",[150,613,614],{},"kafka.external.brokers"," is a comma-separated list of ",[150,617,618],{},"host:port",". Leaving it empty while ",[150,621,587],{}," is a valid, deliberate configuration: it disables event streaming entirely, and with it the realtime stream and templated email. The rest of the platform runs normally without it.",[224,624,180],{"id":625},"redis",[146,627,628,631],{},[150,629,630],{},"redis.mode=bundled"," is the default, and Redis is the one data service the chart still runs itself: a single-node StatefulSet in your namespace. There is no operator involved, on purpose. Redis here is a cache, not durable state, so a cluster-scoped prerequisite just to run it is not worth adding. The chart generates the password and hands it to both the Redis pod and the connection string.",[146,633,634],{},"One node means no failover: if the pod goes away, the cache is unavailable until it is rescheduled. That is a reasonable trade for a cache and a bad one to lose sleep over, since nothing durable lives in it.",[242,636,638],{"className":244,"code":637,"language":246,"meta":247,"style":247},"--set redis.storage=10Gi \\\n--set redis.storageClass=fast-ssd \\\n--set redis.maxmemory=1gb\n",[150,639,640,649,658],{"__ignoreMap":247},[251,641,642,644,647],{"class":253,"line":254},[251,643,258],{"class":257},[251,645,646],{"class":261}," redis.storage=10Gi",[251,648,270],{"class":269},[251,650,651,653,656],{"class":253,"line":273},[251,652,276],{"class":269},[251,654,655],{"class":261},"redis.storageClass=fast-ssd",[251,657,270],{"class":269},[251,659,660,662],{"class":253,"line":284},[251,661,276],{"class":269},[251,663,664],{"class":261},"redis.maxmemory=1gb\n",[146,666,667,670,671,674],{},[150,668,669],{},"redis.maxmemory"," caps what Redis holds before it starts evicting the least recently used keys. Keep it below ",[150,672,673],{},"redis.resources.limits.memory",", or the pod gets OOM-killed instead of evicting.",[355,676,678],{"id":677},"redismodeexternal",[150,679,680],{},"redis.mode=external",[242,682,684],{"className":244,"code":683,"language":246,"meta":247,"style":247},"--set redis.mode=external \\\n--set redis.external.url=redis.acme.internal:6379 \\\n--set redis.external.password=\u003Credis-password>\n",[150,685,686,695,704],{"__ignoreMap":247},[251,687,688,690,693],{"class":253,"line":254},[251,689,258],{"class":257},[251,691,692],{"class":261}," redis.mode=external",[251,694,270],{"class":269},[251,696,697,699,702],{"class":253,"line":273},[251,698,276],{"class":269},[251,700,701],{"class":261},"redis.external.url=redis.acme.internal:6379",[251,703,270],{"class":269},[251,705,706,708,711,713,716,718],{"class":253,"line":284},[251,707,276],{"class":269},[251,709,710],{"class":261},"redis.external.password=",[251,712,405],{"class":387},[251,714,715],{"class":261},"redis-passwor",[251,717,411],{"class":269},[251,719,414],{"class":387},[146,721,722,725,726,728],{},[150,723,724],{},"redis.external.url"," is a bare ",[150,727,618],{},", no scheme in front of it. Point this at a managed cache (ElastiCache, Memorystore, Redis Enterprise, whatever you already run) if you need Redis to survive a node failure; that is the HA path for the cache, since there is no bundled-Redis clustering.",[146,730,731,732,734,735,353],{},"Every value on this page is listed with its default in the ",[238,733,129],{"href":130}," reference. Replica counts on the data layer are half of a highly available install; the other half is the platform's own workloads, covered in ",[238,736,107],{"href":108},[738,739,740],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":247,"searchDepth":254,"depth":273,"links":742},[743,746,749],{"id":226,"depth":273,"text":177,"children":744},[745],{"id":357,"depth":284,"text":360},{"id":466,"depth":273,"text":183,"children":747},[748],{"id":584,"depth":284,"text":587},{"id":625,"depth":273,"text":180,"children":750},[751],{"id":677,"depth":284,"text":680},"Postgres and Kafka run under an operator by default, or point them at a managed service. Redis is the one service the chart still runs itself.","md",null,{},true,{"title":53,"description":752},"a3ddN0QG4ZTVjLY6cput3saM47z_kaSYxYw9MHbNM3A",[760,762],{"title":44,"path":45,"stem":46,"description":761,"children":-1},"Four checks that tell you the release came up licensed and serving.",{"title":57,"path":58,"stem":59,"description":763,"children":-1},"What the chart generates, why an upgrade never rotates it, and how to bring your own Secret from SOPS, sealed-secrets or External Secrets Operator.",1783967741116]