Deployment
The system is fully containerised. Docker Compose brings up Redis, two navigation collectors, two dive detectors, the LiveLink Slack notifier, the SvelteKit dashboard, and a Caddy TLS-terminating reverse proxy — eight services in total — with a single command.
Prerequisites
- Docker Engine 24+ and Docker Compose v2
- Network access to:
- NATS servers:
coredata-rcsn.rc.mbari.org:4222andcoredata-dpkd.dp.mbari.org:4222 - OBS WebSocket servers:
rc-teleops.rc.mbari.org:4455anddp-teleops.rc.mbari.org:4455 - Each OBS instance must have scenes named
MainOnlyandFeedOfflinedefined before the detector is started.
Starting all services
This starts six containers:
| Compose service | Container name | Description |
|---|---|---|
redis |
redis |
Shared data cache |
rc-navproc_collector |
navproc_collector_rcsn |
NATS → Redis for Rachel Carson |
dp-navproc_collector |
navproc_collector_dpkd |
NATS → Redis for David Packard |
rc-dive-detector |
dive_detector_rcsn |
Dive detection for Rachel Carson |
dp-dive-detector |
dive_detector_dpkd |
Dive detection for David Packard |
livelink_slackbot |
livelink_slackbot |
Posts Redis state changes to a Slack workflow webhook |
frontend |
dive_detector_frontend |
SvelteKit dashboard (HTTP on internal port 3000) |
caddy |
dive_detector_caddy |
TLS-terminating reverse proxy on ports 80/443 |
The dashboard is then available at
https://<configured-hostname> once you've placed the cert files (see
Enabling TLS below). Until then the caddy container
will fail to start because the cert files don't exist.
Viewing logs
# All services
docker compose logs -f
# A single service
docker compose logs -f rc-dive-detector
docker compose logs -f frontend
Stopping
Rebuilding images
After a code change in a single service, rebuild just that one:
docker compose up -d --build frontend
docker compose up -d --build rc-dive-detector dp-dive-detector
To rebuild everything:
Service configuration
Environment variables live in compose.yaml. See Configuration for the full reference. Key per-service variables:
navproc_collector
| Variable | Example | Description |
|---|---|---|
SHIP_NAME |
rcsn |
Ship identifier (NATS bucket name) |
SHIP_DOMAIN |
rc |
NATS domain suffix |
REDIS_HOST |
redis |
Redis hostname (Docker service name) |
POLL_INTERVAL |
10 |
Seconds between data pulls |
The collector's NATS key allow-list is in
backend/collector_keys.toml —
edits there require a collector rebuild.
dive_detector
| Variable | Example | Description |
|---|---|---|
SHIP_NAME |
rcsn |
Ship identifier (Redis key prefix) |
OBS_HOST |
rc-teleops.rc.mbari.org |
OBS WebSocket server hostname |
OBS_PORT |
4455 |
OBS WebSocket port |
OBS_PASSWORD |
(secret) | OBS WebSocket password, empty if no auth |
REDIS_HOST |
redis |
Redis hostname |
POLL_INTERVAL |
10 |
Seconds between Redis polls |
frontend
| Variable | Example | Description |
|---|---|---|
REDIS_HOST |
redis |
Redis hostname |
REDIS_PORT |
6379 |
Redis port |
NODE_ENV |
production |
Node environment |
Dockerfiles
docker/dive_detector.dockerfile
Builds the image for both dive-detector services. Based on
ghcr.io/astral-sh/uv:python3.13-trixie-slim. Dependencies are installed
from the lockfile with uv sync.
docker/navproc_collector.dockerfile
Builds the image for both navproc-collector services. Same base image
and dependency approach. The ADD /backend /app step automatically
includes collector_keys.toml in the image.
docker/frontend.dockerfile
Multi-stage Node 22 build. Stage 1 installs all deps and runs
npm run build to produce the SvelteKit Node bundle. Stage 2 contains
only production dependencies plus the build output, and runs
node build.
Running a single ship only
To run only the R/V Rachel Carson services plus the dashboard:
Enabling TLS
The compose stack includes a Caddy reverse
proxy in front of the SvelteKit container. Caddy terminates TLS on
ports 80/443 and proxies plain HTTP requests to frontend:3000 over
the internal redisnet network — the SvelteKit container is no longer
exposed externally.
To turn it on with an MBARI-issued cert:
-
Drop cert files into
certs/. The Caddyfile expectscerts/dashboard.crt(full-chain PEM) andcerts/dashboard.key(PEM-encoded private key). Seecerts/README.md. Both files are gitignored. -
Set the hostname. Edit
Caddyfileand replacedashboard.internal.mbari.orgwith the hostname this server answers to. Make sure DNS resolves that hostname to the docker host. -
Match
ORIGINto the hostname. Edit thefrontendservice incompose.yaml:
This tells SvelteKit that incoming requests should be treated as
HTTPS (so the session cookie picks up the Secure flag and links
resolve correctly), even though the request to the SvelteKit
container itself arrives over HTTP from Caddy.
- Bring it up.
Caddy will reload the Caddyfile on container restart. Logs land in
docker compose logs -f caddy.
- (Optional) Re-enable SvelteKit's CSRF origin check. With
ORIGINset, the check that we disabled earlier insvelte.config.jswill pass cleanly. Flipkit.csrf.checkOriginback totrueif you want that extra layer.
Renewing or rotating certs
Drop the new cert/key files into certs/ (same filenames) and:
Caddy picks them up without rebuilding.
Local debug without TLS
If you need direct HTTP access to the SvelteKit container (e.g. for
troubleshooting), uncomment the port mapping under the frontend
service in compose.yaml:
Re-comment it out when you're done so the only public surface is the Caddy-terminated HTTPS port.
Checking Redis data
Use the Redis dump tools to verify the navigation collector is
populating data. Run them on the host with uv installed:
# Static snapshot of all keys under rcsn:
uv run backend/redis_dump.py rcsn
# Live auto-refreshing TUI (terminal mode)
uv run backend/redis_dump_textual.py rcsn
# Or have it serve a browser-friendly TUI
uv run backend/redis_dump_textual.py rcsn --web --host 0.0.0.0 --port 8000
If Redis isn't exposed on the host, exec into one of the running
containers — they all have redis-cli-like access via Python:
See Redis Tools for more options.