Skip to main content
Container distribution is coming soon. Contact support@causeflow.ai for access to the Relay container image.

Resource requirements

The Relay is designed to be lightweight. Size your host or container task accordingly:
ResourceIdleUnder loadHard limit
CPU~128m< 500m0.5 CPU
Memory100–200 MB< 512 MB512 MB
Disk50 MB (image)
The Relay uses a database connection pool with a maximum of 5 connections per resource, so it has minimal impact on your database server.

Container security model

Regardless of how you deploy the Relay, the container is hardened by default:
Security controlValue
UserNon-root, UID 10001
FilesystemRead-only root filesystem
Linux capabilitiesAll dropped (--cap-drop ALL)
Privilege escalationDisabled (no-new-privileges)
Writable scratch space/tmp only, mounted as tmpfs
NetworkOutbound WSS/443 only
These controls are enforced by the container image itself and by the recommended run flags below. Do not remove them.

Docker

The simplest deployment option — a single docker run command with all security flags applied:
docker run -d \
  --name causeflow-relay \
  --restart unless-stopped \
  -v $(pwd)/relay-config.yaml:/app/relay-config.yaml:ro \
  -e RELAY_TOKEN=<your-relay-token> \
  -e TENANT_ID=<your-tenant-id> \
  -e PG_HOST=your-db-host \
  -e PG_DATABASE=yourdb \
  -e PG_USER=readonly_user \
  -e PG_PASSWORD=secret \
  --memory=512m \
  --cpus=0.5 \
  --read-only \
  --tmpfs /tmp:size=64m \
  --security-opt no-new-privileges \
  --cap-drop ALL \
  causeflow/relay:latest
Check startup logs:
docker logs causeflow-relay

Docker Compose

Use Docker Compose to run the Relay alongside your application stack. This is useful for local development or single-host deployments where the Relay runs next to the database.
docker-compose.yaml
services:
  causeflow-relay:
    image: causeflow/relay:latest
    restart: unless-stopped
    volumes:
      - ./relay-config.yaml:/app/relay-config.yaml:ro
    environment:
      RELAY_TOKEN: ${RELAY_TOKEN}
      TENANT_ID: ${TENANT_ID}
      PG_HOST: postgres
      PG_DATABASE: ${PG_DATABASE}
      PG_USER: ${PG_USER}
      PG_PASSWORD: ${PG_PASSWORD}
    mem_limit: 512m
    cpus: 0.5
    read_only: true
    tmpfs:
      - /tmp:size=64m
    security_opt:
      - no-new-privileges
    cap_drop:
      - ALL
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: ${PG_DATABASE}
      POSTGRES_USER: ${PG_USER}
      POSTGRES_PASSWORD: ${PG_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${PG_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
Store secrets in a .env file (never commit it) and reference them with ${VAR} in the Compose file.

Kubernetes

Deploy the Relay as a Deployment in your cluster. The Relay connects outbound over the cluster’s egress path — ensure your network policy allows outbound TCP to api.causeflow.ai:443.
relay-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: causeflow-relay
  namespace: causeflow
  labels:
    app: causeflow-relay
spec:
  replicas: 1
  selector:
    matchLabels:
      app: causeflow-relay
  template:
    metadata:
      labels:
        app: causeflow-relay
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        fsGroup: 10001
      containers:
        - name: relay
          image: causeflow/relay:latest
          resources:
            requests:
              cpu: "128m"
              memory: "200Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
          volumeMounts:
            - name: config
              mountPath: /app/relay-config.yaml
              subPath: relay-config.yaml
              readOnly: true
            - name: tmp
              mountPath: /tmp
          env:
            - name: RELAY_TOKEN
              valueFrom:
                secretKeyRef:
                  name: causeflow-relay-secret
                  key: relay-token
            - name: TENANT_ID
              valueFrom:
                secretKeyRef:
                  name: causeflow-relay-secret
                  key: tenant-id
            - name: PG_HOST
              value: "postgres-service.database.svc.cluster.local"
            - name: PG_DATABASE
              valueFrom:
                secretKeyRef:
                  name: causeflow-relay-secret
                  key: pg-database
            - name: PG_USER
              valueFrom:
                secretKeyRef:
                  name: causeflow-relay-secret
                  key: pg-user
            - name: PG_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: causeflow-relay-secret
                  key: pg-password
      volumes:
        - name: config
          configMap:
            name: causeflow-relay-config
        - name: tmp
          emptyDir:
            medium: Memory
            sizeLimit: 64Mi
Create the required Secret and ConfigMap before applying:
# Create the secret
kubectl create secret generic causeflow-relay-secret \
  --namespace causeflow \
  --from-literal=relay-token=<your-token> \
  --from-literal=tenant-id=<your-tenant-id> \
  --from-literal=pg-database=yourdb \
  --from-literal=pg-user=readonly_user \
  --from-literal=pg-password=secret

# Create the config
kubectl create configmap causeflow-relay-config \
  --namespace causeflow \
  --from-file=relay-config.yaml=./relay-config.yaml
Run exactly one Relay replica per tenant. The Relay holds a single WebSocket connection to the control plane — multiple replicas would create duplicate connections, not high availability. If you need redundancy, use a Deployment with replicas: 1 and a restartPolicy: Always.

ECS Fargate

Deploy the Relay as an ECS Fargate task in a private subnet with no inbound security group rules. The task needs outbound internet access (via NAT gateway or VPC endpoint) to reach api.causeflow.ai:443. Key settings for your ECS task definition:
{
  "family": "causeflow-relay",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "512",
  "taskRoleArn": "arn:aws:iam::<account-id>:role/causeflow-relay-task-role",
  "executionRoleArn": "arn:aws:iam::<account-id>:role/causeflow-relay-exec-role",
  "containerDefinitions": [
    {
      "name": "relay",
      "image": "causeflow/relay:latest",
      "essential": true,
      "readonlyRootFilesystem": true,
      "user": "10001",
      "secrets": [
        {
          "name": "RELAY_TOKEN",
          "valueFrom": "arn:aws:secretsmanager:<region>:<account>:secret:causeflow/relay-token"
        },
        {
          "name": "TENANT_ID",
          "valueFrom": "arn:aws:secretsmanager:<region>:<account>:secret:causeflow/tenant-id"
        },
        {
          "name": "PG_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:<region>:<account>:secret:causeflow/pg-password"
        }
      ],
      "environment": [
        { "name": "PG_HOST", "value": "your-rds-endpoint.rds.amazonaws.com" },
        { "name": "PG_DATABASE", "value": "yourdb" },
        { "name": "PG_USER", "value": "readonly_user" }
      ],
      "mountPoints": [
        {
          "sourceVolume": "relay-config",
          "containerPath": "/app/relay-config.yaml",
          "readOnly": true
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/causeflow/relay",
          "awslogs-region": "<region>",
          "awslogs-stream-prefix": "relay"
        }
      }
    }
  ]
}
Store relay-config.yaml in AWS Systems Manager Parameter Store or S3 and inject it at startup, or bake it into a custom image layer. Security group rules for the Fargate task:
DirectionProtocolPortDestinationPurpose
OutboundTCP443api.causeflow.aiControl plane WebSocket
OutboundTCP5432Your RDS security groupPostgreSQL
OutboundTCP27017Your MongoDB security groupMongoDB
InboundNone required

VM or bare metal

If you prefer to run the Relay directly on a virtual machine or bare metal server without Docker, the container image can be extracted and run as a standalone process. Contact support@causeflow.ai for guidance on non-container deployments. The configuration file and environment variable conventions are identical regardless of deployment method.