Files
meshcore-hub/docker-compose.yml
T
Louis King 234e1ba39f Updates
2025-12-04 00:21:09 +00:00

352 lines
12 KiB
YAML

# MeshCore Hub - Docker Compose Configuration
#
# Usage:
# docker compose up # Start all core services (mqtt, collector, api, web)
# docker compose --profile interface-receiver up # Add real device receiver
# docker compose --profile interface-sender up # Add real device sender
# docker compose --profile mock up # Use mock device instead of real hardware
#
# Building locally (instead of pulling from registry):
# docker compose build # Build all services
# docker compose up --build # Build and start
#
# Available profiles:
# - interface-receiver: Interface in RECEIVER mode (real device)
# - interface-sender: Interface in SENDER mode (real device)
# - mock: Add mock interface for testing without hardware
# - migrate: Run database migrations
# - seed: Import seed data (node_tags.json, members.json)
#
# Core services (started by default, no profile needed):
# - mqtt: MQTT broker (Eclipse Mosquitto)
# - collector: Event collector and database storage
# - api: REST API server
# - web: Web dashboard
#
# Data Directory (DATA_HOME):
# The DATA_HOME environment variable controls where instance data is stored.
# Default: ./data (local) or /data (in containers)
#
# Structure:
# ${DATA_HOME}/
# └── collector/
# └── meshcore.db # SQLite database
#
# Seed Directory (SEED_HOME):
# The SEED_HOME environment variable controls where seed data files are read from.
# Default: ./seed (local) or /seed (in containers)
#
# Structure:
# ${SEED_HOME}/
# ├── node_tags.json # Node tag definitions
# └── members.json # Network member definitions
#
# Example seed files are provided in ./example/seed/
#
# To import seed data:
# docker compose --profile seed run --rm seed
services:
# ==========================================================================
# MQTT Broker - Eclipse Mosquitto
# ==========================================================================
mqtt:
image: eclipse-mosquitto:2
container_name: meshcore-mqtt
restart: unless-stopped
ports:
- "${MQTT_EXTERNAL_PORT:-1883}:1883"
- "${MQTT_WS_PORT:-9001}:9001"
volumes:
- ./etc/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
- mosquitto_data:/mosquitto/data
- mosquitto_log:/mosquitto/log
healthcheck:
test: ["CMD", "mosquitto_sub", "-t", "$$SYS/#", "-C", "1", "-i", "healthcheck", "-W", "3"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# ==========================================================================
# Interface Receiver - MeshCore device to MQTT bridge (events)
# ==========================================================================
interface-receiver:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-interface-receiver
profiles:
- interface-receiver
restart: unless-stopped
depends_on:
mqtt:
condition: service_healthy
devices:
- "${SERIAL_PORT:-/dev/ttyUSB0}:${SERIAL_PORT:-/dev/ttyUSB0}"
user: root # Required for device access
environment:
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- MQTT_HOST=${MQTT_HOST:-mqtt}
- MQTT_PORT=${MQTT_PORT:-1883}
- MQTT_USERNAME=${MQTT_USERNAME:-}
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
- SERIAL_PORT=${SERIAL_PORT:-/dev/ttyUSB0}
- SERIAL_BAUD=${SERIAL_BAUD:-115200}
- NODE_ADDRESS=${NODE_ADDRESS:-}
command: ["interface", "receiver"]
healthcheck:
test: ["CMD", "meshcore-hub", "health", "interface"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# ==========================================================================
# Interface Sender - MQTT to MeshCore device bridge (commands)
# ==========================================================================
interface-sender:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-interface-sender
profiles:
- interface-sender
restart: unless-stopped
depends_on:
mqtt:
condition: service_healthy
devices:
- "${SERIAL_PORT_SENDER:-/dev/ttyUSB1}:${SERIAL_PORT_SENDER:-/dev/ttyUSB1}"
user: root # Required for device access
environment:
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- MQTT_HOST=${MQTT_HOST:-mqtt}
- MQTT_PORT=${MQTT_PORT:-1883}
- MQTT_USERNAME=${MQTT_USERNAME:-}
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
- SERIAL_PORT=${SERIAL_PORT_SENDER:-/dev/ttyUSB1}
- SERIAL_BAUD=${SERIAL_BAUD:-115200}
- NODE_ADDRESS=${NODE_ADDRESS_SENDER:-}
command: ["interface", "sender"]
healthcheck:
test: ["CMD", "meshcore-hub", "health", "interface"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
# ==========================================================================
# Interface Mock Receiver - For testing without real devices
# ==========================================================================
interface-mock-receiver:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-interface-mock-receiver
profiles:
- mock
restart: unless-stopped
depends_on:
mqtt:
condition: service_healthy
environment:
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- MQTT_HOST=${MQTT_HOST:-mqtt}
- MQTT_PORT=${MQTT_PORT:-1883}
- MQTT_USERNAME=${MQTT_USERNAME:-}
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
- MOCK_DEVICE=true
- NODE_ADDRESS=${NODE_ADDRESS:-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef}
command: ["interface", "receiver", "--mock"]
healthcheck:
test: ["CMD", "meshcore-hub", "health", "interface"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# ==========================================================================
# Collector - MQTT subscriber and database storage
# ==========================================================================
collector:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-collector
restart: unless-stopped
depends_on:
mqtt:
condition: service_healthy
volumes:
# Mount data directory (contains collector/meshcore.db)
- ${DATA_HOME:-./data}:/data
environment:
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- MQTT_HOST=${MQTT_HOST:-mqtt}
- MQTT_PORT=${MQTT_PORT:-1883}
- MQTT_USERNAME=${MQTT_USERNAME:-}
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
- DATA_HOME=/data
# Explicitly unset to use DATA_HOME-based default path
- DATABASE_URL=
# Webhook configuration
- WEBHOOK_ADVERTISEMENT_URL=${WEBHOOK_ADVERTISEMENT_URL:-}
- WEBHOOK_ADVERTISEMENT_SECRET=${WEBHOOK_ADVERTISEMENT_SECRET:-}
- WEBHOOK_MESSAGE_URL=${WEBHOOK_MESSAGE_URL:-}
- WEBHOOK_MESSAGE_SECRET=${WEBHOOK_MESSAGE_SECRET:-}
- WEBHOOK_CHANNEL_MESSAGE_URL=${WEBHOOK_CHANNEL_MESSAGE_URL:-}
- WEBHOOK_CHANNEL_MESSAGE_SECRET=${WEBHOOK_CHANNEL_MESSAGE_SECRET:-}
- WEBHOOK_DIRECT_MESSAGE_URL=${WEBHOOK_DIRECT_MESSAGE_URL:-}
- WEBHOOK_DIRECT_MESSAGE_SECRET=${WEBHOOK_DIRECT_MESSAGE_SECRET:-}
- WEBHOOK_TIMEOUT=${WEBHOOK_TIMEOUT:-10.0}
- WEBHOOK_MAX_RETRIES=${WEBHOOK_MAX_RETRIES:-3}
- WEBHOOK_RETRY_BACKOFF=${WEBHOOK_RETRY_BACKOFF:-2.0}
command: ["collector"]
healthcheck:
test: ["CMD", "meshcore-hub", "health", "collector"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# ==========================================================================
# API Server - REST API for querying data and sending commands
# ==========================================================================
api:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-api
restart: unless-stopped
depends_on:
mqtt:
condition: service_healthy
collector:
condition: service_started
ports:
- "${API_PORT:-8000}:8000"
volumes:
# Mount data directory (uses collector/meshcore.db)
- ${DATA_HOME:-./data}:/data
environment:
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- MQTT_HOST=${MQTT_HOST:-mqtt}
- MQTT_PORT=${MQTT_PORT:-1883}
- MQTT_USERNAME=${MQTT_USERNAME:-}
- MQTT_PASSWORD=${MQTT_PASSWORD:-}
- MQTT_PREFIX=${MQTT_PREFIX:-meshcore}
- DATA_HOME=/data
# Explicitly unset to use DATA_HOME-based default path
- DATABASE_URL=
- API_HOST=0.0.0.0
- API_PORT=8000
- API_READ_KEY=${API_READ_KEY:-}
- API_ADMIN_KEY=${API_ADMIN_KEY:-}
command: ["api"]
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# ==========================================================================
# Web Dashboard - Web interface for network visualization
# ==========================================================================
web:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-web
restart: unless-stopped
depends_on:
api:
condition: service_healthy
ports:
- "${WEB_PORT:-8080}:8080"
environment:
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- API_BASE_URL=http://api:8000
- API_KEY=${API_READ_KEY:-}
- WEB_HOST=0.0.0.0
- WEB_PORT=8080
- NETWORK_NAME=${NETWORK_NAME:-MeshCore Network}
- NETWORK_CITY=${NETWORK_CITY:-}
- NETWORK_COUNTRY=${NETWORK_COUNTRY:-}
- NETWORK_LOCATION=${NETWORK_LOCATION:-}
- NETWORK_RADIO_CONFIG=${NETWORK_RADIO_CONFIG:-}
- NETWORK_CONTACT_EMAIL=${NETWORK_CONTACT_EMAIL:-}
- NETWORK_CONTACT_DISCORD=${NETWORK_CONTACT_DISCORD:-}
command: ["web"]
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# ==========================================================================
# Database Migrations - Run Alembic migrations
# ==========================================================================
db-migrate:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-db-migrate
profiles:
- migrate
volumes:
# Mount data directory (uses collector/meshcore.db)
- ${DATA_HOME:-./data}:/data
environment:
- DATA_HOME=/data
# Explicitly unset to use DATA_HOME-based default path
- DATABASE_URL=
command: ["db", "upgrade"]
# ==========================================================================
# Seed Data - Import node_tags.json and members.json from SEED_HOME
# ==========================================================================
seed:
image: ghcr.io/ipnet-mesh/meshcore-hub:main
build:
context: .
dockerfile: Dockerfile
container_name: meshcore-seed
profiles:
- seed
volumes:
# Mount data directory for database (read-write)
- ${DATA_HOME:-./data}:/data
# Mount seed directory for seed files (read-only)
- ${SEED_HOME:-./seed}:/seed:ro
environment:
- DATA_HOME=/data
- SEED_HOME=/seed
- LOG_LEVEL=${LOG_LEVEL:-INFO}
# Explicitly unset to use DATA_HOME-based default path
- DATABASE_URL=
# Imports both node_tags.json and members.json if they exist
command: ["collector", "seed"]
# ==========================================================================
# Volumes
# ==========================================================================
volumes:
mosquitto_data:
name: meshcore_mosquitto_data
mosquitto_log:
name: meshcore_mosquitto_log