Skip to main content

Common Scenarios

Quick recipes for typical setups. Replace SECRET_KEY/TUNNEL_ID with your account credentials, or omit --tunnel-id and --secret-key entirely for anonymous mode.

React dev server on port 3000

cruma proxy http 127.0.0.1:3000 --tunnel-id react-dev --secret-key SECRET_KEY
  • Start your dev server (npm start/yarn start), then run the tunnel.
  • If your dev server uses HTTPS, use proxy https instead of proxy http.

API server on port 8080 with custom domain

tunnel_id: "api-demo"
tunnel_secret: "SECRET_KEY"

backends:
- id: api
kind: http
destination: "127.0.0.1:8080"

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

Run it:

cruma start ./cruma.yaml
  • CNAME api.dev.yourdomain.com to <tunnel-id>.tun.cruma.io, then add it to the hostname list.
  • api expands to api.<tunnel-id>.tun.cruma.io.

Or add it to an existing config via CLI:

cruma config add http 127.0.0.1:8080 --hostname api --hostname api.dev.yourdomain.com

Multi-target config file

Use a config file to run multiple targets with one command:

tunnel_id: "demo-tunnel"
tunnel_secret: "beta-secret-123"

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

frontends:
- hostname: "api"
backend_id: api
- hostname: "api.dev.yourdomain.com"
backend_id: api
- hostname: "react-dev"
backend_id: web
- hostname: "react.dev.yourdomain.com"
backend_id: web

Run it:

cruma start ./cruma.yaml

Serve a local directory

cruma serve ./public --allow-dir-index --render-markdown --tunnel-id YOUR_TUN_ID --secret-key SECRET_KEY
  • Add --allow-dir-index to enable directory listing when no index file is present.
  • Add --render-markdown to render index.md (and other .md files) as HTML.
  • Add --spa to enable SPA fallback: serve the nearest index.html instead of 404 for missing paths.
  • You can add simple auth with --user USERNAME PASSWORD or --api-key X-API-Key secret123.

SPA (single-page application)

If you're building a React, Vue, or similar SPA and want client-side routing to work:

cruma serve ./dist --spa

With --spa, any request that would 404 instead serves the nearest index.html, so your client-side router handles the path.

In a config file, set spa_fallback: true on the backend:

backends:
- id: spa
kind: local-directory
destination: "./dist"
spa_fallback: true

frontends:
- hostname: "app"
backend_id: spa

Hosted process (e.g. Node.js app)

Let the tunnel agent start and supervise your app server:

tunnel_id: "demo"
tunnel_secret: "SECRET_KEY"

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

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

Or add it via CLI:

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

The agent will start the process, restart it according to the restart policy, and route traffic to it.

Lazy process start (start on request)

You can configure a process to only start when the first request arrives, and optionally stop it after a period of inactivity:

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

Or via CLI:

cruma config add process node --arg server.js --start-on-request --hostname app

When a request arrives for a frontend backed by this process and the process is not running, the agent will start it and wait for it to become ready before proxying the request. With idle_timeout_seconds, the process is automatically stopped after being idle for the specified duration.

Local-only reverse proxy (no cloud tunnel)

Use local_only: true with listeners to run the agent as a purely local reverse proxy — no traffic goes through Cruma cloud:

local_only: true

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

frontends:
- hostname: "web"
backend_id: web

listeners:
- port: 8443
addr: localhost
tls: true
cert_mode: self_signed

Access your service at https://localhost:8443.

Local listener alongside cloud tunnel

You can have both cloud access and a local listener at the same time:

tunnel_id: "demo"
tunnel_secret: "SECRET_KEY"

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

frontends:
- hostname: "app"
backend_id: web

listeners:
- port: 8080
tls: false

Now your service is available both at app.<tunnel-id>.tun.cruma.io and http://localhost:8080.

High-assurance (pinning/mTLS)

  • Use a custom hostname (CNAME to <tunnel-id>.tun.cruma.io). The agent automatically obtains a Let's Encrypt certificate via ACME TLS-ALPN-01 and terminates TLS locally — payloads are end-to-end encrypted between clients and your agent by default.
  • On paid plans with an active subscription, the agent can also terminate TLS for its assigned *.tun.cruma.io hostname after obtaining a certificate via ACME DNS-01. If that process is not ready or fails, Cruma falls back to ingress termination automatically.
  • For additional hardening, pin your agent's certificate in clients (or use mTLS) so only your cert is accepted.
  • Set CAA records on your domain to restrict certificate issuance to your chosen CA (and optionally to your specific ACME account).

CORS headers for an API

Use the allow_cors middleware on a frontend:

backends:
- id: api
kind: http
destination: "127.0.0.1:8080"

frontends:
- hostname: "api"
backend_id: api
middlewares:
- type: allow_cors
origins: { mode: any }
allow_credentials: false
handle_preflight: true
allow_private_network: false

Path-based routing with rewrites

Strip a path prefix before forwarding to the backend:

frontends:
- hostname: "app"
backend_id: api
middlewares:
- type: rewrite_path
strip_prefix: "/api"

Requests to app.<fqdn>/api/users will be forwarded to the backend as /users.

Upstream protocol selection

When your backend requires HTTP/2, use --upstream-protocol on the CLI:

cruma proxy http 127.0.0.1:3000 --upstream-protocol h2

Or set upstream_protocol on a process definition in the config file:

processes:
- id: grpc-server
command: ./grpc-server
upstream_protocol: h2

Available values: h1 (default), h2, h2pk (HTTP/2 Prior Knowledge).