Local Configuration & Setup
This guide will walk you through the steps to get the Single-Repo Data Platform (SRDP) running on your local machine. There are two options:
- Docker Compose: simplest, no Kubernetes needed, good for trying out the stack locally.
- Kubernetes (Helm): closer to the production setup, requires a local cluster.
Both options require mkcert for local TLS certificates.
The *.srdp.localhost domains resolve to your own machine by themselves, so no hosts-file entries are needed.
Option A: Docker Compose
1) Clone the repo
git clone git@github.com:srdp-hub/srdp.git # or git clone https://github.com/srdp-hub/srdp.git
cd srdp
2) Install the local CA and generate TLS certificates
The stack serves everything over HTTPS because Zitadel and OAuth2-Proxy require it. mkcert creates locally-trusted certificates so your browser won't show warnings.
Install mkcert following its own instructions, then run:
just docker-tls # trusts mkcert's local CA and generates certs in deploy/docker/certs/
3) Create the environment file
cp deploy/docker/.env.example deploy/docker/.env
Fill in the empty values in deploy/docker/.env, which are required, and the comment above each one says how to generate it.
You will need to update OIDC_CLIENT_ID and OIDC_CLIENT_SECRET after you create the OIDC application in Zitadel; see First boot: create the Zitadel OIDC application below.
4) Start the stack
just docker-up
This builds the local images and starts every service defined in deploy/docker/docker-compose.yml.
The first run takes a few minutes while images are pulled and built.
Quarto is disabled by default, its base image bundles a full Pandoc/TinyTeX/Deno toolchain sized for scientific publishing, heavy for a single static page with no current use. Its source stays at services/quarto/, wire it back into deploy/docker/docker-compose.yml and config/traefik/traefik.yml when it's needed again.
To stop the stack:
just docker-down
Warning: Do not run
docker compose down -vunless you want to destroy all persistent data, including your Zitadel configuration.
Option B: Kubernetes (Helm)
1) Clone the repo
git clone git@github.com:srdp-hub/srdp.git # or git clone https://github.com/srdp-hub/srdp.git
cd srdp
2) Reach the cluster
The chart uses *.srdp.localhost by default. On the local kind cluster this repo is set up for, Traefik's ports are mapped to 127.0.0.1:18080/127.0.0.1:18443 (see deploy/kubernetes/kind-config.yaml), so 127.0.0.1 is always the right host, but every URL needs the :18443 (or :18080 for plain HTTP) suffix. Those aren't 80/443 on purpose: Docker Compose already publishes 80, 443, and 8080 on the host (deploy/docker/docker-compose.yml), and both stacks are meant to run side by side without tearing one down to use the other. On any other cluster, point these hostnames at whatever IP you use to reach Traefik: a LoadBalancer's external IP once it's up, or a port-forward's 127.0.0.1 if it isn't reachable directly (see docs/06-troubleshooting.md).
3) Install the local CA, create the kind cluster, and generate TLS certificates
Install mkcert and kind following their own instructions, then run:
just local-tls # trusts mkcert's local CA, creates the kind cluster (if needed), certs, and the k8s TLS secret
just local-tls depends on kind-up, so it creates the srdp kind cluster on first run and points kubectl at it; on later runs it just reuses the existing cluster. kind runs as plain containers on whatever Docker daemon you already have (Colima, OrbStack, native Docker Engine on Linux, your own preference, this repo doesn't assume one), no separate VM for Kubernetes itself.
4) Build local container images
The Helm chart references the application images that the kind-load-images recipe builds. just local-deploy (step 6) builds and loads them into the kind cluster automatically via the kind-load-images recipe, kind nodes don't share the host's image store, so pullPolicy: Never needs its own copy loaded with kind load docker-image, not just a local docker build. Run it standalone if you want to rebuild without a full redeploy:
just kind-load-images
5) Fill in secrets and local values
The localSecrets block in deploy/kubernetes/srdp-chart/values-local.yaml holds throwaway development values, so the local cluster runs without extra setup.
The chart turns it into the Secrets every service reads (templates/local-secrets.yaml), and values.yaml itself holds no credentials.
Never reuse those values outside your own machine, and keep real secrets out of tracked files (#61).
Keep custom-ingress-cert (created above), or point to another TLS secret if you prefer.
6) Install the chart locally
just local-deploy
This builds and loads the images (step 4), installs the chart, then reads back Traefik's actual (dynamically-assigned) ClusterIP and feeds it into oauth2-proxy's pod-level host alias in a second pass, since that IP can't be known ahead of the first install. Re-run the same command to pick up updated values.
The chart deploys the full stack: Traefik, PostgreSQL (in-cluster via Bitnami Helm chart), Zitadel, OAuth2-Proxy, Dagster (webserver + daemon + user code), Marimo, Streamlit, the API, DuckDB UI, Marquez, and the hub landing page.
Quarto is disabled by default (quarto.enabled: false in values.yaml), flip it back on when it's needed again.
PostgreSQL hosts the zitadel, dagster, marquez, and ducklake databases.
The Bitnami subchart's auth.* fields create zitadel, and the srdp-setup Job creates the rest from the setup.databases list in values.yaml on every install and upgrade.
First boot: create the Zitadel OIDC application
OAuth2-Proxy needs an OIDC client registered in Zitadel. Zitadel creates its first instance and admin user automatically on first start, but the OIDC application is created manually through the Zitadel console. Do this once, after the stack is up, for either deployment option.
1) Sign in to the Zitadel console
Open https://auth.srdp.localhost (Docker Compose) or https://auth.srdp.localhost:18443 (the local kind cluster, see step 2 of Option B) and sign in as the first-instance admin. Zitadel derives the default admin login name from the configured ExternalDomain, so for the local stack it is:
- Login name:
zitadel-admin@zitadel.auth.srdp.localhost - Password: for the Kubernetes chart, the value of
localSecrets.zitadel.adminPasswordinvalues-local.yaml. The Docker Compose stack requiresZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDto be set indeploy/docker/.env(see.env.example). Zitadel's own complexity rule applies, so the password needs an uppercase letter, a lowercase letter, a digit, and a symbol.
If the login name differs, check it under Users in the Zitadel console.
2) Create the OIDC application
- Create (or open) a project, then add an application of type Web.
- Use the Code authentication flow (client ID + secret).
- Add a redirect URI
https://<host>/oauth2/callbackfor each host that OAuth2-Proxy protects. The host list is theoauth2-proxyrouter rule inconfig/traefik/traefik.yml. Docker Compose uses no port suffix, and the localkindcluster needs:18443on every URL (see step 2 of Option B).
Zitadel then shows a Client ID and Client Secret.
3) Apply the credentials
- Docker Compose: set
OIDC_CLIENT_IDandOIDC_CLIENT_SECRETindeploy/docker/.env, then runjust docker-upto recreate OAuth2-Proxy with the new values. - Kubernetes: set
localSecrets.oauth2Proxy.clientIDandlocalSecrets.oauth2Proxy.clientSecretinvalues-local.yaml, then runjust local-deploy.
Congratulations! The local environment should now be up and running. Proceed to the next section, Usage & Verification, to confirm that everything is working correctly.