Skip to content
Config Schema

Local Development

  • Docker and Docker Compose
  • Rust 1.91+
  • mise — task runner and tool version manager
  • direnv (optional) — auto-loads .envrc

Rise has two deployment backends, and either one can be your local dev environment — both are first-class and fully supported. Choose the backend that fits what you’re working on. Install the mise-managed tools once (both paths need them), then follow Path A (Kubernetes) or Path B (Docker):

Terminal window
# Install mise-managed tools (minikube, helm, kubectl, etc.) — both paths
mise install

The original backend. Pick it to work on the Kubernetes controller / Helm / metacontroller, or if you already run a cluster.

Terminal window
# One-stop dev setup. Cross-platform (Linux + macOS), idempotent, interactive.
# Configures /etc/hosts (sudo), Docker insecure registries, and brings up a
# local cluster (minikube by default; offers k3s on Linux).
mise setup
mise frontend:dev # Vite dev server
mise backend:run # alias: mise br — host backend against the K8s dev config

mise setup is a thin wrapper around ./scripts/dev-setup.sh. Run individual steps with mise setup <hosts|docker|minikube|k3s|preflight> (mise passes the subcommand through; or run ./scripts/dev-setup.sh <...> directly) if you only need one part. The script writes pod-reachable host URLs to .env; if you use direnv, run direnv allow once and .envrc will load them.

On macOS, Docker Desktop’s daemon config lives at ~/.docker/daemon.json and the script updates it there. The script offers to restart Docker Desktop for you so the new insecure-registries take effect.

Apps deploy to the cluster and are reachable at *.rise.local (resolved via the /etc/hosts + host-IP wiring mise setup provisions).

A lighter single-host setup with no cluster — great for backend/frontend feature work and for the Docker deployment backend itself.

Terminal window
# Adds ONLY the /etc/hosts aliases (incl. rise-dex → 127.0.0.1). No cluster,
# no minikube — the full `mise setup` is not needed for this path.
mise setup hosts
# Brings up the compose support services + runs Rise on the host with the
# Docker backend (reuses the env-driven config/docker.yaml; no Rise image build).
mise backend:run docker # alias: mise br docker
mise frontend:dev # optional, in another terminal

The registry uses localhost:5000 (insecure-by-default in Docker — no daemon.json change), and apps are reachable at *.rise.localhost (loopback per RFC 6761, so no /etc/hosts edits for app hosts). The only setup mise br docker needs is the rise-dex host alias from mise setup hosts. See Docker backend (local) below and the operator guide’s Local development section for the full reference.

One command runs the full stack; it takes an optional backend arg (k8s is the default):

Terminal window
mise dev # == mise dev k8s — Kubernetes backend (services + frontend + backend)
mise dev docker # Docker backend (support services + frontend + host server, no image build)

This single command:

  1. Checks prerequisites — verifies /etc/hosts entries, Docker daemon, and (for the Kubernetes backend) Kubernetes connectivity. If anything is missing it lists the issues and asks whether to continue anyway. (The cluster checks only apply to mise dev / mise dev k8s; mise dev docker skips them.)
  2. Starts Docker Compose services — PostgreSQL, Dex (OIDC), container registry (plus Traefik on the Docker path).
  3. Runs database migrations.
  4. Starts the Vite frontend dev server (background).
  5. Starts the backend server against the chosen backend.

To run just the host server without the frontend, use mise br (Kubernetes) or mise br docker (Docker) — see Running Components Individually and Docker backend (local).

Services are then available at:

ServiceURL
Backend API + Web UIhttp://rise.local:3000
PostgreSQLlocalhost:5432
Container registrylocalhost:5000
Registry UIhttp://localhost:5001
Kubernetes ingress (HTTP)localhost:8080
Kubernetes ingress (HTTPS)localhost:8443
Terminal window
mise backend:run # (alias: mise br) — K8s backend: starts deps + migrations + backend
mise backend:run docker # (alias: mise br docker) — Docker backend on the host (no image build)
mise frontend:dev # Vite dev server only

