Deploying Protocol Doors
The compatibility doors (see Compatibility & Doors) default to 127.0.0.1 so an unauthenticated port is never accidentally exposed. To let another container, host, or pod reach a door, you do three things: (1) enable the door, (2) override its bind address, (3) publish the port. This page is the whole story for Docker, Kubernetes, and bare-metal clusters.
Every door honors
RELATA_<DOOR>_BINDas a plain override on every profile — no license needed. The HTTP and gRPC listeners bind0.0.0.0automatically on theserverandclusterprofiles; the 8 compat doors default to loopback on every profile and opt up only when you say so.
The 60-second recipe
Pick the doors you actually use and copy the pattern. The bearer token is the password for every protocol.
# Enable Mongo + Postgres + Redis, bind all to 0.0.0.0 so other hosts/containers can reach them
RELATA_BEARER_TOKEN=change-me \
RELATA_MONGO_ENABLE=true RELATA_MONGO_BIND=0.0.0.0 \
RELATA_PG_ENABLE=true RELATA_PG_BIND=0.0.0.0 \
RELATA_REDIS_ENABLE=true RELATA_REDIS_BIND=0.0.0.0 \
relata serveIf RELATA_BEARER_TOKEN is set you can drop the explicit _ENABLE=true lines — every door auto-enables on a token. Keep the _BIND=0.0.0.0 overrides; they're what makes the door reachable off-loopback. (pgwire is the one exception — it auto-starts the moment a token is present, no enable flag needed, and it's fail-closed without a token.)
Docker — publish every door you enable
RELATA_<DOOR>_BIND=0.0.0.0 makes the door listen on all interfaces inside the container; you still need -p to publish the port to the host. A door enabled but not -p-published is unreachable from outside the container.
docker run -d \
-p 9090:9090 `# HTTP REST (always on)` \
-p 5433:5433 `# Postgres / pgvector` \
-p 27017:27017 `# MongoDB wire` \
-p 6379:6379 `# Redis RESP` \
-p 7474:7474 `# Neo4j HTTP Cypher` \
-p 7687:7687 `# Neo4j Bolt` \
-p 8123:8123 `# ClickHouse HTTP` \
-p 9000:9000 `# ClickHouse native TCP` \
-p 9191:9191 `# S3-compatible` \
-p 50051:50051 `# gRPC` \
-p 8815:8815 `# Arrow Flight` \
-e RELATA_PROFILE=server \
-e RELATA_BEARER_TOKEN=change-me \
-e RELATA_MONGO_BIND=0.0.0.0 \
-e RELATA_PG_BIND=0.0.0.0 \
-e RELATA_REDIS_BIND=0.0.0.0 \
-e RELATA_S3_BIND=0.0.0.0 \
-e RELATA_FLIGHT_ENABLE=true \
-e RELATA_FLIGHT_BIND=0.0.0.0 \
-v "$PWD/relata-data:/data/relata" \
--name relata ghcr.io/relatadb/relata:2.0.0Only publish the doors you actually use — every published port is attack surface. To bind to a specific interface instead of all interfaces, use the host IP (e.g. RELATA_MONGO_BIND=10.0.0.5 or -p 10.0.0.5:27017:27017).
Kubernetes — declare containerPort + the env pair
A door enabled without a matching containerPort is silent: it binds inside the pod but no Service routes to it. Declare every door you use.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: relata-db
spec:
serviceName: relata-db
replicas: 1
template:
spec:
containers:
- name: relata-db
image: ghcr.io/relatadb/relata:2.0.0
ports:
- { containerPort: 9090, name: http }
- { containerPort: 5433, name: pgwire }
- { containerPort: 27017, name: mongo }
- { containerPort: 6379, name: redis }
- { containerPort: 7474, name: neo4j-http }
- { containerPort: 7687, name: bolt }
- { containerPort: 8123, name: clickhouse-http }
- { containerPort: 9000, name: clickhouse-native }
- { containerPort: 9191, name: s3 }
- { containerPort: 50051, name: grpc }
- { containerPort: 8815, name: flight }
env:
- { name: RELATA_PROFILE, value: server }
- { name: RELATA_BEARER_TOKEN, valueFrom: { secretKeyRef: { name: relata-secrets, key: admin-token } } }
# Bind the doors you expose to 0.0.0.0 (default is loopback).
- { name: RELATA_MONGO_BIND, value: "0.0.0.0" }
- { name: RELATA_PG_BIND, value: "0.0.0.0" }
- { name: RELATA_REDIS_BIND, value: "0.0.0.0" }
- { name: RELATA_S3_BIND, value: "0.0.0.0" }
- { name: RELATA_FLIGHT_ENABLE, value: "true" }
- { name: RELATA_FLIGHT_BIND, value: "0.0.0.0" }
# ...add one BIND per door you publish...Then expose each door through a Service. A single ClusterIP for internal callers, or a LoadBalancer/Ingress per protocol for external clients (most compat protocols aren't HTTP, so Ingress usually isn't the right fit — prefer LoadBalancer or NodePort for Mongo/Postgres/Redis/Bolt/ClickHouse/S3).
apiVersion: v1
kind: Service
metadata:
name: relata-mongo
spec:
selector: { app: relata-db }
type: LoadBalancer
ports:
- { port: 27017, targetPort: 27017, name: mongo }Repeat per protocol. For a full worked example (PVC, readiness/liveness, multi-tenant mode), see Kubernetes Deployment.
Full port reference (all 11 networked surfaces)
| Door | Enable flag | Port | Bind var | Default bind |
|---|---|---|---|---|
| HTTP REST | always on | 9090 | RELATA_HTTP_BIND | 0.0.0.0 on server/cluster, 127.0.0.1 on free |
| gRPC | always on | 50051 | RELATA_GRPC_BIND | 0.0.0.0 on server/cluster, 127.0.0.1 on free |
| Postgres + pgvector | token required | 5433 | RELATA_PG_BIND | 127.0.0.1 |
| MongoDB | RELATA_MONGO_ENABLE | 27017 | RELATA_MONGO_BIND | 127.0.0.1 |
| Redis | RELATA_REDIS_ENABLE | 6379 | RELATA_REDIS_BIND | 127.0.0.1 |
| Neo4j HTTP | RELATA_NEO4J_ENABLE | 7474 | RELATA_NEO4J_BIND | 127.0.0.1 |
| Bolt | RELATA_BOLT_ENABLE | 7687 | RELATA_BOLT_BIND | 127.0.0.1 |
| ClickHouse HTTP | RELATA_CLICKHOUSE_ENABLE | 8123 | RELATA_CLICKHOUSE_BIND | 127.0.0.1 |
| ClickHouse native | RELATA_CLICKHOUSE_NATIVE_ENABLE | 9000 | RELATA_CH_NATIVE_BIND | 127.0.0.1 |
| S3-compatible | RELATA_S3_ENABLE | 9191 | RELATA_S3_BIND | 127.0.0.1 |
| Arrow Flight | RELATA_FLIGHT_ENABLE | 8815 | RELATA_FLIGHT_BIND | 127.0.0.1 |
MCP and SPARQL ride on the HTTP listener (/mcp, /sparql) — no separate port.
Cluster mode — one door port per node
In a multi-node cluster (see Cluster Setup), every node defaults to the same door ports. If you run three nodes on one host for testing, two of them will silently WARN-fail the door binds and lose ⅔ of your door capacity. Either:
- Run one node per host (production), or
- Give each node a distinct
RELATA_<DOOR>_PORTin test, or - Disable the doors on
reader/indexernodes (RELATA_MONGO_ENABLE=false, etc.) and route door traffic only tocoordinator/writernodes.
Doors are stateless front-ends over the same governed store — any node can serve any door; writes funnel through the planner regardless of which node received them.
Security checklist before production
-
RELATA_BEARER_TOKENis a strong random value (e.g.openssl rand -hex 32), notchange-me. - Only the doors you use have
RELATA_<DOOR>_BIND=0.0.0.0; the rest stay on loopback. - Only the doors you use are
-ppublished / have aService. - TLS is terminated either by Relata (
RELATA_TLS_CERT/RELATA_TLS_KEY,RELATA_PG_TLS_CERT/RELATA_PG_TLS_KEY,RELATA_GRPC_TLS_CERT/_KEY) or by a reverse proxy / sidecar in front (RELATA_PLAINTEXT_OK=trueonly if you terminate TLS upstream). - For S3 in production, set
RELATA_S3_SECRET_KEYto a dedicated SigV4 secret (defaults to the bearer token otherwise) and leaveRELATA_S3_ALLOW_PLAINTEXTunset. -
RELATA_TENANCY_MODE=multiis set only if you actually want per-tenant isolation (it's the one real cluster-only gate).
See also
- Compatibility & Doors — connection strings and 3-step quickstarts per protocol
- Protocol Compatibility (reference) — per-protocol semantics and limits
- Kubernetes Deployment — full Helm/StatefulSet walkthrough
- Cluster Setup — multi-node mechanics and gotchas
- Environment Variables — canonical env-var reference