Skip to content

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:4222 and coredata-dpkd.dp.mbari.org:4222
  • OBS WebSocket servers: rc-teleops.rc.mbari.org:4455 and dp-teleops.rc.mbari.org:4455
  • Each OBS instance must have scenes named MainOnly and FeedOffline defined before the detector is started.

Starting all services

docker compose up -d --build

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

docker compose down

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:

docker compose build
docker compose up -d

Service configuration

Environment variables live in compose.yaml. See Configuration for the full reference. Key per-service variables:

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:

docker compose up -d redis rc-navproc_collector rc-dive-detector frontend

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:

  1. Drop cert files into certs/. The Caddyfile expects certs/dashboard.crt (full-chain PEM) and certs/dashboard.key (PEM-encoded private key). See certs/README.md. Both files are gitignored.

  2. Set the hostname. Edit Caddyfile and replace dashboard.internal.mbari.org with the hostname this server answers to. Make sure DNS resolves that hostname to the docker host.

  3. Match ORIGIN to the hostname. Edit the frontend service in compose.yaml:

environment:
  - ORIGIN=https://dashboard.internal.mbari.org

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.

  1. Bring it up.
docker compose up -d --build frontend caddy

Caddy will reload the Caddyfile on container restart. Logs land in docker compose logs -f caddy.

  1. (Optional) Re-enable SvelteKit's CSRF origin check. With ORIGIN set, the check that we disabled earlier in svelte.config.js will pass cleanly. Flip kit.csrf.checkOrigin back to true if you want that extra layer.

Renewing or rotating certs

Drop the new cert/key files into certs/ (same filenames) and:

docker compose restart caddy

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:

ports:
  - "3000:3000"

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:

docker compose exec rc-dive-detector uv run backend/redis_dump.py rcsn

See Redis Tools for more options.