mise backend:run (mise br) takes the same optional backend arg as mise dev: omitted or k8s runs the Kubernetes path, docker runs the Docker path.

TaskPurpose
setupOne-stop dev setup: hosts + docker + cluster (interactive)
downUndo what setup did (stops the hosts watcher, kills port-forward, deletes cluster, strips .env block + daemon.json registries + /etc/hosts block)

Everything is driven by ./scripts/dev-setup.sh. mise setup and mise down are convenience wrappers; positional args pass through, so any script subcommand works as mise setup <subcmd>:

InvocationWhat it does
mise setup hostsRewrite the managed /etc/hosts block (base hosts + *.rise.local ingress hosts enumerated from the current cluster)
mise setup hosts-clearRemove the managed /etc/hosts block
mise setup hosts-watchStart a background watcher that keeps the managed /etc/hosts block synced to ingress changes (event-driven; idempotent — won’t double-spawn). mise setup offers this as an opt-in prompt when a cluster is reachable
mise setup hosts-watch-downStop the /etc/hosts watcher
mise setup dockerConfigure Docker insecure-registries (asks to restart Docker Desktop on macOS)
mise setup docker-clearRemove rise registries from daemon.json
mise setup minikubeBring up Minikube + ingress/Loki port-forwards + Helm install (preferred on most dev machines)
mise setup minikube-downminikube delete
mise setup k3sBring up k3s (Linux only — use when Minikube doesn’t work, or in ephemeral/dedicated Rise dev environments)
mise setup k3s-downUninstall k3s
mise setup pfStart the ingress port-forward (localhost:8080 + :8443) in the background
mise setup pf-downStop the ingress port-forward
mise setup loki-pfStart the Loki port-forward (localhost:3100) in the background
mise setup loki-pf-downStop the Loki port-forward
mise setup preflighthosts + docker only, no cluster

Minikube vs K3s: Minikube is preferred on most developer machines — it runs a single-node Kubernetes cluster inside a Docker container, starts quickly, and integrates well with the local Docker network so pods can reach rise-registry:5000. Use k3s (Linux only) when Minikube doesn’t work on your machine (e.g., nested virtualization issues) or when setting up an ephemeral or dedicated environment solely for Rise development where the slight extra k3s overhead doesn’t matter.

TaskPurpose
dev [k8s|docker]Full dev stack (preflight prompt + services + frontend + backend). Defaults to k8s; dev docker runs the Docker backend
backend:run [k8s|docker] (alias br)Host server only. Defaults to k8s (starts deps + migrates); br docker runs the Docker backend (support services + Rise on the host; no image build, no cluster)
frontend:devVite frontend dev server
db:migrateRun database migrations
db:nukeDrop and recreate the database
docs:serveServe bundled user docs with Starlight (port 3001)
docs:engineering:serveServe engineering docs with Starlight (port 3002)
TaskPurpose
lintclippy + fmt check + sqlx check + helm lint
sqlx:prepareRegenerate SQLX offline query cache
sqlx:checkVerify SQLX queries are valid
config:schema:generate / checkBackend settings JSON schema
crd:generate / checkCRD YAML from Rust definition

Backend — edit code, then restart with mise backend:run.

Frontend — Vite hot-reloads automatically. The backend proxies frontend routes to http://localhost:5173 when server.frontend_dev_proxy_url is configured.

CLI:

Terminal window
cargo build --bin rise
rise <command>

Database schema:

Terminal window
sqlx migrate add <migration_name>
# Edit the new migration in migrations/
sqlx migrate run
cargo sqlx prepare # update offline query cache, commit the result

The local setup uses two registry URLs:

  • rise-registry:5000 — used by deployment controllers (inside Docker/Kubernetes networks)
  • localhost:5000 — used by the CLI on the host for push operations

This is configured in config/development.yaml:

registry:
type: "oci-client-auth"
registry_url: "rise-registry:5000"
namespace: "rise-apps/"
client_registry_url: "localhost:5000"

