Configuration Guide
Rise backend uses YAML configuration files with environment variable substitution support. TOML is also supported for backward compatibility.
Configuration Files
Configuration files are located in config/ and loaded in this order:
default.{toml,yaml,yml}- Base configuration with sensible defaults{RISE_CONFIG_RUN_MODE}.{toml,yaml,yml}- Environment-specific config (optional)development.tomlordevelopment.yamlwhenRISE_CONFIG_RUN_MODE=developmentproduction.tomlorproduction.yamlwhenRISE_CONFIG_RUN_MODE=production
local.{toml,yaml,yml}- Local overrides (not checked into git)
Later files override earlier ones.
File Format: The backend supports both YAML and TOML formats. When multiple formats exist for the same config file (e.g., both default.yaml and default.toml), TOML takes precedence. YAML is the recommended format as it integrates seamlessly with Kubernetes/Helm deployments.
Environment Variable Substitution
Configuration values can reference environment variables using the syntax:
# TOML example
client_secret = "${RISE_AUTH_CLIENT_SECRET:-rise-backend-secret}"
account_id = "${AWS_ACCOUNT_ID}"
public_url = "https://${DOMAIN_NAME}:${PORT}"
# YAML example
auth:
client_secret: "${RISE_AUTH_CLIENT_SECRET:-rise-backend-secret}"
registry:
account_id: "${AWS_ACCOUNT_ID}"
server:
public_url: "https://${DOMAIN_NAME}:${PORT}"
Syntax
${VAR_NAME}- Use environment variableVAR_NAME, error if not set${VAR_NAME:-default}- UseVAR_NAMEif set, otherwise usedefault
How It Works
- Configuration files are parsed as TOML or YAML
- String values are scanned for
${...}patterns - Patterns are replaced with environment variable values
- Resulting configuration is deserialized into Settings struct
This happens after TOML/YAML parsing but before deserialization, so:
- ✅ Works in all string values (including nested tables/maps and arrays)
- ✅ Preserves structure and types
- ✅ Clear error messages if required variables are missing
Configuration Precedence
Configuration is loaded in this order (later values override earlier ones):
default.{toml,yaml,yml}- Base configuration with defaults{RISE_CONFIG_RUN_MODE}.{toml,yaml,yml}- Environment-specific (e.g., production.yaml)local.{toml,yaml,yml}- Local overrides (not in git)- Environment variable substitution -
${VAR}patterns are replaced - DATABASE_URL special case - Overrides
[database] urlif set
Note: When multiple file formats exist for the same config file, TOML takes precedence over YAML.
Example (TOML):
# In default.toml
client_secret = "${AUTH_SECRET:-default-secret}"
# In production.toml
client_secret = "${AUTH_SECRET}" # Override: no default, required
# In local.toml
client_secret = "my-local-secret" # Override: hardcoded value
Example (YAML):
# In default.yaml
auth:
client_secret: "${AUTH_SECRET:-default-secret}"
# In production.yaml (overrides default.yaml)
auth:
client_secret: "${AUTH_SECRET}" # No default, required
Special Cases
DATABASE_URL: For convenience, the DATABASE_URL environment variable is checked after config loading and will override any [database] url setting. This is optional - you can use ${DATABASE_URL} in TOML instead:
# Option 1: Direct environment variable (checked after config loads)
[database]
url = "" # Empty, DATABASE_URL env var will be used
# Option 2: Explicit substitution (recommended for consistency)
[database]
url = "${DATABASE_URL}"
Note: DATABASE_URL is only required at compile time for SQLX query verification. At runtime, you can set it via either method above.
Examples
Development (default.toml)
[server]
host = "0.0.0.0"
port = 3000
public_url = "http://localhost:3000"
[auth]
issuer = "http://localhost:5556/dex"
client_id = "rise-backend"
client_secret = "${RISE_AUTH_CLIENT_SECRET:-rise-backend-secret}"
Production with Environment Variables (TOML)
# production.toml
[server]
host = "0.0.0.0"
port = "${PORT:-3000}"
public_url = "${PUBLIC_URL}" # Required, no default
cookie_secure = true
[auth]
issuer = "${DEX_ISSUER}"
client_id = "${OIDC_CLIENT_ID}"
client_secret = "${OIDC_CLIENT_SECRET}" # Required
admin_users = ["${ADMIN_EMAIL}"]
[registry]
type = "ecr"
region = "${AWS_REGION:-us-east-1}"
account_id = "${AWS_ACCOUNT_ID}"
role_arn = "${ECR_CONTROLLER_ROLE_ARN}"
push_role_arn = "${ECR_PUSH_ROLE_ARN}"
Production with Environment Variables (YAML)
# production.yaml - ideal for Kubernetes/Helm deployments
server:
host: "0.0.0.0"
port: "${PORT:-3000}"
public_url: "${PUBLIC_URL}" # Required, no default
cookie_secure: true
auth:
issuer: "${DEX_ISSUER}"
client_id: "${OIDC_CLIENT_ID}"
client_secret: "${OIDC_CLIENT_SECRET}"
admin_users:
- "${ADMIN_EMAIL}"
database:
url: "${DATABASE_URL}"
registry:
type: "ecr"
region: "${AWS_REGION:-us-east-1}"
account_id: "${AWS_ACCOUNT_ID}"
role_arn: "${ECR_CONTROLLER_ROLE_ARN}"
push_role_arn: "${ECR_PUSH_ROLE_ARN}"
Environment file:
# .env
PUBLIC_URL=https://rise.example.com
DEX_ISSUER=https://dex.example.com
OIDC_CLIENT_ID=rise-production
OIDC_CLIENT_SECRET=very-secret-value
ADMIN_EMAIL=admin@example.com
AWS_ACCOUNT_ID=123456789012
ECR_CONTROLLER_ROLE_ARN=arn:aws:iam::123456789012:policy/rise-backend
ECR_PUSH_ROLE_ARN=arn:aws:iam::123456789012:role/rise-backend-ecr-push
DATABASE_URL=postgres://rise:${DB_PASSWORD}@db.example.com/rise
Local Overrides (local.toml)
For local development, create local.toml (not checked into git):
# Override just what you need
[auth]
client_secret = "my-local-secret"
[registry]
type = "oci-client-auth"
registry_url = "localhost:5000"
Configuration Reference
Server Settings
[server]
host = "0.0.0.0" # Bind address
port = 3000 # HTTP port
public_url = "http://..." # Public URL (for OAuth redirects)
cookie_domain = "" # Cookie domain ("" = current host only)
cookie_secure = false # Set true for HTTPS
jwt_signing_secret = "..." # JWT signing secret (base64-encoded, min 32 bytes)
jwt_expiry_seconds = 86400 # JWT expiry duration in seconds (default: 24 hours)
jwt_claims = ["sub", "email", "name"] # Claims to include from IdP
rs256_private_key_pem = "..." # Optional: RS256 private key (persists JWTs across restarts)
rs256_public_key_pem = "..." # Optional: RS256 public key (derived if not provided)
JWT Configuration:
jwt_signing_secret: Base64-encoded secret for HS256 JWT signing (generate withopenssl rand -base64 32)jwt_expiry_seconds: Duration in seconds before JWTs expire (default: 86400 = 24 hours)jwt_claims: Claims to include from IdP token in Rise JWTsrs256_private_key_pem: Optional pre-configured RS256 private key (prevents JWT invalidation on restart)rs256_public_key_pem: Optional RS256 public key (automatically derived from private key if omitted)
Auth Settings
[auth]
issuer = "http://..." # OIDC issuer URL
client_id = "rise-backend" # OAuth2 client ID
client_secret = "..." # OAuth2 client secret
admin_users = ["email@..."] # Admin user emails (array)
Database Settings
[database]
url = "postgres://..." # PostgreSQL connection string
# Or use DATABASE_URL env var
Registry Settings
AWS ECR
[registry]
type = "ecr"
region = "us-east-1"
account_id = "123456789012"
repo_prefix = "rise/"
role_arn = "arn:aws:iam::..."
push_role_arn = "arn:aws:iam::..."
auto_remove = true
OCI Registry (Docker, Harbor, Quay)
[registry]
type = "oci-client-auth"
registry_url = "registry.example.com"
namespace = "rise-apps"
Controller Settings (Optional)
[controller]
reconcile_interval_secs = 5
health_check_interval_secs = 5
termination_interval_secs = 5
cancellation_interval_secs = 5
expiration_interval_secs = 60
secret_refresh_interval_secs = 3600
Validation
The backend validates configuration on startup:
- Required fields must be set
- Invalid values cause startup failure with clear error messages
- Environment variable substitution errors are reported
- Unknown configuration fields generate warnings (as of v0.9.0)
Checking Configuration
Use the rise backend check-config command to validate backend configuration:
rise backend check-config
This command:
- Loads and validates backend configuration files
- Reports any unknown/unused configuration fields as warnings
- Exits with an error if configuration is invalid
- Useful for CI/CD pipelines and deployment validation
Example output:
Checking backend configuration...
⚠️ WARN: Unknown configuration field in backend config: server.typo_field
⚠️ WARN: Unknown configuration field in backend config: unknown_section
✓ Configuration is valid
Unknown Field Warnings
Starting in v0.9.0, Rise warns about unrecognized configuration fields to help catch typos and outdated options:
Backend Configuration (YAML/TOML):
# Warnings appear in logs when starting server or using check-config
WARN rise::server::settings: Unknown configuration field in backend config: server.unknown_field
Project Configuration (rise.toml):
# Warnings appear when loading rise.toml (during build, deploy, etc.)
WARN rise::build::config: Unknown configuration field in ./rise.toml: build.?.typo_field
These are warnings, not errors - your configuration will still load and work. The warnings help you:
- Catch typos in field names
- Identify outdated configuration options after upgrades
- Ensure your configuration is being used as intended
Run with RUST_LOG=debug to see configuration loading details:
RUST_LOG=debug cargo run --bin rise -- backend server
Custom Domains
Rise supports custom domains for projects, allowing you to serve your applications from your own domain names instead of (or in addition to) the default project URL.
Primary Custom Domains
Each project can designate one custom domain as primary. The primary domain is used as the canonical URL for the application and is exposed via the RISE_APP_URL environment variable.
RISE_APP_URL Environment Variable
Rise automatically creates a RISE_APP_URL deployment environment variable containing the canonical URL for the application. This variable is determined at deployment creation time and persisted in the database:
- If a primary custom domain is set:
RISE_APP_URLcontains the primary custom domain URL (e.g.,https://example.com) - If no primary domain is set:
RISE_APP_URLcontains the default project URL (e.g.,https://my-app.rise.dev)
Since this is a deployment environment variable, you can view it via the API or CLI along with your other environment variables.
This environment variable is useful for:
- Generating absolute URLs in your application (e.g., for email links, OAuth redirects)
- Implementing canonical URL redirects (redirect all traffic to the primary domain)
- Setting the correct domain for cookies and CORS headers
Example usage in your application:
// Node.js
const canonicalUrl = process.env.RISE_APP_URL;
// Redirect to canonical domain
app.use((req, res, next) => {
const requestUrl = `${req.protocol}://${req.get('host')}`;
if (requestUrl !== canonicalUrl) {
return res.redirect(301, `${canonicalUrl}${req.url}`);
}
next();
});
# Python
import os
canonical_url = os.environ.get('RISE_APP_URL')
# Flask: Set SERVER_NAME
app.config['SERVER_NAME'] = canonical_url.replace('https://', '').replace('http://', '')
Managing Custom Domains
Via Frontend:
- Navigate to your project’s Domains tab
- Add custom domains using the “Add Domain” button
- Click the star icon next to a domain to set it as primary
- The primary domain will show a filled yellow star and a “Primary” badge
Via API:
# List custom domains
curl https://rise.dev/api/projects/my-app/domains
# Add a custom domain
curl -X POST https://rise.dev/api/projects/my-app/domains \
-H "Authorization: Bearer $TOKEN" \
-d '{"domain": "example.com"}'
# Set domain as primary
curl -X PUT https://rise.dev/api/projects/my-app/domains/example.com/primary \
-H "Authorization: Bearer $TOKEN"
# Unset primary status
curl -X DELETE https://rise.dev/api/projects/my-app/domains/example.com/primary \
-H "Authorization: Bearer $TOKEN"
DNS Configuration
Before adding a custom domain, you must configure your DNS to point to your Rise deployment:
# A record for root domain
example.com. IN A <rise-ingress-ip>
# CNAME for subdomain
www.example.com. IN CNAME <rise-ingress-hostname>
Custom domains are added to the ingress for the default deployment group only.
TLS/SSL
Custom domains use the same TLS configuration as the default project URL:
- If your Rise deployment uses a wildcard certificate, custom domains will use HTTP unless configured with per-domain TLS
- Configure
custom_domain_tls_modein the Kubernetes controller settings for automatic HTTPS on custom domains
Behavior
- Automatic reconciliation: Setting or unsetting a primary domain triggers reconciliation of the active deployment to update the
RISE_APP_URLenvironment variable - Deletion protection: You can delete a primary domain;
RISE_APP_URLwill fall back to the default project URL - Multiple domains: You can add multiple custom domains to a project, but only one can be primary
- Environment variable list: All custom domains (primary and non-primary) are also available in the
RISE_APP_URLSenvironment variable as a JSON array