From f4648d7fe72d01f83a4c0de07afa863c4ac67a3f Mon Sep 17 00:00:00 2001 From: Louis King Date: Tue, 14 Apr 2026 20:41:26 +0100 Subject: [PATCH] Split Docker Compose into base/dev/prod/traefik overrides with multi-instance support - Split docker-compose.yml into base config + environment overrides - docker-compose.dev.yml: port mappings for local development - docker-compose.prod.yml: external proxy-net network, no exposed ports - docker-compose.traefik.yml: optional Traefik auto-discovery labels - Parameterize container and volume names with COMPOSE_PROJECT_NAME - Default: hub-dev (containers: hub-dev-api, volumes: hub-dev_hub_data) - Override per instance for multi-instance deployments (hub-prod, hub-beta) - Add Makefile with build/up/down/logs/backup/restore targets - Add TRAEFIK_DOMAIN env var for Traefik routing configuration - Update UPGRADING.md with volume migration instructions (rename + copy methods) - Update README.md with multi-instance deployment and backup/restore sections --- .env.example | 11 +++ .gitignore | 1 + AGENTS.md | 9 +- Makefile | 40 +++++++++ README.md | 138 +++++++++++++++++++++++++--- UPGRADING.md | 179 +++++++++++++++++++++++++++++++------ docker-compose.dev.yml | 28 ++++++ docker-compose.prod.yml | 29 ++++++ docker-compose.traefik.yml | 33 +++++++ docker-compose.yml | 38 +++----- 10 files changed, 440 insertions(+), 66 deletions(-) create mode 100644 Makefile create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.traefik.yml diff --git a/.env.example b/.env.example index eadfff8..b8624db 100644 --- a/.env.example +++ b/.env.example @@ -29,6 +29,17 @@ # ============================================================================= # These settings apply to all services +# Docker Compose project name +# Used as a prefix for container names (e.g., hub-dev-api) and volume names +# (e.g., hub-dev_hub_data). Change per instance when running multiple deployments +# on the same Docker host (e.g., hub-prod, hub-beta, hub-stg). +# For multi-instance setups, see the "Multi-Instance Deployment" section in README.md. +COMPOSE_PROJECT_NAME=hub-dev + +# Domain name for this instance (only needed for Traefik deployments) +# Used by docker-compose.traefik.yml to configure routing rules +# TRAEFIK_DOMAIN=meshcore.example.com + # Docker image version tag to use # Options: latest, main, v1.0.0, etc. IMAGE_VERSION=latest diff --git a/.gitignore b/.gitignore index 13014d0..99e80a9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ !example/data/ /seed/ !example/seed/ +/backup/ /content/ # Byte-compiled / optimized / DLL files diff --git a/AGENTS.md b/AGENTS.md index cb5be95..4b650a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -324,7 +324,11 @@ meshcore-hub/ │ └── collector/ # Collector data │ └── meshcore.db # SQLite database ├── Dockerfile # Docker build configuration -└── docker-compose.yml # Docker Compose services +├── docker-compose.yml # Docker Compose base config +├── docker-compose.dev.yml # Development overrides (port mappings) +├── docker-compose.prod.yml # Production overrides (proxy network) +├── docker-compose.traefik.yml # Optional Traefik labels +└── SCHEMAS.md ``` ## MQTT Topic Structure @@ -585,6 +589,7 @@ meshcore-hub collector See [PLAN.md](PLAN.md#configuration-environment-variables) for complete list. Key variables: +- `COMPOSE_PROJECT_NAME` - Docker Compose project prefix for containers and volumes (default: `hub-dev`) - `DATA_HOME` - Base directory for runtime data (default: `./data`) - `SEED_HOME` - Directory containing seed data files (default: `./seed`) - `CONTENT_HOME` - Directory containing custom content (pages, media) (default: `./content`) @@ -663,7 +668,7 @@ The database can be seeded with node tags and network members from YAML files in meshcore-hub collector seed # With Docker Compose -docker compose --profile seed up +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile seed up ``` **Note:** Once the admin UI is enabled (`WEB_ADMIN_ENABLED=true`), tags should be managed through the web interface rather than seed files. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c80e093 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +ifneq (,$(wildcard ./.env)) + include .env + export +endif + +PROFILES ?= mqtt core +COMPOSE_FILES = -f docker-compose.yml -f docker-compose.dev.yml +VOLUMES = $(COMPOSE_PROJECT_NAME)_hub_data $(COMPOSE_PROJECT_NAME)_mqtt_broker_data \ + $(COMPOSE_PROJECT_NAME)_prometheus_data $(COMPOSE_PROJECT_NAME)_alertmanager_data \ + $(COMPOSE_PROJECT_NAME)_packetcapture_data + +.PHONY: build up down logs backup restore + +build: + docker compose $(COMPOSE_FILES) --profile all build --no-cache + +up: + docker compose $(COMPOSE_FILES) $(foreach p,$(PROFILES),--profile $(p)) up -d --force-recreate + +down: + docker compose $(COMPOSE_FILES) --profile all down --remove-orphans + +logs: + docker compose $(COMPOSE_FILES) --profile all logs -f + +backup: + @mkdir -p backup + @for vol in $(VOLUMES); do \ + echo "Backing up $$vol..."; \ + docker run --rm -v $$vol:/data -v $(PWD)/backup:/backup \ + alpine tar czf /backup/$$vol-$$(date +%Y%m%d-%H%M%S).tar.gz -C / data; \ + done + @echo "Backups saved to $(PWD)/backup/" + +restore: + @if [ -z "$(FILE)" ]; then echo "Usage: make restore FILE=backup/"; exit 1; fi + @vol=$$(basename $(FILE) | sed 's/-[0-9]\{8\}-[0-9]\{6\}\.tar\.gz//'); \ + echo "Restoring $$vol from $(FILE)..."; \ + docker run --rm -v $$vol:/data -v $(PWD)/backup:/backup \ + alpine sh -c "cd / && tar xzf /backup/$$(basename $(FILE))" diff --git a/README.md b/README.md index 69b01f9..2963daf 100644 --- a/README.md +++ b/README.md @@ -82,12 +82,13 @@ The quickest way to get started is running the entire stack on a single machine **Steps:** ```bash -# Create a directory, download the Docker Compose file and +# Create a directory, download the Docker Compose files and # example environment configuration file mkdir meshcore-hub cd meshcore-hub wget https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/refs/heads/main/docker-compose.yml +wget https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/refs/heads/main/docker-compose.dev.yml wget https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/refs/heads/main/.env.example # Copy and configure environment @@ -95,7 +96,7 @@ cp .env.example .env # Edit .env: set MQTT_HOST to your MQTT broker if not using the local one # Start the entire stack with local MQTT broker -docker compose --profile mqtt --profile core up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile mqtt --profile core up -d # View the web dashboard open http://localhost:8080 @@ -107,7 +108,29 @@ This starts all services: MQTT broker, collector, API, and web dashboard. MeshCo ### Docker Compose Profiles -Docker Compose uses **profiles** to select which services to run: +Docker Compose uses **profiles** to select which services to run. The configuration is split across multiple files: + +| File | Purpose | +|------|---------| +| `docker-compose.yml` | Base shared config (services, profiles, healthchecks, environment) | +| `docker-compose.dev.yml` | Development overrides (port mappings for direct access) | +| `docker-compose.prod.yml` | Production overrides (external proxy network, no exposed ports) | +| `docker-compose.traefik.yml` | Optional Traefik auto-discovery labels | + +All `docker compose` commands require explicit file selection with `-f`: + +```bash +# Development (default — exposes ports for local access) +docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d + +# Production (generic reverse proxy — nginx, caddy, etc.) +docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d + +# Production (Traefik) +docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d +``` + +Service profiles: | Profile | Services | Use Case | |---------|----------|----------| @@ -123,27 +146,112 @@ Docker Compose uses **profiles** to select which services to run: ```bash # Create database schema -docker compose --profile migrate run --rm db-migrate +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile migrate run --rm db-migrate # Seed the database -docker compose --profile seed run --rm seed +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile seed run --rm seed # Start core services with local MQTT broker -docker compose --profile mqtt --profile core up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile mqtt --profile core up -d # Or connect to external MQTT (configure MQTT_HOST in .env) -docker compose --profile core up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile core up -d # Start everything including packet capture observer -docker compose --profile mqtt --profile core --profile receiver up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile mqtt --profile core --profile receiver up -d # View logs -docker compose logs -f +docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f # Stop services -docker compose down +docker compose -f docker-compose.yml -f docker-compose.dev.yml down ``` +### Multi-Instance Deployment + +Multiple MeshCore Hub instances can run on the same Docker host (e.g., production, beta, staging). Each instance uses a unique `COMPOSE_PROJECT_NAME` to isolate containers, volumes, and networking. + +**1. Create a shared proxy network** (once): + +```bash +docker network create proxy-net +``` + +**2. Set up each instance in its own directory:** + +```bash +# Production instance +mkdir hub-prod && cd hub-prod +cp ../docker-compose.yml ../docker-compose.prod.yml ../.env.example . +cp .env.example .env +# Edit .env: set COMPOSE_PROJECT_NAME=hub-prod +``` + +```bash +# Beta instance +mkdir hub-beta && cd hub-beta +cp ../docker-compose.yml ../docker-compose.prod.yml ../.env.example . +cp .env.example .env +# Edit .env: set COMPOSE_PROJECT_NAME=hub-beta +``` + +**3. Configure your reverse proxy** to route traffic to the container names: + +| Instance | API Container | Web Container | +|----------|--------------|---------------| +| Production | `hub-prod-api:8000` | `hub-prod-web:8080` | +| Beta | `hub-beta-api:8000` | `hub-beta-web:8080` | + +**4. Start each instance:** + +```bash +# Production (generic reverse proxy) +docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile core up -d + +# Beta (with Traefik) +docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.traefik.yml --profile core up -d +``` + +Containers and volumes are automatically prefixed with the project name — no compose file edits needed per instance. + +### Backup & Restore + +#### Using Makefile + +```bash +# Back up all volumes to backup/ +make backup + +# Restore a specific volume +make restore FILE=backup/hub-dev_hub_data-20260414-120000.tar.gz +``` + +#### Using shell commands + +```bash +# Back up all volumes +source .env 2>/dev/null || true +mkdir -p backup +for vol in ${COMPOSE_PROJECT_NAME:-hub-dev}_hub_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_mqtt_broker_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_prometheus_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_alertmanager_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_packetcapture_data; do + echo "Backing up $vol..." + docker run --rm -v $vol:/data -v $(pwd)/backup:/backup \ + alpine tar czf /backup/$vol-$(date +%Y%m%d-%H%M%S).tar.gz -C / data +done + +# Restore a specific volume (volume name derived from tarball filename) +source .env 2>/dev/null || true +FILE=backup/${COMPOSE_PROJECT_NAME:-hub-dev}_hub_data-20260414-120000.tar.gz +vol=$(basename "$FILE" | sed 's/-[0-9]\{8\}-[0-9]\{6\}\.tar\.gz//') +docker run --rm -v $vol:/data -v $(pwd)/backup:/backup \ + alpine sh -c "cd / && tar xzf /backup/$(basename $FILE)" +``` + +> **Note:** Replace `hub-dev` with your `COMPOSE_PROJECT_NAME` if using a different instance name. + ### Manual Installation ```bash @@ -417,8 +525,7 @@ The markdown content is rendered as-is, so include your own `# Heading` if desir Pages automatically appear in the navigation menu and sitemap. With Docker, mount the content directory: ```yaml -# docker-compose.yml (already configured) -volumes: +# docker-compose.yml (already configured)volumes: - ${CONTENT_HOME:-./content}:/content:ro environment: - CONTENT_HOME=/content @@ -433,7 +540,7 @@ The database can be seeded with node tags and network members from YAML files in Seeding is a separate process and must be run explicitly: ```bash -docker compose --profile seed up +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile seed up ``` This imports data from the following files (if they exist): @@ -646,7 +753,10 @@ meshcore-hub/ │ └── images/ # Custom images (logo.svg/png/jpg/jpeg/webp replace default logo) ├── data/ # Runtime data directory (DATA_HOME, created at runtime) ├── Dockerfile # Docker build configuration -├── docker-compose.yml # Docker Compose services +├── docker-compose.yml # Docker Compose base config +├── docker-compose.dev.yml # Development overrides (port mappings) +├── docker-compose.prod.yml # Production overrides (proxy network) +├── docker-compose.traefik.yml # Optional Traefik labels ├── SCHEMAS.md # Event schema documentation ├── UPGRADING.md # Upgrade guide for breaking changes └── AGENTS.md # AI assistant guidelines diff --git a/UPGRADING.md b/UPGRADING.md index d0505d1..66505c7 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -16,29 +16,39 @@ This guide covers upgrading from a previous MeshCore Hub release to the current | DB table | `event_receivers` | `event_observers` | | API commands | `/api/v1/commands/*` | Removed | | Compose profiles | `receiver`, `sender`, `mock` | `receiver` (packet-capture) | +| Compose files | Single `docker-compose.yml` | Base + environment overrides (`.dev.yml`, `.prod.yml`) | +| Container names | `meshcore-*` | Parameterized via `COMPOSE_PROJECT_NAME` (default: `hub-dev-*`) | +| Volume names | `meshcore_*` | Parameterized via `COMPOSE_PROJECT_NAME` (default: `hub-dev_*`) | -## Step 1: Backup the Database +## Step 1: Backup -**Do not skip this step.** The database migration renames columns and tables, and while it has been tested, you should always have a backup. +**Do not skip this step.** Back up all data volumes before proceeding. + +### Using Makefile ```bash -# Create a timestamped backup of the database volume -docker run --rm \ - -v meshcore_hub_data:/data \ - -v $(pwd):/backup \ - alpine tar czf /backup/meshcore-hub-db-$(date +%Y%m%d-%H%M%S).tar.gz -C / data +make backup +``` -# Verify the backup was created -ls -lh meshcore-hub-db-*.tar.gz +### Using shell commands + +```bash +source .env 2>/dev/null || true +mkdir -p backup +for vol in ${COMPOSE_PROJECT_NAME:-hub-dev}_hub_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_mqtt_broker_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_prometheus_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_alertmanager_data \ + ${COMPOSE_PROJECT_NAME:-hub-dev}_packetcapture_data; do + docker run --rm -v $vol:/data -v $(pwd)/backup:/backup \ + alpine tar czf /backup/$vol-$(date +%Y%m%d-%H%M%S).tar.gz -C / data +done ``` To restore from backup if needed: ```bash -docker run --rm \ - -v meshcore_hub_data:/data \ - -v $(pwd):/backup \ - alpine sh -c "cd / && tar xzf /backup/meshcore-hub-db-YYYYMMDD-HHMMSS.tar.gz" +make restore FILE=backup/hub-dev_hub_data-YYYYMMDD-HHMMSS.tar.gz ``` ## Step 2: Stop and Remove Containers @@ -51,21 +61,109 @@ docker compose down --remove-orphans > **Important:** Do NOT use `--volumes` / `-v`. That would delete your database. The `--remove-orphans` flag cleans up old services (like `interface-receiver`, `interface-sender`) that no longer exist in the new compose file. -## Step 3: Update Configuration Files +## Step 3: Rename Docker Volumes + +Container and volume names are now parameterized via `COMPOSE_PROJECT_NAME`. The default is `hub-dev`, so volumes are renamed from `meshcore_*` to `hub-dev_*`. + +First, check which volumes you have: + +```bash +docker volume ls | grep meshcore +``` + +### Volumes to migrate + +These volumes always need migrating: + +| Old Name | New Name | +|----------|----------| +| `meshcore_hub_data` | `hub-dev_hub_data` | +| `meshcore_prometheus_data` | `hub-dev_prometheus_data` | +| `meshcore_alertmanager_data` | `hub-dev_alertmanager_data` | +| `meshcore_packetcapture_data` | `hub-dev_packetcapture_data` | + +For the MQTT broker, it depends on your current version: + +| Your Current Broker | Volume to Migrate | Action | +|---------------------|-------------------|--------| +| `meshcore-mqtt-broker` | `meshcore_mqtt_broker_data` → `hub-dev_mqtt_broker_data` | Rename or copy below | +| Mosquitto (older) | `meshcore_mosquitto_data`, `meshcore_mosquitto_log` | **Remove** — no longer used. New volume created automatically on first run. | + +### Option A: Rename (Docker Engine 23.0+) + +> **Note:** `docker volume rename` is not available in all Docker builds (e.g., Docker Desktop). If the command is not found, use Option B instead. + +```bash +docker volume rename meshcore_hub_data hub-dev_hub_data +docker volume rename meshcore_prometheus_data hub-dev_prometheus_data +docker volume rename meshcore_alertmanager_data hub-dev_alertmanager_data +docker volume rename meshcore_packetcapture_data hub-dev_packetcapture_data + +# Only if you already have meshcore-mqtt-broker (skip if still on Mosquitto) +docker volume rename meshcore_mqtt_broker_data hub-dev_mqtt_broker_data +``` + +### Option B: Copy (all Docker versions) + +If `docker volume rename` is not available in your Docker build: + +```bash +# For each volume: create new, copy data, remove old +docker volume create hub-dev_hub_data +docker run --rm -v meshcore_hub_data:/from -v hub-dev_hub_data:/to alpine sh -c "cp -a /from/. /to/" + +docker volume create hub-dev_prometheus_data +docker run --rm -v meshcore_prometheus_data:/from -v hub-dev_prometheus_data:/to alpine sh -c "cp -a /from/. /to/" + +docker volume create hub-dev_alertmanager_data +docker run --rm -v meshcore_alertmanager_data:/from -v hub-dev_alertmanager_data:/to alpine sh -c "cp -a /from/. /to/" + +docker volume create hub-dev_packetcapture_data +docker run --rm -v meshcore_packetcapture_data:/from -v hub-dev_packetcapture_data:/to alpine sh -c "cp -a /from/. /to/" + +# Only if you already have meshcore-mqtt-broker (skip if still on Mosquitto) +docker volume create hub-dev_mqtt_broker_data +docker run --rm -v meshcore_mqtt_broker_data:/from -v hub-dev_mqtt_broker_data:/to alpine sh -c "cp -a /from/. /to/" + +# Verify the new volumes have data, then remove old ones +docker volume rm meshcore_hub_data meshcore_prometheus_data meshcore_alertmanager_data meshcore_packetcapture_data + +# Only if you already have meshcore-mqtt-broker +docker volume rm meshcore_mqtt_broker_data +``` + +### Clean up old Mosquitto volumes (if applicable) + +If upgrading from the Mosquitto era, remove the unused volumes: + +```bash +# Skip if these don't exist +docker volume rm meshcore_mosquitto_data meshcore_mosquitto_log +``` + +> **Note:** If any volumes show "in use", remove any stopped containers first: `docker rm -f `. + +> **Note:** If setting up a multi-instance deployment (e.g., `hub-prod`, `hub-beta`), use that project name instead of `hub-dev`. + +> **Note:** After migrating volumes, you may see warnings like `volume "hub-dev_hub_data" already exists but was not created by Docker Compose. Use \`external: true\` to use an existing volume`. This is safe to ignore — it appears because the volumes were created manually during migration rather than by Docker Compose. Fresh deployments will not see this warning. + +## Step 4: Update Configuration Files Download the latest configuration files: ```bash -# Download the new docker-compose.yml +# Download the base compose file and environment overrides wget -O docker-compose.yml https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/main/docker-compose.yml +wget -O docker-compose.dev.yml https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/main/docker-compose.dev.yml +wget -O docker-compose.prod.yml https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/main/docker-compose.prod.yml # Download the new .env.example for reference wget -O .env.example https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/main/.env.example ``` -Then compare your existing `.env` against the new `.env.example` and update it (see Step 4). +Then compare your existing `.env` against the new `.env.example` and update it (see Step 5). -## Step 4: Migrate Your `.env` File +## Step 5: Migrate Your `.env` File ### Variables to Remove @@ -110,6 +208,9 @@ MQTT_WS_PORT=9001 ### Variables to Add ```bash +# Docker Compose project name (container and volume prefix) +COMPOSE_PROJECT_NAME=hub-dev + # MQTT subscriber authentication for the collector # The collector connects as a subscriber to read all published topics # including /internal. Set these to match your broker's SUBSCRIBER_1 config. @@ -131,47 +232,47 @@ PACKETCAPTURE_IATA=LOC All other `PACKETCAPTURE_*` variables have sensible defaults in `docker-compose.yml` and only need to be set in `.env` if you want to override them. See `.env.example` for the full list. -## Step 5: Run Database Migration +## Step 6: Run Database Migration The migration renames `receiver_node_id` → `observer_node_id` across all event tables and `event_receivers` → `event_observers`: ```bash -docker compose --profile core run --rm db-migrate +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile core run --rm db-migrate ``` This runs automatically as part of the `core` profile, but can also be run standalone with the `migrate` profile: ```bash -docker compose --profile migrate run --rm db-migrate +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile migrate run --rm db-migrate ``` -## Step 6: Start Services +## Step 7: Start Services ### With local MQTT broker (single-host deployment) ```bash # Start everything including the MQTT broker -docker compose --profile mqtt --profile core up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile mqtt --profile core up -d # Or include packet capture on the same host -docker compose --profile mqtt --profile core --profile receiver up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile mqtt --profile core --profile receiver up -d ``` ### With external MQTT broker ```bash # Start core services only (broker runs elsewhere) -docker compose --profile core up -d +docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile core up -d ``` ### Verify ```bash # Check all containers are running -docker compose ps +docker compose -f docker-compose.yml -f docker-compose.dev.yml ps # Check collector connected to MQTT -docker compose logs collector | grep -i "connected to mqtt" +docker compose -f docker-compose.yml -f docker-compose.dev.yml logs collector | grep -i "connected to mqtt" # Check the web dashboard open http://localhost:8080 @@ -235,6 +336,32 @@ The following Docker Compose services have been removed: The `packet-capture` service uses the [meshcore-packet-capture](https://github.com/agessaman/meshcore-packet-capture) image and is included in `docker-compose.yml` under the `receiver` profile for an easy transition. +### New Docker Compose File Structure + +The Docker Compose configuration is now split into multiple files: + +| File | Purpose | +|------|---------| +| `docker-compose.yml` | Base shared config (services, profiles, healthchecks, environment) | +| `docker-compose.dev.yml` | Development overrides (port mappings for direct access) | +| `docker-compose.prod.yml` | Production overrides (external proxy network, no exposed ports) | +| `docker-compose.traefik.yml` | Optional Traefik auto-discovery labels | + +All `docker compose` commands now require explicit file selection: + +```bash +# Development (exposes ports for local access) +docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d + +# Production (connects to reverse proxy network) +docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d + +# Production with Traefik +docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d +``` + +Container and volume names are parameterized via `COMPOSE_PROJECT_NAME` in `.env`. This enables multiple instances (e.g., `hub-prod`, `hub-beta`) on the same Docker host. + ### Removed API Endpoints The command dispatch API endpoints have been removed: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..1e1f3e5 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,28 @@ +# MeshCore Hub - Development Docker Compose Override +# +# Exposes service ports for local development and testing. +# NOT intended for production use — use docker-compose.prod.yml instead. +# +# Usage: +# docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d + +services: + mqtt: + ports: + - "${MQTT_PORT:-1883}:${MQTT_PORT:-1883}" + + api: + ports: + - "${API_PORT:-8000}:8000" + + web: + ports: + - "${WEB_PORT:-8080}:8080" + + prometheus: + ports: + - "${PROMETHEUS_PORT:-9090}:9090" + + alertmanager: + ports: + - "${ALERTMANAGER_PORT:-9093}:9093" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..cb60f66 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,29 @@ +# MeshCore Hub - Production Docker Compose Override +# +# Connects api and web services to an external proxy network for +# reverse proxy access (Traefik, Nginx, Caddy, etc.). +# No ports are exposed directly — all traffic goes through the reverse proxy. +# +# Prerequisites: +# docker network create proxy-net +# +# Usage: +# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d +# +# With Traefik: +# docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d + +services: + api: + networks: + - default + - proxy-net + + web: + networks: + - default + - proxy-net + +networks: + proxy-net: + external: true diff --git a/docker-compose.traefik.yml b/docker-compose.traefik.yml new file mode 100644 index 0000000..6d53693 --- /dev/null +++ b/docker-compose.traefik.yml @@ -0,0 +1,33 @@ +# MeshCore Hub - Traefik Docker Compose Override +# +# Adds Traefik auto-discovery labels to api and web services. +# Requires docker-compose.prod.yml (for the proxy-net network). +# +# Set DOMAIN in your .env file to the hostname for this instance. +# +# Prerequisites: +# - Traefik configured with Docker provider and attached to proxy-net +# - TRAEFIK_DOMAIN set in .env (e.g., TRAEFIK_DOMAIN=meshcore.example.com) +# +# Usage: +# docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d + +services: + api: + labels: + - "traefik.enable=true" + - "traefik.http.services.${COMPOSE_PROJECT_NAME:-hub-dev}-api.loadbalancer.server.port=8000" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-api.rule=Host(`${TRAEFIK_DOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/metrics`) || PathPrefix(`/health`))" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-api.entrypoints=websecure" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-api.tls=true" + - "traefik.docker.network=proxy-net" + + web: + labels: + - "traefik.enable=true" + - "traefik.http.services.${COMPOSE_PROJECT_NAME:-hub-dev}-web.loadbalancer.server.port=8080" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-web.rule=Host(`${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-web.entrypoints=websecure" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-web.tls=true" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:-hub-dev}-web.priority=1" + - "traefik.docker.network=proxy-net" diff --git a/docker-compose.yml b/docker-compose.yml index 18184b1..3c10a76 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,13 +7,11 @@ services: # ========================================================================== mqtt: image: ghcr.io/ipnet-mesh/meshcore-mqtt-broker:latest - container_name: meshcore-mqtt + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-mqtt profiles: - all - mqtt restart: unless-stopped - ports: - - "${MQTT_PORT:-1883}:${MQTT_PORT:-1883}" volumes: - mqtt_broker_data:/data environment: @@ -64,7 +62,7 @@ services: # ========================================================================== packet-capture: image: ghcr.io/agessaman/meshcore-packet-capture:${PACKETCAPTURE_IMAGE_VERSION:-latest} - container_name: meshcore-packet-capture + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-packet-capture profiles: - all - receiver @@ -136,7 +134,7 @@ services: build: context: . dockerfile: Dockerfile - container_name: meshcore-collector + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-collector profiles: - all - core @@ -196,7 +194,7 @@ services: build: context: . dockerfile: Dockerfile - container_name: meshcore-api + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-api profiles: - all - core @@ -206,8 +204,6 @@ services: condition: service_completed_successfully collector: condition: service_started - ports: - - "${API_PORT:-8000}:8000" volumes: - hub_data:/data environment: @@ -249,7 +245,7 @@ services: build: context: . dockerfile: Dockerfile - container_name: meshcore-web + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-web profiles: - all - core @@ -257,8 +253,6 @@ services: depends_on: api: condition: service_healthy - ports: - - "${WEB_PORT:-8080}:8080" volumes: - ${CONTENT_HOME:-./content}:/content:ro environment: @@ -315,7 +309,7 @@ services: build: context: . dockerfile: Dockerfile - container_name: meshcore-db-migrate + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-db-migrate profiles: - all - core @@ -338,7 +332,7 @@ services: build: context: . dockerfile: Dockerfile - container_name: meshcore-seed + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-seed profiles: - seed restart: "no" @@ -357,7 +351,7 @@ services: # ========================================================================== prometheus: image: prom/prometheus:latest - container_name: meshcore-prometheus + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-prometheus profiles: - all - metrics @@ -365,8 +359,6 @@ services: depends_on: api: condition: service_healthy - ports: - - "${PROMETHEUS_PORT:-9090}:9090" command: - "--config.file=/etc/prometheus/prometheus.yml" - "--storage.tsdb.retention.time=30d" @@ -380,13 +372,11 @@ services: # ========================================================================== alertmanager: image: prom/alertmanager:latest - container_name: meshcore-alertmanager + container_name: ${COMPOSE_PROJECT_NAME:-hub-dev}-alertmanager profiles: - all - metrics restart: unless-stopped - ports: - - "${ALERTMANAGER_PORT:-9093}:9093" volumes: - ./etc/alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro - alertmanager_data:/alertmanager @@ -399,12 +389,12 @@ services: # ========================================================================== volumes: hub_data: - name: meshcore_hub_data + name: ${COMPOSE_PROJECT_NAME:-hub-dev}_hub_data mqtt_broker_data: - name: meshcore_mqtt_broker_data + name: ${COMPOSE_PROJECT_NAME:-hub-dev}_mqtt_broker_data prometheus_data: - name: meshcore_prometheus_data + name: ${COMPOSE_PROJECT_NAME:-hub-dev}_prometheus_data alertmanager_data: - name: meshcore_alertmanager_data + name: ${COMPOSE_PROJECT_NAME:-hub-dev}_alertmanager_data packetcapture_data: - name: meshcore_packetcapture_data + name: ${COMPOSE_PROJECT_NAME:-hub-dev}_packetcapture_data