Skip to main content

Configuration via file

You can run cruma from a config file instead of passing flags. The CLI accepts YAML or JSON and will validate the structure before starting the tunnel. The agent watches the file and applies changes to credentials and targets automatically.

Quick reference

CommandDescription
cruma config locatePrint the default config file path
cruma config init [PATH]Create a new config with ANON credentials and no targets
cruma config resetDelete and recreate the default config
cruma config profilesList known profile IDs
cruma config showDisplay the current configuration
cruma config set-credentials <TID> <SK>Set tunnel credentials
cruma config clearRemove all frontends, backends, and processes
cruma config add <type> ...Add a backend + frontend route
cruma config remove <INDEX>Remove a frontend/backend by index
cruma config remove-process <INDEX>Remove a hosted process by index
cruma config update <INDEX> ...Update a target by index
cruma config add-listener <PORT>Add a local listener
cruma config remove-listener <INDEX>Remove a local listener by index
cruma config update-listener <INDEX> ...Update a local listener by index
cruma schemaPrint the full JSON schema for the config file

All config subcommands accept -c <PATH> / --config <PATH> to target a specific file instead of the default.

Config file structure

The config file has two required top-level arrays — backends and frontends — plus optional sections for credentials, processes, listeners, and agent behavior.

warning

Older configs that used a flat targets array are no longer supported. The agent will reject them with an error and suggest running cruma config reset to migrate.

Minimal example

tunnel_id: "ANON"
tunnel_secret: "ANON"

backends:
- id: backend-1
kind: http
destination: "127.0.0.1:3000"

frontends:
- hostname: "react-dev"
backend_id: backend-1

Full example

tunnel_id: "demo-tunnel"
tunnel_secret: "beta-secret-123"
tower_server: "tower.cruma.io:443" # optional, default shown
profile: "my-profile" # optional, scopes cached identity
# temp: true # optional, fresh FQDN each run

backends:
- id: web
kind: http
destination: "127.0.0.1:3000"

- id: api
kind: https
destination: "example.com:443"

- id: docs
kind: local-directory
destination: "./public"
allow_directory_indexing: true
render_markdown: true
spa_fallback: false

frontends:
- hostname: "react-dev"
backend_id: web

- hostname: "api"
backend_id: api
- hostname: "api.dev.yourdomain.com"
backend_id: api

- hostname: "docs"
backend_id: docs

processes:
- id: my-app
command: node
args: ["server.js"]
working_directory: "./app"
env:
PORT: "3000"
NODE_ENV: "development"
restart_policy: on-failure # never | on-failure | always
auto_start: true

listeners:
- port: 8443
kind: https
addr: localhost
cert_mode: self_signed

- port: 8080
kind: http
addr: localhost

Backends

A backend describes where traffic goes. Each backend has a stable id that frontends reference.

FieldRequiredDescription
idyesStable identifier (e.g. backend-1, web, api)
kindyeshttp, https, tcp, raw, or local-directory
destinationyeshost:port for network backends, or a directory path for local-directory
allow_directory_indexingnoShow directory listing when no index file is present (local-directory only)
render_markdownnoRender .md files as HTML (local-directory only)
spa_fallbacknoServe the nearest index.html for 404 paths — standard SPA behavior (local-directory only)
form_authnoDefault form-based auth for frontends that don't override it
api_key_authnoDefault API key auth ["Header", "value"]

Backend kinds

  • http — connect to the backend over plain HTTP.
  • https — connect to the backend over HTTPS (TLS to origin).
  • tcp — forward plain TCP to the backend after TLS has been terminated. For assigned FQDNs (*.tun.cruma.io), TLS is terminated either on the agent or at the Cruma ingress depending on whether the assigned-hostname certificate is active; for CNAME'd custom domains (paid tier only), TLS is terminated on your agent. Either way, the backend receives a plain TCP stream.
  • raw — pass through the TCP stream without additional TLS termination. For CNAME'd custom domains, this means the encrypted TLS stream is forwarded directly to the backend, which must handle TLS itself (true end-to-end TLS between client and backend). For assigned FQDNs (*.tun.cruma.io), TLS is still terminated before the backend, either on the agent or at the Cruma ingress, so raw behaves the same as tcp — the backend receives plain TCP.
  • local-directory — serve static files from a local directory path.
tip

If you need true TLS pass-through to your backend, use raw with a CNAME'd custom domain. With assigned FQDNs, TLS termination normally done using the agent itself as it reuires custom protocols to do DNS01 ACME challenges through the control channels of cruma.io.

Frontends

A frontend maps a hostname pattern to either a backend or a hosted process. Each hostname gets its own frontend entry.

FieldRequiredDescription
hostnameyesHostname pattern (see below)
backend_idone ofReferences a backend id. Mutually exclusive with process_id.
process_idone ofReferences a process id. Mutually exclusive with backend_id.
middlewaresnoOrdered list of HTTP middlewares (see Middlewares)
form_authnoRoute-level form auth override
api_key_authnoRoute-level API key auth override

