Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. default.{toml,yaml,yml} - Base configuration with sensible defaults
  2. {RISE_CONFIG_RUN_MODE}.{toml,yaml,yml} - Environment-specific config (optional)
    • development.toml or development.yaml when RISE_CONFIG_RUN_MODE=development
    • production.toml or production.yaml when RISE_CONFIG_RUN_MODE=production
  3. 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 variable VAR_NAME, error if not set
  • ${VAR_NAME:-default} - Use VAR_NAME if set, otherwise use default

How It Works

  1. Configuration files are parsed as TOML or YAML
  2. String values are scanned for ${...} patterns
  3. Patterns are replaced with environment variable values
  4. 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):

  1. default.{toml,yaml,yml} - Base configuration with defaults
  2. {RISE_CONFIG_RUN_MODE}.{toml,yaml,yml} - Environment-specific (e.g., production.yaml)
  3. local.{toml,yaml,yml} - Local overrides (not in git)
  4. Environment variable substitution - ${VAR} patterns are replaced
  5. DATABASE_URL special case - Overrides [database] url if 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 with openssl 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 JWTs
  • rs256_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_URL contains the primary custom domain URL (e.g., https://example.com)
  • If no primary domain is set: RISE_APP_URL contains 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:

  1. Navigate to your project’s Domains tab
  2. Add custom domains using the “Add Domain” button
  3. Click the star icon next to a domain to set it as primary
  4. 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_mode in 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_URL environment variable
  • Deletion protection: You can delete a primary domain; RISE_APP_URL will 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_URLS environment variable as a JSON array