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 httpsinstead ofproxy 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.comto<tunnel-id>.tun.cruma.io, then add it to the hostname list. apiexpands toapi.<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-indexto enable directory listing when no index file is present. - Add
--render-markdownto renderindex.md(and other.mdfiles) as HTML. - Add
--spato enable SPA fallback: serve the nearestindex.htmlinstead of 404 for missing paths. - You can add simple auth with
--user USERNAME PASSWORDor--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.iohostname 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).