SmartSpectra C++ SDK
Measure human vitals from video with SmartSpectra C++ SDK.
Loading...
Searching...
No Matches
docker Directory Reference

Detailed Description

Docker Compose Deployment

This directory contains Docker configuration files for deploying SmartSpectra OnPrem using Docker Compose.

Quick Start

Option A: Build from Source

# 1. Copy configuration files
cp docker-compose.example.yml docker-compose.yml
cp .env.example .env # Optional: Edit .env to customize
# 2. Build images (first time only)
docker compose build
# 3. Start all services
docker compose up
# 4. Access the dashboard
# Open http://localhost:8080 in your browser
# 5. Stop services
docker compose down

Option B: Use Pre-built Images

If you received a pre-built image tar file:

# 1. Load the images
docker load -i smartspectra-images.tar
# 2. Copy the example compose file
cp docker-compose.example.yml docker-compose.yml
# 3. Start all services
docker compose up -d
# 4. Access the dashboard
# Open http://localhost:8080 in your browser

Prerequisites

  • Docker 20.10+ with Docker Compose V2 plugin installed
  • Camera device available at /dev/video0 (or customize in docker-compose.yml)
  • At least 4GB RAM available
  • At least 10GB disk space

Install Docker (Ubuntu 22.04)

# Install Docker (includes Docker Compose V2)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Verify Docker Compose V2 is installed
docker compose version
# Add your user to docker group (logout/login required)
sudo usermod -aG docker $USER

Architecture

┌──────────────────────────────────────────────────────────┐
│ nginx (Optional) │
│ HTTPS Reverse Proxy │
│ ports: 443, 80 │
└──────────────────────┬───────────────────────────────────┘
┌──────────────┴──────────────┐
│ │
┌───────▼────────┐ ┌─────────▼─────────────────┐
│ Frontend │ │ Browser (User) │
│ (React + Node) │ │ http://localhost:5173 │
│ Gateway │ └────────────────────────────┘
│ :8080, :5173 │ ▲
└───────┬────────┘ │ WebSocket
│ Pub commands │
│ Sub metrics │
┌───────▼──────────────────────────────────────────────────┐
│ Redis :6379 │
│ Channels: │
│ • physiology:metrics:core (from server) │
│ • physiology:metrics:edge (from server) │
│ • physiology:command_queue (from gateway) │
└────┬──────────────────────────────┬────────────────────┬─┘
│ Sub metrics │ Sub commands │
│ │ │
┌────▼─────────────────┐ ┌────────▼───────────────┐ │
│ physiology_server │ │ Python Backend │ │
│ :50051 │◄──┤ (Command Bridge) │ │
│ + grpc_core_server │ │ • Monitors commands │ │
│ :50052 │ │ • Calls gRPC │ │
│ • Publishes metrics │ │ • Saves metrics │ │
└──────────────────────┘ └────────────────────────┘ │
Pub metrics ◄─┘

Data Flow:

  1. User → Frontend Gateway: WebSocket (commands like "start recording")
  2. Frontend → Redis: Publishes commands to command_queue
  3. Python Backend → Redis: Subscribes to command_queue
  4. Python Backend → physiology_server: gRPC calls (StartRecording, etc.)
  5. physiology_server → Redis: Publishes metrics to metrics:core/edge
  6. Frontend Gateway → Redis: Subscribes to metrics channels
  7. Frontend Gateway → Browser: WebSocket (real-time metrics)

Services

1. Redis

  • Purpose: Pub/sub backend for metrics streaming and command queue
  • Image: redis:7-alpine
  • Port: 6380:6379 (mapped to 6380 on host to avoid conflict with system Redis)
  • Internal Port: 6379 (services connect using redis:6379)
  • Health Check: redis-cli ping
  • Channels (with default physiology:metrics prefix):
    • physiology:metrics:core_metrics - Core metrics from physiology_server
    • physiology:metrics:edge_metrics - Edge metrics from physiology_server
    • physiology:metrics:command_queue - Commands from frontend gateway
    • physiology:metrics:recording_state - Recording state updates
  • ⚠️ CRITICAL: All services must use the same Redis key prefix (default: physiology:metrics)

