prole/supabase/helm/knoe-supabase
chrisfu 903f84f200 feat: ship Junie #3 (image rename) + Phase 2 OIDC GKE deploy + k3d chrisfu seed
Three independent lines of work landing in one commit because they were all
on disk together end-of-session and the cross-file edits (TODO, knoe-system,
junie/README) interleave cleanly:

1. **Queue #3 — image rename `knoe-authority` → `knoe-auth`** (Junie).
   Closes drift R6. The Maven artifact has been `knoe-auth.jar` since
   commit b355855; the deploy manifests now match.
     authority/Dockerfile.app                                         (NEW)
     deploy/gcp/gke/knoe-auth-deployment.yaml                         (3 image tags renamed)
     deploy/opentofu/k3s/manifests/knoe/knoe-auth-deployment.yaml     (2 image tags renamed)
     Makefile                                                         (docker-build-auth +
                                                                       docker-push-auth + REGISTRY/KNOE_AUTH_VERSION
                                                                       defaults)
     docs/plans/junie/03-image-rename-knoe-authority-to-knoe-auth.md  (brief, kept as design record)

2. **Phase 2 OIDC provider — GKE deploy** (Junie). Source landed via the
   merge that brought claude/crazy-bose-fec256 back; the k3d sandbox
   shipped earlier today (commit 93157b0). This commit completes the GKE
   path: Kong route `/auth` → knoe-auth, default values added, signing
   key wired via `knoe-auth-oidc` secret per the brief.
     supabase/helm/knoe-supabase/templates/kong/config.yaml           (Kong route /auth, gated
                                                                       on .Values.knoeAuth.enabled)
     supabase/helm/knoe-supabase/values.yaml                          (knoeAuth.enabled=false default;
                                                                       studioIngress block — also
                                                                       fixes the helm lint issue noted
                                                                       in earlier commit 03bb731)
     docs/plans/junie/phase2-oidc-gke-deploy.md                       (brief, kept as design record)

3. **k3d dev-user seed: `chrisfu` + `knoe_developer`** (Claude). Closes
   "I want auth as chrisfu@knoey.com to my local cnpg database" for the
   local dev loop. `etc/init_knoe_auth.sh --mode k3d` now runs
   `seed_dev_users_k3d()` after the schema bootstrap, creating:
     - `knoe_developer` group role with R/W on `knoe`+`public` (mirrors
       the GKE production layout from docs/db-access.md; was hand-rolled
       in production per the 2026-04-30 onboarding work, never baked into
       postInitTemplateSQL).
     - `chrisfu` LOGIN role with password `chrisfu-dev` (idempotent —
       resets on every `make k3d-knoe-up`, so the rebuild loop is
       deterministic). Granted into `knoe_developer`.
   From the host with port-forward up:
     PGPASSWORD=chrisfu-dev psql "postgresql://chrisfu@localhost:5432/knoe-db?sslmode=require"

Cross-cutting doc updates:
  docs/TODO.md            — Phase 2 GKE entry removed from "In progress"
                            (now "(none — all items shipped or paused)");
                            queue #3 + reality table R6 removed; pg_oauth
                            paused note flipped to "OIDC issuer now
                            deployed to GKE — pg_oauth can resume"; Done
                            section gets new entries for #3, Phase 2 GKE,
                            and chrisfu seed.
  docs/knoe-system.md     — Phase 2 GKE row → Shipped; pg_oauth → Ready;
                            knoe-auth-deployment.yaml note "knoe-auth:latest"
                            instead of "knoe-authority:latest"; "One-time
                            cluster setup" mentions the chrisfu seed.
  docs/local-dev-knoe-auth.md — "Verify psql connectivity" rewritten to
                            connect as chrisfu (was `knoe`); new "Reset /
                            rebuild loop" section explaining idempotency.
  docs/plans/junie/README.md — two new rows under Shipped: brief 03 and
                            phase2-oidc-gke-deploy.

Verification (the bits I ran locally):
  - bash -n etc/init_knoe_auth.sh                                     OK
  - The seed SQL is idempotent (DO blocks with EXISTS checks +
    ALTER ROLE on the password reset path)
  - The Kong /auth route is gated on .Values.knoeAuth.enabled — default
    false, no behavior change until someone flips it on per Junie's brief

Out of scope for this commit:
  - Actually flipping knoeAuth.enabled=true on the live GKE chart and
    rolling out — Junie's brief covers the runbook; needs the
    knoe-auth-oidc K8s secret populated from 1Password first
  - Rebuilding + pushing the new knoe-auth:latest image — `make
    docker-push-auth` is wired, just hasn't been run yet
  - Round 1.5 OpenBao transit-key encryption (still queued)

