Local Development
Prerequisites
Section titled “Prerequisites”- Docker and Docker Compose
- Rust 1.91+
- mise — task runner and tool version manager
- direnv (optional) — auto-loads
.envrc
First-Time Setup
Section titled “First-Time Setup”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):
# Install mise-managed tools (minikube, helm, kubectl, etc.) — both pathsmise installPath A — Kubernetes backend
Section titled “Path A — Kubernetes backend”The original backend. Pick it to work on the Kubernetes controller / Helm / metacontroller, or if you already run a cluster.
# 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 servermise backend:run # alias: mise br — host backend against the K8s dev configmise 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).
Path B — Docker backend
Section titled “Path B — Docker backend”A lighter single-host setup with no cluster — great for backend/frontend feature work and for the Docker deployment backend itself.
# 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 terminalThe 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.
Day-to-Day
Section titled “Day-to-Day”One command runs the full stack; it takes an optional backend arg (k8s is the
default):
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:
- Checks prerequisites — verifies
/etc/hostsentries, 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 tomise dev/mise dev k8s;mise dev dockerskips them.) - Starts Docker Compose services — PostgreSQL, Dex (OIDC), container registry (plus Traefik on the Docker path).
- Runs database migrations.
- Starts the Vite frontend dev server (background).
- 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:
| Service | URL |
|---|---|
| Backend API + Web UI | http://rise.local:3000 |
| PostgreSQL | localhost:5432 |
| Container registry | localhost:5000 |
| Registry UI | http://localhost:5001 |
| Kubernetes ingress (HTTP) | localhost:8080 |
| Kubernetes ingress (HTTPS) | localhost:8443 |
Running Components Individually
Section titled “Running Components Individually”mise backend:run # (alias: mise br) — K8s backend: starts deps + migrations + backendmise backend:run docker # (alias: mise br docker) — Docker backend on the host (no image build)mise frontend:dev # Vite dev server onlymise 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.
Mise Tasks Reference
Section titled “Mise Tasks Reference”Setup (one-time, idempotent)
Section titled “Setup (one-time, idempotent)”| Task | Purpose |
|---|---|
setup | One-stop dev setup: hosts + docker + cluster (interactive) |
down | Undo 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>:
| Invocation | What it does |
|---|---|
mise setup hosts | Rewrite the managed /etc/hosts block (base hosts + *.rise.local ingress hosts enumerated from the current cluster) |
mise setup hosts-clear | Remove the managed /etc/hosts block |
mise setup hosts-watch | Start 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-down | Stop the /etc/hosts watcher |
mise setup docker | Configure Docker insecure-registries (asks to restart Docker Desktop on macOS) |
mise setup docker-clear | Remove rise registries from daemon.json |
mise setup minikube | Bring up Minikube + ingress/Loki port-forwards + Helm install (preferred on most dev machines) |
mise setup minikube-down | minikube delete |
mise setup k3s | Bring up k3s (Linux only — use when Minikube doesn’t work, or in ephemeral/dedicated Rise dev environments) |
mise setup k3s-down | Uninstall k3s |
mise setup pf | Start the ingress port-forward (localhost:8080 + :8443) in the background |
mise setup pf-down | Stop the ingress port-forward |
mise setup loki-pf | Start the Loki port-forward (localhost:3100) in the background |
mise setup loki-pf-down | Stop the Loki port-forward |
mise setup preflight | hosts + 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.
Development
Section titled “Development”| Task | Purpose |
|---|---|
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:dev | Vite frontend dev server |
db:migrate | Run database migrations |
db:nuke | Drop and recreate the database |
docs:serve | Serve bundled user docs with Starlight (port 3001) |
docs:engineering:serve | Serve engineering docs with Starlight (port 3002) |
CI / Quality
Section titled “CI / Quality”| Task | Purpose |
|---|---|
lint | clippy + fmt check + sqlx check + helm lint |
sqlx:prepare | Regenerate SQLX offline query cache |
sqlx:check | Verify SQLX queries are valid |
config:schema:generate / check | Backend settings JSON schema |
crd:generate / check | CRD YAML from Rust definition |
Development Workflow
Section titled “Development Workflow”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:
cargo build --bin riserise <command>Database schema:
sqlx migrate add <migration_name># Edit the new migration in migrations/sqlx migrate runcargo sqlx prepare # update offline query cache, commit the resultRegistry Configuration
Section titled “Registry Configuration”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"Environment Variables
Section titled “Environment Variables”.envrc (loaded by direnv) sets: DATABASE_URL, RISE_CONFIG_RUN_MODE, RISE_MANAGED_BUILDKIT_*, and PATH.
Server configuration lives in config/development.yaml.
Default Credentials
Section titled “Default Credentials”| Service | Credentials |
|---|---|
| PostgreSQL | postgres://rise:rise123@localhost:5432/rise |
| Dex (OIDC) | admin@example.com, dev@example.com, user@example.com — password: password |
Networking Overview
Section titled “Networking Overview”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_defaultDocker network (viaRISE_MANAGED_BUILDKIT_NETWORK_NAME) so it can push torise-registry:5000. - Minikube joins the same network so pods can pull images.
- The local cluster setup writes
.envvalues such asRISE_AUTH_BACKEND_URLandRISE_K8S_HOST_IPso Kubernetes pods can reach the host backend. - Runtime logs use the local Loki backend by default.
mise setupinstalls the dev Loki/Alloy chart components and forwards Loki tolocalhost:3100, whichconfig/development.yamlreads throughRISE_LOKI_URL.
Docker backend (local)
Section titled “Docker backend (local)”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:
mise setup hosts # one-time: adds /etc/hosts aliases (incl. rise-dex)mise backend:run docker # alias: mise br dockermise 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_defaultnetwork. There is no webhook listener and no cluster. replicas=1caveat: the Docker runtime clamps every deployment to a single container; horizontal scaling is a follow-up.- Env vars are passed as plain
KEY=VALUEand are therefore visible viadocker 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/hostsedits, since*.localhostresolves to127.0.0.1.
Troubleshooting
Section titled “Troubleshooting”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:
minikube ssh -- curl http://rise-registry:5000/v2/# Should return: {}If it fails, re-run mise setup minikube.
Reset everything:
docker compose down -vcargo cleanmise installmise devAccessing the Database
Section titled “Accessing the Database”docker compose exec postgres psql -U rise -d rise