Hostname patterns

  • app — expands to app.<assigned-fqdn> (e.g. app.abc123.tun.cruma.io)
  • app.yourdomain.com — exact match on a custom domain (requires CNAME)
  • *.yourdomain.com — wildcard match for any subdomain
  • * — matches any hostname (not recommended)

Multiple frontends can point to the same backend (e.g. a shortname and a custom domain both routing to the same service).

Processes

The agent can supervise local processes and optionally create frontend routes backed by them. This is useful for running your app server alongside the tunnel in a single command.

FieldRequiredDescription
idyesStable process identifier
commandyesBinary or command to run
argsnoList of arguments
working_directorynoWorking directory for the process
envnoMap of environment variables
auto_startnoStart the process automatically (default: true)
start_on_requestnoLazily start the process on first incoming request (default: false)
restart_policynonever, on-failure, or always (default: on-failure)
upstream_protocolnoHTTP protocol to the process: h1 (default), h2, or h2pk
upstream_tlsnoUse HTTPS to connect to the process (default: false)
backend_timeout_secondsnoTimeout for upstream response in seconds (default: 10)
idle_timeout_secondsnoAuto-stop the process after being idle for this many seconds. Combined with start_on_request for full spin-up/spin-down lifecycle. 0 or omitted means never auto-stop.

To route traffic to a process, add a frontend with process_id instead of backend_id:

processes:
- id: my-app
command: node
args: ["server.js"]
working_directory: "./app"
env:
PORT: "3000"
restart_policy: on-failure

frontends:
- hostname: "myapp"
process_id: my-app

Add a process via CLI:

cruma config add process node --arg server.js --working-dir ./app --env "PORT=3000" --hostname myapp

Additional CLI options for config add process:

  • --id <ID> — set a stable process identifier (auto-generated if omitted)
  • --no-auto-start — disable auto-start
  • --start-on-request — lazily start on first request
  • --restart <POLICY> — restart policy: never, on-failure, always (default: on-failure)

Listeners

Listeners bind to local addresses so you can access your routes directly on localhost (or on a LAN) without going through the Cruma cloud ingress. They share the same routing table as the cloud ingress.

