Architecture
Metacontroller Integration
Section titled “Metacontroller Integration”Metacontroller is a Kubernetes operator that implements the composite-controller pattern on top of a simple webhook protocol — so Rise does not need to run a watch loop or write reconciliation logic from scratch.
Sync webhook
Metacontroller calls POST /api/v1/metacontroller/sync whenever a RiseProject resource changes or the configured resync interval elapses. The request body contains:
parent: theRiseProjectobject (name equals the project slug; spec is intentionally empty — the database is the source of truth)children: a snapshot of every child resource Metacontroller currently owns, grouped by kind
Rise reads the project state from the database, inspects the observed children to update deployment health/status, then returns the fully-specified set of child resources that should exist. Metacontroller creates, updates, or deletes child resources to match — including garbage-collecting anything no longer returned.
Finalize webhook
When a RiseProject is deleted, Metacontroller calls POST /api/v1/metacontroller/finalize before removing child resources. Rise marks all deployments for the project as Stopped, then returns finalized: true, at which point Metacontroller deletes the owned children.
Why Metacontroller
Using Metacontroller lets Rise express desired cluster state as a stateless function (database state → JSON list of resources) without owning the watch loop, retry logic, or garbage collection. Metacontroller handles watch/cache/retry; Rise handles business logic.
For webhook authentication details, see Webhook Security.
Naming Scheme
Section titled “Naming Scheme”Resources follow consistent naming patterns:
| Resource | Pattern | Example |
|---|---|---|
| Namespace | rise-{project} | rise-my-app |
| Deployment | {project}-{deployment_id} | my-app-20251207-143022 |
| Service | {escaped_group} | default, mr--26 |
| Ingress | {escaped_group} | default, mr--26 |
| ServiceAccount | env-{environment} | env-production, env-staging |
| Secret | rise-registry-creds | rise-registry-creds |
Character escaping: Sequences of characters not in [A-Za-z0-9-_.] are replaced with --. For example, mr/26 becomes mr--26. Group names are lowercase alphanumeric segments joined by single - or / separators — adjacent separators (--, //, -/, /-) are rejected so no two group names normalize to the same string, and the normalized result must be at most 63 characters (Kubernetes label value limit).
Deployment Groups and URLs
Section titled “Deployment Groups and URLs”Each deployment group gets its own Service and Ingress with a unique URL:
| Group | URL Pattern | Example |
|---|---|---|
default | production_ingress_url_template | my-app.apps.rise.local |
| Custom groups | staging_ingress_url_template | my-app--mr--26.preview.rise.local |
Environments and URL routing: Environments layer on top of deployment groups to control which URL template applies. The environment marked as production determines which deployments receive the production URL (via production_ingress_url_template). All other deployments — regardless of their group name — use the staging URL template. Custom domains also attach to production environment deployments only. See the environments guide for how to create and configure environments.
Hostnames
Section titled “Hostnames”Every URL template is a hostname pattern, so each project (and each deployment group and environment) gets a host of its own:
- Production:
{project_name}.apps.rise.local - Staging:
{project_name}--{deployment_group}.preview.rise.local - Ingress paths are the app’s own routes (
/by default,Prefixtype); no path rewriting - Requires a wildcard TLS certificate (e.g.,
*.apps.rise.example.comand*.preview.rise.example.com) — see Custom Domains for cert-manager setup
Path-based templates such as rise.local/{project_name} are rejected at
startup: apps sharing one host would share its cookies and origin.
Blue/Green Deployments
Section titled “Blue/Green Deployments”The controller implements blue/green deployments using Service selector updates:
- Deploy new Deployment: Create new Deployment with deployment-specific labels
- Wait for health: Wait until new Deployment pods are ready and pass health checks
- Switch traffic: Update Service selector to point to new deployment labels
- Previous deployment: Old Deployment remains but receives no traffic
This ensures zero-downtime deployments with instant rollback capability.
Labels
Section titled “Labels”All resources are labeled for management and selection:
labels: app.kubernetes.io/managed-by: "rise" rise.dev/project: "my-app" rise.dev/environment: "production" # present when deployment has an environment rise.dev/deployment-group: "default" rise.dev/deployment-id: "20251207-143022" rise.dev/deployment-uuid: "550e8400-e29b-41d4-a716-446655440000"