2. Physiology Server

  • Purpose: Core processing and metrics generation
  • Build: From local .deb and .whl files
  • Ports: 50051 (gRPC API), 50052 (internal Core gRPC)
  • Requires: Camera device, grpc_core_server.py volume mount
  • Function: Publishes metrics to Redis, accepts gRPC commands

3. Python Backend

  • Purpose: Command bridge between UI and physiology_server
  • Build: From local .whl files + Python samples
  • Script: redis_ipc_metrics_saving_client.py
  • Function:
    • Monitors Redis command_queue for UI commands (async, non-blocking)
    • Forwards commands to physiology_server via gRPC
    • Collects metrics from Redis pub/sub
    • Saves metrics to JSONL and JSON files
  • Critical: Without this service, UI commands (Start/Stop Recording) won't work

4. Frontend

  • Purpose: React dashboard + WebSocket gateway
  • Build: From typescript/samples/react-dashboard
  • Ports: 8080 (gateway), 5173 (Vite dev server)
  • Serves:
    • WebSocket API at /ws (metrics + commands)
    • MJPEG HUD stream at /hud.mjpg
    • Health check at /api/health
    • React dashboard static files
  • Function: Bridges browser WebSocket to Redis pub/sub

5. Nginx (Optional)

  • Purpose: HTTPS reverse proxy
  • Image: nginx:alpine
  • Ports: 443 (HTTPS), 80 (HTTP redirect)
  • Proxies: All traffic to frontend gateway

Configuration

Environment Variables

The easiest way to configure the deployment is using a .env file:

# 1. Copy the example file
cp .env.example .env
# 2. Edit values (optional - defaults work for most cases)
nano .env
# 3. Start services - they'll automatically use .env values
docker compose up

Key variables in .env:

# ⚠️ CRITICAL: Redis prefix - must be the same for ALL services
REDIS_PREFIX=physiology:metrics
# Redis connection (internal Docker network)
REDIS_HOST=redis
REDIS_PORT=6379
# Camera configuration
CAMERA_DEVICE=/dev/video0
INPUT_TRANSFORM_MODE=ccw90
# Node environment
NODE_ENV=production # or 'development'
# Port mappings (change if conflicts exist)
REDIS_HOST_PORT=6380
GATEWAY_HOST_PORT=8080

Why use .env?

  • Single source of truth - change REDIS_PREFIX once, applies to all services
  • Prevents configuration drift - no need to update 3 places in docker-compose.yml
  • Easy to review - see all config in one file
  • Version control friendly - commit .env.example, gitignore .env
  • Avoids prefix mismatches - the #1 cause of "UI commands don't work" issues

Without .env file: Docker Compose uses built-in defaults (${VAR:-default} syntax), which work for most deployments.

Customizing docker-compose.yml

Change Camera Device

physiology-server:
devices:
- /dev/video1:/dev/video0 # Use camera 1 instead of 0

Or use privileged mode to access all devices:

physiology-server:
privileged: true # Access all devices (less secure)

Enable Redis Password

redis:
command: redis-server --requirepass ${REDIS_PASSWORD}
physiology-server:
command: >
physiology_server
--redis_password=${REDIS_PASSWORD}
# ... other flags

Change Ports

frontend:
ports:
- "9090:8080" # Gateway on port 9090
- "3000:5173" # Vite on port 3000

Add Resource Limits

physiology-server:
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '1.0'
memory: 2G

Building Images

Build All Services

docker compose build

Build Individual Service

# Physiology server
docker compose build physiology-server
# Frontend
docker compose build frontend

No-Cache Build

docker compose build --no-cache

Running Services

Start All Services (Foreground)

docker compose up

Start All Services (Background)

docker compose up -d

Start Specific Service

docker compose up redis
docker compose up physiology-server
docker compose up frontend

View Logs

# All services
docker compose logs -f
# Specific service
docker compose logs -f physiology-server
# Last 100 lines
docker compose logs --tail=100 frontend

Check Service Status

docker compose ps

Stop Services

# Graceful stop
docker compose stop
# Force stop
docker compose kill
# Stop and remove containers
docker compose down
# Stop and remove volumes
docker compose down -v