FieldRequiredDescription
kindnohttp, https, or cruma. When omitted, defaults to https if TLS is enabled, http otherwise.
portyesPort number to listen on (ignored for kind: cruma)
addrnolocalhost (default, loopback only) or all (0.0.0.0)
tlsnoLegacy field. Prefer using kind instead.
cert_modenoself_signed (default), acme_alpn (Let's Encrypt via TLS-ALPN-01; requires port 443 reachable from the internet), or from_frontend (inherit cert mode from each frontend route)
listeners:
- kind: https
port: 8443
addr: localhost
cert_mode: self_signed

- kind: http
port: 8080
addr: localhost

Add a listener via CLI:

cruma config add-listener 8443
cruma config add-listener 8080 --no-tls
cruma config add-listener 443 --addr all --cert-mode acme-alpn
cruma config add-listener 8443 --kind https --cert-mode from-frontend

Update a listener:

cruma config update-listener 0 --port 9443
cruma config update-listener 0 --addr all
cruma config update-listener 0 --cert-mode acme-alpn
cruma config update-listener 0 --kind http --tls false

Local-only mode

When local_only: true is set in the config, the agent does not connect to the Cruma cloud. Only local listeners are active. This is useful for running the agent purely as a local reverse proxy or development tool.

local_only: true

backends:
- id: web
kind: http
destination: "127.0.0.1:3000"

frontends:
- hostname: "web"
backend_id: web

listeners:
- kind: https
port: 8443

Access controls

Form-based authentication

Form auth provides a built-in login page with signed-cookie sessions. It can be set as a default on the backend or overridden per frontend.

On a backend (applies to all frontends that don't override):

backends:
- id: web
kind: http
destination: "127.0.0.1:3000"
form_auth:
users:
- ["admin", "secret-password"]
- ["viewer", "viewer-pass"]
secret: "replace-with-a-random-secret"

API key authentication

Require a specific header and value on every request:

backends:
- id: web
kind: http
destination: "127.0.0.1:3000"
api_key_auth: ["X-API-Key", "secret123"]

Middlewares

Frontends support an ordered list of HTTP middlewares. These are applied in order for each request.

frontends:
- hostname: "app"
backend_id: web
middlewares:
- type: redirect_http_to_https

- type: allow_cors
origins: { mode: any }
allow_credentials: false
handle_preflight: true
allow_private_network: false

- type: add_req_header
name: "X-Custom-Header"
value: "my-value"

- type: remove_req_header
name: "X-Unwanted"

- type: add_resp_header
name: "X-Frame-Options"
value: "DENY"

- type: remove_resp_header
name: "Server"

- type: rewrite_path
strip_prefix: "/api"

- type: rewrite_host
to: "internal.example.com"

- type: redirect
location: "https://example.com/new-path"
code: 301

Available middleware types

TypeDescription
redirect_http_to_httpsRedirect HTTP requests to HTTPS
basic_authHTTP Basic Auth (RFC 7617)
form_authHTML form-based login with signed-cookie sessions
authenticationGeneral authentication with pluggable schemes
allow_corsCORS headers and preflight handling
add_req_headerAdd a header to the inbound request
remove_req_headerRemove a header from the inbound request
add_resp_headerAdd a header to the outbound response
remove_resp_headerRemove a header from the outbound response
rewrite_pathRewrite the request path (strip_prefix or replace_regex)
rewrite_hostReplace the Host header sent to origin
redirectIssue an HTTP redirect (short-circuit)
cookie_opsAdd or remove cookies
ip_filterIP allow/deny filtering (not yet implemented in runtime)
cache_control_overrideOverride Cache-Control headers (not yet implemented in runtime)
cacheIn-memory response cache for GET requests (not yet implemented in runtime)
compressionContent compression negotiation (not yet implemented in runtime)
rate_limitRate limiting by IP, header, cookie, or composite key (not yet implemented in runtime)

Note: X-Forwarded-Proto, X-Forwarded-For, and X-Forwarded-Host headers are automatically added by the proxy runtime — you don't need to configure them.

Use cruma schema to see the full JSON schema with all middleware fields and validation rules.

Managing configs with the CLI

Adding targets

The cruma config add command creates a backend and a frontend route in one step:

# Add an HTTP target
cruma config add http 127.0.0.1:3000 --hostname react-dev

# Add an HTTPS target with multiple hostnames
cruma config add https example.com:443 --hostname api --hostname api.dev.yourdomain.com

# Add a TCP target
cruma config add tcp 127.0.0.1:5432 --hostname db

# Add a raw TCP target
cruma config add raw 127.0.0.1:4943 --hostname raw-service

# Add a local directory with auth
cruma config add dir ./public --hostname docs --enable-index true --render-markdown --user admin:pass123

# Add a hosted process with a frontend route
cruma config add process node --arg server.js --hostname myapp --env "PORT=3000" --restart on-failure

# Add a process that starts lazily on first request
cruma config add process node --arg server.js --hostname myapp --start-on-request

# Add a process without auto-starting it
cruma config add process node --arg server.js --no-auto-start

Inspecting configuration

cruma config show

This prints an indexed view of all backends, frontends, processes, and listeners, which you need for remove and update commands.

Updating targets

# Change destination
cruma config update 0 --dest 127.0.0.1:4000

# Add/remove hostnames
cruma config update 0 --add-hostname new-host --remove-hostname old-host

# Clear all hostnames at once
cruma config update 0 --clear-hostnames

# Add form auth user
cruma config update 0 --add-user admin:password

# Clear all form auth users
cruma config update 0 --clear-users

# Set API key auth
cruma config update 0 --api-key "X-API-Key:secret123"

# Clear API key auth
cruma config update 0 --clear-api-key

# Enable/disable directory indexing (for directory targets)
cruma config update 0 --enable-index true

# Enable/disable markdown rendering (for directory targets)
cruma config update 0 --render-markdown false

Removing targets

# Remove by index (see 'config show' for indices)
cruma config remove 0

# Remove a process
cruma config remove-process 0

# Remove a listener
cruma config remove-listener 0

Setting credentials

cruma config set-credentials MY_TUNNEL_ID MY_SECRET_KEY

Running from a config file

# Use the default config file
cruma start

# Use a specific file
cruma start ./cruma.yaml

# Shorthand: use -c / --config on the top-level command
cruma -c ./cruma.yaml

Default config vs. multiple configs

The default config file is just a convenience. You can run multiple agents by pointing each one at a different config file (with a different profile set in each config):

cruma start ./configs/app-a.yaml
cruma start ./configs/app-b.yaml

Where each config file specifies its own profile:

# configs/app-a.yaml
profile: "app-a"
# ...

Use multiple configs when you want separate tunnel credentials or distinct target sets per agent. If you only need one agent with multiple services, keep a single config file and add multiple targets.

Top-level settings reference

FieldRequiredDefaultDescription
backendsyes[]Backend services
frontendsyes[]Frontend routes
tunnel_idnoANONTunnel ID for the public FQDN
tunnel_secretnoANONTunnel secret key
tower_servernotower.cruma.io:443Control plane endpoint
profilenoNamed profile to scope cached identity
tempnofalseUse a temporary identity (fresh FQDN each run)
local_onlynofalseDisable cloud tunnel; only local listeners are active
processesno[]Hosted processes supervised by the agent
listenersno[]Local listeners

Notes:

  • profile and temp are mutually exclusive.
  • Changes to tower_server, profile, or temp require a restart to take effect.
  • Other changes (credentials, backends, frontends) are hot-reloaded when the config file is saved.