# MeshCore Hub - Docker Compose Configuration # # Usage with profiles: # docker compose --profile mqtt --profile collector --profile api up # docker compose --profile all up # docker compose --profile mock up # For testing with mock devices # # Available profiles: # - mqtt: MQTT broker (Eclipse Mosquitto) # - interface-receiver: Interface in RECEIVER mode # - interface-sender: Interface in SENDER mode # - collector: Event collector and database storage # - api: REST API server # - web: Web dashboard # - mock: All components with mock device (for testing) # - all: All production components (requires real device) # - migrate: Run database migrations # - import-tags: Import node tags from JSON file # # Data Directory (DATA_HOME): # The DATA_HOME environment variable controls where all service data is stored. # Default: ./data (local) or /data (in containers) # # Structure: # ${DATA_HOME}/ # ├── collector/ # │ ├── meshcore.db # SQLite database # │ └── tags.json # Node tags for import # └── web/ # └── members.json # Network members list # # Example data files are provided in ./example/data/ # # To import tags: # docker compose --profile import-tags run --rm import-tags services: # ========================================================================== # MQTT Broker - Eclipse Mosquitto # ========================================================================== mqtt: image: eclipse-mosquitto:2 container_name: meshcore-mqtt profiles: - mqtt - all - mock 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: build: context: . dockerfile: Dockerfile container_name: meshcore-interface-receiver profiles: - interface-receiver - all 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: build: context: . dockerfile: Dockerfile container_name: meshcore-interface-sender profiles: - interface-sender - all 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: 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: build: context: . dockerfile: Dockerfile container_name: meshcore-collector profiles: - collector - all - mock restart: unless-stopped depends_on: mqtt: condition: service_healthy volumes: # Mount data directory (contains collector/meshcore.db and collector/tags.json) - ${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 # 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: build: context: . dockerfile: Dockerfile container_name: meshcore-api profiles: - api - all - mock 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 - 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: build: context: . dockerfile: Dockerfile container_name: meshcore-web profiles: - web - all - mock restart: unless-stopped depends_on: api: condition: service_healthy ports: - "${WEB_PORT:-8080}:8080" volumes: # Mount data directory (uses web/members.json) - ${DATA_HOME:-./data}:/data 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 - DATA_HOME=/data - 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: 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 command: ["db", "upgrade"] # ========================================================================== # Import Tags - Import node tags from JSON file # ========================================================================== import-tags: build: context: . dockerfile: Dockerfile container_name: meshcore-import-tags profiles: - import-tags volumes: # Mount data directory (uses collector/tags.json and collector/meshcore.db) - ${DATA_HOME:-./data}:/data environment: - DATA_HOME=/data - LOG_LEVEL=${LOG_LEVEL:-INFO} # Uses default tags file: /data/collector/tags.json command: ["collector", "import-tags"] # ========================================================================== # Volumes # ========================================================================== volumes: mosquitto_data: name: meshcore_mosquitto_data mosquitto_log: name: meshcore_mosquitto_log