Co-authored-by: Junie <junie@jetbrains.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:07:40 -07:00
..
ci fix(cnpg,kong): wire cnpg-backup-sa, migrate PodMonitor, drop DASHBOARD consumer 2026-05-02 03:08:34 -07:00
docker stabilize unattended GKE Supabase/CNPG deployment 2026-04-10 15:53:58 -07:00
scripts stabilize unattended GKE Supabase/CNPG deployment 2026-04-10 15:53:58 -07:00
templates feat: ship Junie #3 (image rename) + Phase 2 OIDC GKE deploy + k3d chrisfu seed 2026-05-02 13:07:40 -07:00
.helmignore stabilize unattended GKE Supabase/CNPG deployment 2026-04-10 15:53:58 -07:00
Chart.yaml stabilize unattended GKE Supabase/CNPG deployment 2026-04-10 15:53:58 -07:00
README.md stabilize unattended GKE Supabase/CNPG deployment 2026-04-10 15:53:58 -07:00
values-gke.yaml feat(oauth): add Google OIDC/OAuth2 Proxy support for Studio and Grafana. Add Google OIDC configuration for Grafana in gke.cfg, introduce oauth2-proxy templates and configurations for Supabase Studio, update Helm values/templates for authenticated Studio access, and add ingress/service manifests for Postgres TCP and Prole services. 2026-04-28 11:27:34 -07:00
values.yaml feat: ship Junie #3 (image rename) + Phase 2 OIDC GKE deploy + k3d chrisfu seed 2026-05-02 13:07:40 -07:00

Supabase for Kubernetes with Helm

This directory contains the configurations and scripts required to run Supabase inside a Kubernetes cluster.

Disclamer

We use supabase/postgres to create and manage the Postgres database. This permit you to use replication if needed but you'll have to use the Postgres image provided Supabase or build your own on top of it. You can also choose to use other databases provider like StackGres or Postgres Operator.

For the moment we are using a root container to permit the installation of the missing pgjwt and wal2json extension inside the initdbScripts. This is considered a security issue, but you can use your own Postgres image instead with the extension already installed to prevent this. We provide an example of Dockerfilefor this purpose, you can use ours or build and host it on your own.

The database configuration we provide is an example using only one master. If you want to go to production, we highly recommend you to use a replicated database.

Usage example

For this section we're using Minikube and Docker to create a Kubernetes cluster

  1. Create a cluster with Minikube:

    minikube start --driver=docker
    minikube addons enable ingress
    echo "$(minikube ip)     supabase.local" | sudo tee -a /etc/hosts > /dev/null
    
  2. Add the Supabase Helm repository:

    helm repo add supabase https://supabase-community.github.io/supabase-kubernetes
    
  3. Install Supabase:

    helm install demo supabase/supabase
    
  4. The first deployment can take some time to complete (especially auth service). You can view the status of the pods using:

    kubectl get pod -l app.kubernetes.io/instance=demo
    
    NAME                                      READY   STATUS    RESTARTS      AGE
    demo-supabase-analytics-xxxxxxxxxx-xxxxx  1/1     Running   0             47s
    demo-supabase-auth-xxxxxxxxxx-xxxxx       1/1     Running   0             47s
    demo-supabase-db-0-xxxxxxxxxx-xxxxx       1/1     Running   0             47s
    demo-supabase-functions-xxxxxxxxxx-xxxxx  1/1     Running   0             47s
    demo-supabase-imgproxy-xxxxxxxxxx-xxxxx   1/1     Running   0             47s
    demo-supabase-kong-xxxxxxxxxx-xxxxx       1/1     Running   0             47s
    demo-supabase-meta-xxxxxxxxxx-xxxxx       1/1     Running   0             47s
    demo-supabase-realtime-xxxxxxxxxx-xxxxx   1/1     Running   0             47s
    demo-supabase-rest-xxxxxxxxxx-xxxxx       1/1     Running   0             47s
    demo-supabase-storage-xxxxxxxxxx-xxxxx    1/1     Running   0             47s
    demo-supabase-studio-xxxxxxxxxx-xxxxx     1/1     Running   0             47s
    demo-supabase-vector-xxxxxxxxxx-xxxxx     1/1     Running   0             47s
    
  5. Open Supabase Studio in your browser: http://supabase.local

    Use the default credentials below (for local development only):

    • Username: supabase
    • Password: this_password_is_insecure_and_should_be_updated
  6. Uninstall Supabase example:

    helm uninstall demo
    minikube delete
    sudo sed -i '/[[:space:]]supabase\.local$/d' /etc/hosts
    

Customize

You should consider to adjust the following values in values.yaml:

  • RELEASE_NAME: Name used for helm release
  • STUDIO.EXAMPLE.COM URL to Studio

If you want to use mail, consider to adjust the following values in values.yaml:

  • SMTP_ADMIN_MAIL
  • SMTP_HOST
  • SMTP_PORT
  • SMTP_SENDER_NAME

JWT Secret

We encourage you to use your own JWT keys by generating a new Kubernetes secret and reference it in values.yaml:

secret:
  jwt:
    anonKey: <new-anon-jwt>
    serviceKey: <new-service-role-jwt>
    secret: <jwt-secret>

32 characters long secret can be generated with openssl rand 64 | base64 You can use the JWT Tool to generate anon and service keys.

SMTP Secret

Connection credentials for the SMTP mail server will also be provided via Kubernetes secret referenced in values.yaml:

secret:
  smtp:
    username: <your-smtp-username>
    password: <your-smtp-password>

DB Secret

DB credentials will also be stored in a Kubernetes secret and referenced in values.yaml:

secret:
  db:
    password: <db-password>
    database: <supabase-database-name>

The secret can be created with kubectl via command-line:

If you depend on database providers like StackGres, Postgres Operator or self-hosted Postgres instance, fill in the secret above and modify any relevant Postgres attributes such as port or hostname (e.g. PGPORT, DB_HOST) for any relevant deployments. Refer to values.yaml for more details.

Dashboard secret

By default, a username and password is required to access the Supabase Studio dashboard. Simply change them at:

secret:
  dashboard:
    username: supabase
    password: this_password_is_insecure_and_should_be_updated

Analytics secret

A new logflare secret API key is required for securing communication between all of the Supabase services. To set the secret, generate a new 32 characters long secret similar to the step above.

secret:
  analytics:
    publicAccessToken: "your-super-secret-and-long-logflare-key-public"
    privateAccessToken: "your-super-secret-and-long-logflare-key-private"

S3 secret

Supabase storage supports the use of S3 object-storage. To enable S3 for Supabase storage:

  1. Set S3 key ID and access key:
 secret:
  s3:
    keyId: your-s3-key-id
    accessKey: your-s3-access-key
  1. Set storage S3 environment variables:
storage:
  environment:
    # Set S3 endpoint if using external object-storage
    # GLOBAL_S3_ENDPOINT: http://minio:9000
    STORAGE_BACKEND: s3
    GLOBAL_S3_PROTOCOL: http
    GLOBAL_S3_FORCE_PATH_STYLE: true
    AWS_DEFAULT_REGION: stub
  1. (Optional) Enable internal minio deployment
minio:
  enabled: true

How to use in Production

We didn't provide a complete configuration to go production because of the multiple possibility.

But here are the important points you have to think about:

  • Use a replicated version of the Postgres database.
  • Add SSL to the Postgres database.
  • Add SSL configuration to the ingresses endpoints using either the cert-manager or a LoadBalancer provider.
  • Change the domain used in the ingresses endpoints.
  • Generate a new secure JWT Secret.

Migration

Migration from local development is made easy by adding migration scripts at db.config field. This will apply all of the migration scripts during the database initialization. For example:

db:
  config:
    20230101000000_profiles.sql: |
      create table profiles (
        id uuid references auth.users not null,
        updated_at timestamp with time zone,
        username text unique,
        avatar_url text,
        website text,

        primary key (id),
        unique(username),
        constraint username_length check (char_length(username) >= 3)
      );      

To make copying scripts easier, use this handy bash script:

#!/bin/bash

for file in $1/*; do
  clipboard+="    $(basename $file): |\n"
  clipboard+=$(cat $file | awk '{print "      "$0}')
  clipboard+="\n"
done

echo -e "$clipboard"

and pipe it to your system clipboard handler:

# Using xclip as an example
./script.sh supabase/migrations | xclip -sel clipboard

Troubleshooting

Ingress Controller and Ingress Class

Depending on your Kubernetes version you might want to fill the className property instead of the kubernetes.io/ingress.class annotations. For example:

kong:
  ingress:
    enabled: 'true'
    className: "nginx"
    annotations:
      nginx.ingress.kubernetes.io/rewrite-target: /

Testing suite

Before creating a merge request, you can test the charts locally by using helm/chart-testing. If you have Docker and a Kubernetes environment to test with, simply run:

# Run chart-testing (lint)
docker run -it \
  --workdir=/data \
  --volume $(pwd)/charts/supabase:/data \
  quay.io/helmpack/chart-testing:v3.7.1 \
  ct lint --validate-maintainers=false --chart-dirs . --charts .
# Run chart-testing (install)
docker run -it \
  --network host \
  --workdir=/data \
  --volume ~/.kube/config:/root/.kube/config:ro \
  --volume $(pwd)/charts/supabase:/data \
  quay.io/helmpack/chart-testing:v3.7.1 \
  ct install --chart-dirs . --charts .

Version compatibility

0.0.x to 0.1.x

  • supabase/postgres is updated from 14.1 to 15.1, which warrants backing up all your data before proceeding to update to the next major version.
  • Intialization scripts for supabase/postgres has been reworked and matched closely to the Docker Compose version. Further tweaks to the scripts are needed to ensure backward-compatibility.
  • Migration scripts are now exposed at db.config, which will be mounted at /docker-entrypoint-initdb.d/migrations/. Simply copy your migration files from your local project's supabase/migration and populate the db.config.
  • Ingress are now limited to kong & db services. This is by design to limit entry to the stack through secure kong service.
  • kong.yaml has been modified to follow Docker kong.yaml template.
  • supabase/storage does not comes with pre-populated /var/lib/storage, therefore an emptyDir will be created if persistence is disabled. This might be incompatible with previous version if the persistent storage location is set to location other than specified above.
  • supabase/vector requires read access to the /var/log/pods directory. When run in a Kubernetes cluster this can be provided with a hostPath volume.