Skip to main content

Deployment

note

This guide covers single-host deployment in detail; for cluster deployments see Containers & Kubernetes below.

This guide covers deploying OpenRobOps for production use.

Architecture Components

A production OpenRobOps deployment consists of four services:

ServiceTechnologyPortNotes
Web AppMeteor 3 (Node.js)3000UI, REST API, WebSocket pub/sub
MongoDBMongoDB3001Primary data store
MQTT BrokerMosquitto + go-auth1883 / 9001Robot agent communication
IngestNode.js (Babel)Telemetry processing

Docker Compose Setup

The MQTT broker runs via Docker Compose. From the mqtt/ directory:

docker compose up -d

The docker-compose.yml uses the ghcr.io/openrobops/mosquitto-go-auth image, which provides Mosquitto with a MongoDB-backed authentication plugin. The container runs with network_mode: host so the auth plugin can reach Meteor's MongoDB on localhost:3001.

Mosquitto Configuration

The broker is configured in mqtt/mosquitto/mosquitto.conf:

  • Port 1883 — standard MQTT protocol
  • Port 9001 — WebSocket protocol (for browser-based clients)
  • Authentication — uses the mosquitto-go-auth plugin querying the mqtt_credentials collection in MongoDB
  • Password hashing — PBKDF2 with SHA-512, 100,000 iterations, 16-byte salt

Configuration with Terraform

OpenRobOps uses Terraform to generate settings.json files for the web app and ingest service. This ensures consistent configuration across services.

# Preview settings changes
./scripts/generate-settings.sh --plan

# Generate/apply settings
./scripts/generate-settings.sh

# Force-rotate all generated secrets (invalidates connected robots)
./scripts/generate-settings.sh --clean

Secrets (the MQTT master password, credential encryption key, peer key, and robot API key) are Terraform random_* resources persisted in state — re-running the script preserves them; only --clean rotates them.

Key Configuration Variables

Defined in terraform/variables.tf:

VariableDescription
hostnameHost used in generated service URLs
mqtt_port / mqtt_websocket_portMQTT broker ports
mqtt_master_usernameUsername for the ingest service's MQTT superuser
mongo_url / mongo_dbMongoDB connection for ingest
peer_client_urlURL ingest uses to reach the web app's peer API
oauth_google_client_id / oauth_google_secretGoogle OAuth credentials
oauth_github_client_id / oauth_github_secretGitHub OAuth credentials
smtp_urlSMTP server for passwordless email login
admin_emailsEmails auto-granted the admin role on first login (see Access Control)
upstream_*Upstream forwarding settings (see Upstream Forwarding)

Override defaults in terraform/local.tfvars. Secrets such as mqtt_master_password and the credential encryption key are not variables — they are auto-generated by Terraform.

Environment Overview

Settings Files

FileUsed ByGenerated By
app/settings.jsonMeteor app serverTerraform
ingest/settings.jsonIngest serviceTerraform
terraform/local.tfvarsTerraformManual (user creates)

Ports Reference

PortProtocolService
3000HTTP/WSWeb app (Meteor)
3001MongoDB wireMongoDB (Meteor-managed)
1883MQTTMosquitto broker
9001WebSocketMosquitto broker (WS)

Running in Production

Starting Services

The quickest way to launch all three services locally is the helper script, which opens a tmux session named oro with one tab per service (each running its run.sh):

./scripts/start-local-env.sh

This requires tmux. Stop everything with ./scripts/stop-local-env.sh.

To run the components independently (or on separate hosts), start each one directly:

# MQTT broker
cd mqtt && ./run.sh

# Web app + MongoDB
cd app && ./run.sh

# Ingest service
cd ingest && ./run.sh

Monitoring

  • Web app logs — stdout from the Meteor process
  • Ingest logs — stdout from the Node.js process, includes MQTT connection status
  • MQTT broker logs — Docker container logs (docker compose logs -f)

Settings Validation

The web app validates settings.json on startup and refuses to start if anything is invalid, reporting every problem at once (Invalid application settings — refusing to start: ...). The checked invariants:

  • the mqtt settings object is present
  • mqtt.credentialEncryptionKey is exactly 64 hex characters (an AES-256-GCM key)
  • mqtt.brokers is non-empty
  • mqtt.defaultBrokerId names one of the brokers

Terraform-generated settings always satisfy these — a validation failure usually means a hand-edited file. Note the ingest service is not covered by this check; it logs and continues instead.

Versions & Releases

The app and ingest are versioned and released together from a git tag (see RELEASING.md in the repo). The running version appears in the startup log and in the Settings sidebar footer. Telemetry relayed upstream carries an agentVersion stamped with a +oro-<version> suffix (see Upstream Forwarding).

Containers & Kubernetes

CI builds and publishes container images to GHCR on main and release tags: ghcr.io/openrobops/oro-app and ghcr.io/openrobops/oro-ingest (build cache also lives in GHCR). Local equivalents: scripts/build-app-image.sh, scripts/build-ingest-image.sh, and scripts/smoke-test-app-image.sh.

Kubernetes manifests ship in k8s/ (namespaces, deployments, services, ingress) for the app, ingest, and Mosquitto:

kubectl apply -f k8s/app/ -f k8s/ingest/ -f k8s/mosquitto/

Configuration differs from the single-host layout:

ComponentConfig source
AppMETEOR_SETTINGS env (the settings.json JSON) from a Secret; ROOT_URL from a ConfigMap
IngestSecret mounted as /app/settings.json
MosquittoConfig from its manifest/secret

Copy each secret.example.yaml to a real Secret with your generated values before applying. Container images run the same code as the local processes, so the settings content itself is unchanged.

:::caution Websocket timeouts The app serves Meteor DDP over long-lived websockets. Whatever fronts it must allow long-running connections: the sample ingress sets nginx's proxy-read-timeout/proxy-send-timeout to 3600s, but on GKE with a GCE-class ingress those annotations are silently ignored and the load balancer's 30-second default kills every session, leaving clients in a reconnect/resubscribe loop. On GKE, attach a BackendConfig with timeoutSec: 3600 to the oro-app Service instead (see the note in k8s/app/ingress.yaml). :::

Security Considerations

  • Change all default passwords generated by Terraform
  • Restrict MongoDB access to localhost or trusted networks
  • Use TLS termination (reverse proxy) in front of port 3000 for production
  • Configure OAuth with proper redirect URIs for your production domain
  • The MQTT broker should not be directly exposed to the public internet without TLS

Next Steps