Restart Service

docker compose restart physiology-server

Testing

1. Verify All Services Running

docker compose ps

Expected output:

NAME STATUS PORTS
smartspectra-redis Up (healthy) 0.0.0.0:6380->6379/tcp
smartspectra-physiology Up (healthy) 0.0.0.0:50051-50052->50051-50052/tcp
smartspectra-python-backend Up
smartspectra-frontend Up (healthy) 0.0.0.0:5173->5173/tcp, 0.0.0.0:8080->8080/tcp

2. Test Redis Connection

docker exec smartspectra-redis redis-cli ping
# Expected: PONG

3. Test Physiology Server

# Check logs for "Ready to accept connections"
docker compose logs physiology-server | grep -i "ready"
# Test gRPC endpoint (if grpc_health_probe installed)
docker exec smartspectra-physiology grpc_health_probe -addr=:50051

4. Test Python Backend

# Check if running
docker compose ps python-backend
# Expected: Up
# Check logs
docker compose logs python-backend | tail -20
# Verify it's connected to Redis and physiology_server
docker compose logs python-backend | grep -i "connected\|subscrib"

5. Test Frontend Gateway

# Health check
curl http://localhost:8080/api/health
# Expected: {"status":"ok"}
# WebSocket (requires wscat)
npm install -g wscat
wscat -c ws://localhost:8080/ws

5. Test Dashboard

# Open in browser
xdg-open http://localhost:5173 # Linux
open http://localhost:5173 # macOS

6. End-to-End Test

  1. Open dashboard: http://localhost:5173
  2. Click "Start Recording" toggle in header
  3. Behind the scenes:
    • Gateway publishes command to Redis (command_queue)
    • Python backend receives command from Redis
    • Python backend calls physiology_server.StartRecording() via gRPC
    • physiology_server starts publishing metrics to Redis
    • Gateway forwards metrics to browser via WebSocket
  4. Verify metrics appearing in charts (heart rate, breathing, etc.)
  5. Verify HUD video stream shows camera feed
  6. Click "Stop Recording" and verify metrics stop updating
  7. Check metrics files created: ls -la recordings/metrics.jsonl

Troubleshooting

Container Won't Start

Check logs:

docker compose logs physiology-server

Common issues:

  • grpc_core_server.py not found → Check volume mount
  • Camera not found → Check device mapping or use privileged
  • Python import errors → Rebuild image with --no-cache

Redis Connection Refused

Symptoms:

  • redis.exceptions.ConnectionError
  • ECONNREFUSED redis:6379

Fixes:

  1. Wait for Redis to be healthy:

    docker compose ps redis
  2. Check network:

    docker network inspect smartspectra_smartspectra-net
  3. Verify hostname (use redis not localhost in containers)

Camera Not Found

Symptoms:

  • Failed to open video device
  • physiology_server logs show camera errors

Fixes:

  1. Check device exists on host:

    ls -l /dev/video*
  2. Verify permissions:

    # Host permissions
    sudo chmod 666 /dev/video0
    # Or add user to video group
    sudo usermod -aG video $USER
  3. Use privileged mode:

    physiology-server:
    privileged: true

UI Commands Don't Work (Start/Stop Recording)

Symptoms:

  • Clicking "Start Recording" does nothing or requires multiple presses
  • python-backend shows no command activity

Root Cause:

Redis key prefix mismatch between services.

Fix:

  1. Verify all services use the same prefix:

    docker compose logs physiology-server | grep "key_prefix"
    docker exec smartspectra-python-backend printenv REDIS_PREFIX
    docker exec smartspectra-frontend printenv REDIS_PREFIX
    # All should show: physiology:metrics
  2. If mismatched, update .env file:

    # Edit .env
    REDIS_PREFIX=physiology:metrics
  3. Recreate containers:

    docker compose down
    docker compose up -d

WebSocket Won't Connect

Symptoms:

  • Browser console: WebSocket connection failed
  • Dashboard shows "Disconnected"

Fixes:

  1. Check frontend is running:

    docker compose ps frontend
    curl http://localhost:8080/api/health
  2. Check browser console for exact error
  3. Verify Redis is accessible from frontend:

    docker exec smartspectra-frontend ping -c 1 redis

