Deployment

Deployment

Requirements and deployment model for the self-hosted engine.


The Community Edition engine runs as one process named rstream-engine-ce. It reads YAML configuration from --config, -c, or RSTREAM_ENGINE_CONFIG, then applies environment overrides with the RSTREAM_ENGINE_ prefix.

CE requires a TLS listener, a static certificate provider, and a JWT authentication backend. It does not issue certificates and does not call the hosted Control plane.

Network Shape

Expose the TLS listener on TCP 443 in production. Agents use it for the rstream control channel, downstream users use it for published HTTPS and TLS tunnels, and API clients use it for /api endpoints. The optional HTTP listener can be exposed on TCP 80 when plain HTTP requests should redirect to HTTPS. Prometheus should stay private, or require a bearer token when exposed outside loopback.

PortRequiredUse
443/tcpYesTLS listener for agent control channels, Engine API, and published endpoints.
80/tcpOptionalHTTP redirect listener.
Configured TCP ephemeral rangeOptionalDynamically allocated published TCP listeners.
9090/tcp or private equivalentOptionalPrometheus metrics endpoint when enabled.

DNS must point the base engine host and the tunnel wildcard to the server:

edge.example.com       A/AAAA  <engine-public-ip>
*.t.edge.example.com   CNAME   edge.example.com

Agents connect to the base engine host, for example edge.example.com:443. Published stable domains are labels under .t.<engine-host>; a requested label becomes <label>.t.edge.example.com.

TLS Certificate

The static certificate must be valid for every hostname that reaches the TLS listener. For edge.example.com, include at least these SANs:

DNS:edge.example.com
DNS:*.t.edge.example.com

A wildcard for *.edge.example.com is not part of the CE hostname model. CE needs the base engine host and the .t.<engine-host> tunnel wildcard.

The engine loads certs.static.cert_file and certs.static.key_file at startup. It checks file modification times on later TLS handshakes and reloads the pair when either file changes. Certificate sidecars should write new files next to the live files and then rename them into place.

When the engine runs in Docker with bind-mounted certificate files, make the private key readable by the engine process without making it world-readable. A common pattern is to run the engine container with a dedicated host UID/GID and write key.pem with mode 0600 for that same UID.

Minimal Configuration

The minimal CE configuration includes an engine host, TLS listener, static certificate provider, and JWT authentication:

engine:
  host: edge.example.com
  log_level: info
tls:
  enabled: true
  listen_addr: ":443"
certs:
  static:
    enabled: true
    cert_file: /etc/rstream/tls/cert.pem
    key_file: /etc/rstream/tls/key.pem
auth:
  jwt:
    enabled: true
    token_jwt_secret: "<secret>"

In production, keep secrets in environment variables, process-manager secrets, or container secrets instead of YAML:

RSTREAM_ENGINE_AUTH__JWT__TOKEN_JWT_SECRET=...
RSTREAM_ENGINE_CERTS__STATIC__CERT_FILE=/etc/rstream/tls/cert.pem
RSTREAM_ENGINE_CERTS__STATIC__KEY_FILE=/etc/rstream/tls/key.pem

Compose Baseline

The public image is rstream/rstream-engine-ce. A production Compose service should mount configuration and TLS material, keep the JWT secret outside the YAML file, and bind metrics deliberately:

services:
  engine:
    image: rstream/rstream-engine-ce:latest
    command: ["--config", "/etc/rstream/config.yaml"]
    user: "${RSTREAM_RUNTIME_UID}:${RSTREAM_RUNTIME_GID}"
    env_file: [".env"]
    ports:
      - "80:80"
      - "443:443"
      - "127.0.0.1:9090:9090"
    volumes:
      - "./config.yaml:/etc/rstream/config.yaml:ro"
      - "./tls:/etc/rstream/tls:ro"

The matching environment file usually carries deployment-specific bind addresses and secrets:

RSTREAM_ENGINE_AUTH__JWT__TOKEN_JWT_SECRET=...
RSTREAM_ENGINE_CERTS__STATIC__CERT_FILE=/etc/rstream/tls/cert.pem
RSTREAM_ENGINE_CERTS__STATIC__KEY_FILE=/etc/rstream/tls/key.pem
RSTREAM_ENGINE_TLS__LISTEN_ADDR=[::]:443
RSTREAM_ENGINE_HTTP__LISTEN_ADDR=[::]:80
RSTREAM_ENGINE_METRICS__PROMETHEUS__LISTEN_ADDR=127.0.0.1:9090
RSTREAM_RUNTIME_UID=1000
RSTREAM_RUNTIME_GID=1000

If Prometheus binds to a non-loopback address inside the container, configure RSTREAM_ENGINE_METRICS__PROMETHEUS__BEARER_TOKEN and send that bearer token from the scraper.

Published TCP networking

When published TCP is enabled, open the configured ephemeral range in both the host firewall and the infrastructure firewall. The dynamic ingress backend binds only ports assigned to active tunnels.

Use host networking for a containerized engine. Publishing the complete range through Docker would install and reserve every mapping before a tunnel exists, defeating dynamic listener allocation:

services:
  engine:
    network_mode: host

With host networking, bind Prometheus to loopback and remove the Compose ports section. Reserved TCP addresses are a hosted Control plane feature and are not available in CE.

On Linux, the tproxy backend can receive the whole range through one transparent listener instead. Select a listener outside the public range:

published_tcp:
  enabled: true
  hostname: tcp.edge.example.com
  backend: tproxy
  ephemeral_range:
    start: 20000
    end: 29999
  tproxy:
    listen_addresses:
      - 0.0.0.0:30000

The engine process needs CAP_NET_ADMIN to set IP_TRANSPARENT. A container still uses host networking and adds that capability:

services:
  engine:
    network_mode: host
    cap_add:
      - NET_ADMIN

Configure policy routing and interception on the host before starting the engine. For an IPv4 range arriving on eth0, an iptables setup is:

ip rule add fwmark 1/1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 20000:29999 \
  -j TPROXY --on-port 30000 --tproxy-mark 1/1

Use the actual public interface and range, and configure equivalent IPv6 policy routing when the deployment exposes IPv6. nftables can provide the same TPROXY rule. Keep the internal listener port closed externally; only the published range belongs in host and provider firewalls.

The engine validates its listener and fails startup when transparent sockets are unavailable. It does not install or remove routing rules and never falls back to dynamic. This keeps host networking under the deployment system's ownership.

Agent Context

Agents connect to the base engine host. For edge.example.com, configure the agent engine as edge.example.com:443:

version: 1
defaults:
  context:
    name: self-hosted-ce
contexts:
  - name: self-hosted-ce
    engine: edge.example.com:443
    auth:
      token:
        storage:
          kind: inline
          value: "<jwt-signed-with-engine-secret>"

The token is an HS256 JWT signed with auth.jwt.token_jwt_secret. Give it an expiration and rotate it through the same secret-management path used for other service credentials.

CE Boundary

The public self-hosted deployment documented here is CE. The Compose shape above runs the standalone CE engine with direct JWT agent authentication, static TLS files, Prometheus metrics, and live Engine API state.

Use hosted rstream, Enterprise, or Integrator Edition when the runtime must enforce managed project controls, certificate-backed authentication, browser-based access flows, network access policies, persisted operational history, automatic certificate management, or partner-operated customer deployments.