.envrc (loaded by direnv) sets: DATABASE_URL, RISE_CONFIG_RUN_MODE, RISE_MANAGED_BUILDKIT_*, and PATH.

Server configuration lives in config/development.yaml.

ServiceCredentials
PostgreSQLpostgres://rise:rise123@localhost:5432/rise
Dex (OIDC)admin@example.com, dev@example.com, user@example.com — password: password
Host Machine (127.0.0.1)
├── rise.local:3000 → Rise Backend
├── localhost:5173 → Vite dev server
├── localhost:8080/8443 → K8s ingress (port-forward or hostPort)
├── localhost:3100 → Loki (port-forward)
├── Docker network: rise_default
│ ├── rise-postgres (5432)
│ ├── rise-dex (5556)
│ ├── rise-registry (5000)
│ ├── rise-buildkit (managed, joins this network)
│ └── minikube node (connected to this network)
└── Kubernetes cluster
└── Pods pull from rise-registry:5000 via network connectivity
  • BuildKit connects to the rise_default Docker network (via RISE_MANAGED_BUILDKIT_NETWORK_NAME) so it can push to rise-registry:5000.
  • Minikube joins the same network so pods can pull images.
  • The local cluster setup writes .env values such as RISE_AUTH_BACKEND_URL and RISE_K8S_HOST_IP so Kubernetes pods can reach the host backend.
  • Runtime logs use the local Loki backend by default. mise setup installs the dev Loki/Alloy chart components and forwards Loki to localhost:3100, which config/development.yaml reads through RISE_LOKI_URL.

The Docker backend deploys to a single Docker host with Traefik for routing — the lightest local setup, with no cluster. Run it on the host with no Rise image build:

Terminal window
mise setup hosts # one-time: adds /etc/hosts aliases (incl. rise-dex)
mise backend:run docker # alias: mise br docker

mise br docker brings up only the compose support services (Postgres, Dex, registry, Traefik — no Rise container) and runs Rise on the host against the env-driven config/docker.yaml (run_mode docker). It is the Docker backend of the unified mise br [k8s|docker] task (k8s is the default, against the Kubernetes dev config). You no longer hand-edit config/development.yaml to switch controllers — the task selects the Docker controller via run_mode. See the operator guide’s Local development section for the full reference (gateway-IP / extra_hosts wiring, Docker Desktop notes, etc.).

How it works:

  • The backend runs an in-process reconcile loop (replacing Metacontroller): it enumerates projects, drives deployment state transitions, diffs the desired state against the actual Rise-labelled containers, creates/recreates/garbage-collects them, and HTTP-probes for health — all on the shared rise_default network. There is no webhook listener and no cluster.
  • replicas=1 caveat: the Docker runtime clamps every deployment to a single container; horizontal scaling is a follow-up.
  • Env vars are passed as plain KEY=VALUE and are therefore visible via docker inspect <container> (Docker has no secret primitive) — acceptable for the single-host scope.
  • Apps are reachable at http://<project>.rise.localhost (and the <group>--<project> / <env>--<project> variants) with no DNS or /etc/hosts edits, since *.localhost resolves to 127.0.0.1.

http: server gave HTTP response to HTTPS client — insecure registries not configured. Run mise setup docker (or ./scripts/dev-setup.sh docker directly).

BuildKit can’t push to registry — verify RISE_MANAGED_BUILDKIT_NETWORK_NAME=rise_default is set in your environment (should be in .envrc).

OAuth redirects fail — ensure rise.local is in /etc/hosts (re-run mise setup hosts to add it).

Minikube pods ImagePullBackOff — verify registry access from inside Minikube:

Terminal window
minikube ssh -- curl http://rise-registry:5000/v2/
# Should return: {}

If it fails, re-run mise setup minikube.

Reset everything:

Terminal window
docker compose down -v
cargo clean
mise install
mise dev
5432/rise
docker compose exec postgres psql -U rise -d rise