Port Already in Use

Symptoms:

  • bind: address already in use

Fixes:

  1. Find what's using the port:

    sudo lsof -i :6380 # Redis (host port)
    sudo lsof -i :50051 # physiology_server
    sudo lsof -i :8080 # Gateway
    sudo lsof -i :5173 # Vite dev server
  2. Redis Port Conflict (6379): The default compose file uses port 6380 on the host to avoid conflicts with system Redis. If you still have conflicts:

    redis:
    ports:
    - "6381:6379" # Use a different host port
  3. Stop the conflicting service or change ports in docker-compose.yml

Out of Disk Space

Symptoms:

  • no space left on device

Fixes:

# Clean up unused containers/images
docker system prune -a
# Remove old volumes
docker volume prune
# Check disk usage
docker system df

Exporting Docker Images for Distribution

To create pre-built Docker images for offline/air-gapped deployment:

# Export all images to a single tar file
./export_docker_images.sh [output_file]
# Example
./export_docker_images.sh ./smartspectra-images.tar

This will:

  1. Build all Docker images from docker-compose.example.yml
  2. Pull the Redis image
  3. Export everything to a single tar file
  4. Create a README with loading instructions

Default output: smartspectra-images.tar (~1.5-2 GB)

Loading on target system:

docker load -i smartspectra-images.tar

Use case: Distribute to customers without requiring them to build from source or have internet access.

Production Deployment

For production deployment, make these changes:

1. Use Production Mode

frontend:
environment:
- NODE_ENV=production
command: node backend/server.js # Don't use dev mode

2. Enable Restart Policies

all-services:
restart: always # or 'unless-stopped'

3. Add Logging

physiology-server:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

4. Use Redis Password

redis:
command: redis-server --requirepass ${REDIS_PASSWORD}

5. Set Resource Limits

physiology-server:
deploy:
resources:
limits:
memory: 4G

6. Monitor Services

# Check health regularly
watch docker compose ps
# Monitor logs
docker compose logs -f

Scaling

Frontend (Horizontal Scaling)

# Run 3 frontend instances
docker compose up --scale frontend=3 -d

Note: For production, you'd add a load balancer (e.g., nginx, HAProxy) to distribute traffic.

physiology-server (NO Scaling)

⚠️ Cannot scale physiology-server - camera device conflict

For multiple cameras, run separate stacks:

# Camera 0
docker compose -p cam0 up -d
# Camera 1 (different config)
CAMERA_DEVICE=/dev/video1 REDIS_PREFIX=physiology_cam1 \
docker compose -p cam1 up -d

Development vs Production

Development Setup

# docker-compose.dev.yml
services:
physiology-server:
volumes:
- ./grpc_core_server.py:/app/grpc_core_server.py # Hot reload
frontend:
command: npm run dev # Vite hot reload
volumes:
- ./typescript/samples/react-dashboard/src:/app/src

Run:

docker compose -f docker-compose.yml -f docker-compose.dev.yml up

Production Setup

# docker-compose.prod.yml
services:
physiology-server:
restart: always
frontend:
environment:
- NODE_ENV=production
command: node backend/server.js

Run:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Backup and Restore

Backup Volumes

# Backup Redis data
docker run --rm \
-v smartspectra_redis-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/redis-backup.tar.gz /data
# Backup recordings
docker run --rm \
-v $(pwd)/recordings:/recordings \
-v $(pwd):/backup \
alpine tar czf /backup/recordings-backup.tar.gz /recordings

Restore Volumes

# Restore Redis data
docker run --rm \
-v smartspectra_redis-data:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/redis-backup.tar.gz -C /

Uninstall

# Stop and remove all containers
docker compose down
# Remove volumes (CAUTION: deletes all data)
docker compose down -v
# Remove images
docker rmi smartspectra/physiology-server:latest
docker rmi smartspectra/frontend:latest
docker rmi redis:7-alpine

Further Reading

  • Main README - General OnPrem documentation
  • QUICKSTART - Native installation guide
  • TypeScript Guide - Frontend development
  • Redis IPC Guide - Redis configuration
  • Examples Guide - Python client examples