A production-ready MQTT proxy for Meshtastic devices that enables bidirectional message forwarding between Meshtastic nodes and MQTT brokers. Supports TCP and Serial interface connections with a clean factory pattern architecture.
Version: 1.6.4
- ✅ Modular Architecture - Clean separation of concerns with
config.py,handlers/mqtt.py,handlers/meshtastic.py, andhandlers/queue.py - ✅ Multi-Interface Support - TCP and Serial connections to Meshtastic nodes
- ✅ Bidirectional Forwarding - Messages flow both ways between node and MQTT broker
- ✅ Message Queue - Rate-limited transmission with configurable delay to prevent radio congestion
- ✅ Robust Packet Handling - SafeInterfaceMixin prevents crashes from malformed packets
- ✅ Implicit ACK Restoration - Intelligently bypasses loop protection for echoed packets, restoring the firmware's missing delivery confirmations (fixing the "Red X" issue)
- ✅ mqttClientProxyMessage Protocol - Implements Meshtastic's official proxy protocol
- ✅ Docker Containerized - Easy deployment with Docker Compose
- ✅ Environment Configuration - Flexible configuration via environment variables
- ✅ Production Ready - Error handling, logging, and automatic reconnection
- ✅ Channel Support - Works with all Meshtastic channels and message types
- ✅ SSL/TLS Support - Auto-configuration for secure brokers (matches iOS behavior)
- ✅ Traffic Optimization - Smart subscription strategy (
msh/2/e/#) to prevent serial link saturation - ✅ Hammering Prevention - Correctly flags retained messages to avoid
NO_RESPONSEstorms - ✅ Channel Filtering - Respects
uplink_enabledanddownlink_enabledchannel settings - ✅ Virtual Channels - Monitor cross-region MQTT traffic via
EXTRA_MQTT_ROOTSwithout RF crosstalk (payload mutation prevents radio decryption & rebroadcast) - ✅ MeshMonitor Compatible - Seamless integration with MeshMonitor and other tools
Note: BLE interface is not currently supported. Use TCP or Serial interfaces.
Note
The Docker setup below is designed for Linux systems. For Windows and macOS, see the Platform Support section.
- Docker and Docker Compose (Linux)
- Meshtastic node (accessible via TCP or Serial)
- MQTT broker (configured on your Meshtastic node)
- Clone the repository:
git clone https://github.com/LN4CY/mqtt-proxy.git
cd mqtt-proxy- Create
.envfile:
cp .env.example .env- Configure your connection in
.env:
INTERFACE_TYPE=tcp
TCP_NODE_HOST=192.168.1.100
TCP_NODE_PORT=4403- Start the proxy:
docker compose up -dYou can run the proxy directly without cloning the code using the pre-built image from GitHub Container Registry:
docker run -d \
--name mqtt-proxy \
--net=host \
--restart unless-stopped \
-e INTERFACE_TYPE=tcp \
-e TCP_NODE_HOST=192.168.1.100 \
-e TCP_NODE_PORT=4403 \
ghcr.io/ln4cy/mqtt-proxy:masterTCP Interface (default):
INTERFACE_TYPE=tcp
TCP_NODE_HOST=localhost
TCP_NODE_PORT=4403Serial Interface:
INTERFACE_TYPE=serial
SERIAL_PORT=/dev/ttyACM0| Variable | Default | Description |
|---|---|---|
INTERFACE_TYPE |
tcp |
Interface type: tcp or serial |
TCP_NODE_HOST |
localhost |
TCP hostname or IP address |
TCP_NODE_PORT |
4403 |
TCP port number |
SERIAL_PORT |
/dev/ttyUSB0 |
Serial device path |
LOG_LEVEL |
INFO |
Logging level |
TCP_TIMEOUT |
300 |
TCP connection timeout (seconds) |
CONFIG_WAIT_TIMEOUT |
60 |
Node config wait timeout (seconds) |
POLL_INTERVAL |
1 |
Config polling interval (seconds) |
MESH_TRANSMIT_DELAY |
0.5 |
Delay between packets for rate limiting (seconds) |
See CONFIG.md for detailed configuration options.
The proxy implements a robust health monitoring system to ensure reliable operation:
- Connection Watchdog: Automatically restarts if the Meshtastic connection is reported lost and doesn't recover within 60 seconds.
- Radio Activity Probe: Actively probes the radio if silent for 5 minutes. If no response receives, restarts the container.
- MQTT Publish Watchdog: Monitors MQTT publish success. If 5 consecutive publish attempts fail (e.g., broker unavailable, auth error), the proxy forces a restart to attempt a clean reconnection.
This self-healing behavior ensures the proxy recovers automatically from network interruptions without manual intervention.
Connect to a Meshtastic node via TCP (e.g., MeshMonitor's virtual node):
# .env
INTERFACE_TYPE=tcp
TCP_NODE_HOST=192.168.1.100
TCP_NODE_PORT=4404Connect to a USB-connected Meshtastic device:
# .env
INTERFACE_TYPE=serial
SERIAL_PORT=/dev/ttyACM0Note: Serial interface requires privileged mode (already configured in docker-compose.yml).
docker compose logs -f mqtt-proxyIf you are integrating mqtt-proxy as a child process inside another application (e.g., via Node.js spawn or execFile), you can pipe its output reliably. The proxy explicitly enforces UTF-8 encoding and line-buffered streaming for sys.stdout and sys.stderr when PIPED.
All proxy logs (including emojis) will stream instantly over standard IO without any buffering delays or Windows Console encoding crashes ([WinError 10106]).
docker compose downThe Docker setup is designed for Linux and works out of the box for both TCP and Serial interfaces.
Option 1: Self-Contained Executable (Click-and-Run - Recommended)
If you prefer not to manage Python environments or Docker on Windows, you can download a pre-built standalone .exe directly from the GitHub Releases page.
- Download the
mqtt-proxy-windows-amd64.exefrom the latest release. - Open PowerShell or Command Prompt.
- Run the executable, passing your connection details as command-line arguments:
# Example: Connecting to a MeshMonitor Virtual Node
.\mqtt-proxy-windows-amd64.exe --interface tcp --tcp-host 127.0.0.1 --tcp-port 4404
# Example: Connecting directly to a USB device
.\mqtt-proxy-windows-amd64.exe --interface serial --serial-port COM3
# Example: Check the version
.\mqtt-proxy-windows-amd64.exe --versionNote: You can also use a standard
.envfile in the same directory as the executable, and it will read those defaults automatically. Run.\mqtt-proxy-windows-amd64.exe --helpto see all available configuration overrides!
Option 2: Run Natively with Python venv Running via Python natively is great for development or if you already have Python installed.
- In the MeshMonitor Windows App, go to Settings and check "Enable Virtual Node Server". This starts a local MeshNode server on port
4404(Skip if using direct USB serial). - Open PowerShell or Command Prompt.
- Create and activate a virtual environment:
python -m venv venv
.\venv\Scripts\Activate.ps1- Install dependencies:
pip install -r requirements.txt- Run the proxy with command-line arguments:
# Connecting to Virtual Node
python mqtt-proxy.py --interface tcp --tcp-host 127.0.0.1 --tcp-port 4404
# Connecting to USB
python mqtt-proxy.py --interface serial --serial-port COM3
# Check the version
python mqtt-proxy.py --versionOption 3: Run via Docker Desktop
TCP Interface:
Docker Desktop for Windows works perfectly for TCP connections. You can use the standard docker-compose.yml configuration out-of-the-box.
Serial Interface (USB):
Docker on Windows does not support USB passthrough directly because Docker runs in WSL2. If you absolutely must use Docker for a USB serial connection on Windows, you can bridge it with usbipd:
- Install usbipd-win
- Attach device:
usbipd wsl attach --busid <BUSID> - Device appears as
/dev/ttyACM0in WSL2/Docker
TCP Interface:
Docker Desktop for Mac works perfectly for TCP connections. Use the standard docker-compose.yml.
Serial Interface (USB): Docker on macOS does not support USB passthrough directly because Docker runs in a VM.
Option A: Run Natively with venv (Recommended)
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Find your device (usually /dev/cu.usbmodem* or /dev/tty.usbmodem*)
ls /dev/cu.usbmodem*
# Run with environment variables
export INTERFACE_TYPE=serial
export SERIAL_PORT=/dev/cu.usbmodem14201 # Use your actual device path
python mqtt-proxy.pyOption B: Docker Desktop USB Forwarding (Experimental) Docker Desktop for Mac 4.27+ supports USB device forwarding:
- Enable in Docker Desktop settings: Settings → Resources → USB devices
- Select your Meshtastic device
- Device appears as
/dev/ttyACM0in containers - Update
docker-compose.ymldevices section accordingly
For a seamless integration with MeshMonitor, add the proxy as a service in your main docker-compose.yml.
Important
If you plan to use the MeshMonitor serial bridge or BLE bridge, you must use a virtual node enabled configuration for MeshMonitor to ensure proper connectivity.
- Shared Network: Use a custom bridge network (
meshtastic_net) for all services to enable service-name discovery. - Startup Order: Use a healthcheck on
meshmonitorsomqtt-proxyonly starts when the virtual node is ready. - Environment: Use
TCP_NODE_HOST=meshmonitorto avoid hardcoded IPs.
version: '3'
services:
# The main application
meshmonitor:
image: ghcr.io/yeraze/meshmonitor:latest
container_name: meshmonitor
restart: unless-stopped
ports:
- "8181:3001"
- "4404:4404"
environment:
- ENABLE_VIRTUAL_NODE=true
# Optional: Subscribe to another region's traffic
- EXTRA_MQTT_ROOTS=msh/US/NC:NC
- VIRTUAL_NODE_PORT=4404
- MESHTASTIC_NODE_IP=serial-bridge # Connects to serial-bridge by name
- STATUS_FILE=/data/.upgrade-status
- CHECK_INTERVAL=5
- COMPOSE_PROJECT_DIR=/compose
- COMPOSE_PROJECT_NAME=meshmonitor # Critical: Forces upgrader to use shared network
command: /data/scripts/upgrade-watchdog.sh
# Add simple healthcheck to ensure port 4404 is open
healthcheck:
test: ["CMD-SHELL", "node -e 'const net = require(\"net\"); const client = new net.Socket(); client.connect(4404, \"127.0.0.1\", () => { process.exit(0); }); client.on(\"error\", () => { process.exit(1); });'"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
depends_on:
- serial-bridge
networks:
- meshtastic_net
# The generic serial bridge
serial-bridge:
image: ghcr.io/yeraze/meshtastic-serial-bridge:latest
container_name: meshtastic-serial-bridge
devices:
- /dev/ttyACM0:/dev/ttyACM0
environment:
- SERIAL_DEVICE=/dev/ttyACM0
- TCP_PORT=4403
networks:
- meshtastic_net
# This proxy service (The Glue)
mqtt-proxy:
image: ghcr.io/ln4cy/mqtt-proxy:master
container_name: mqtt-proxy
restart: unless-stopped
environment:
- INTERFACE_TYPE=tcp
- TCP_NODE_HOST=meshmonitor # Connects to meshmonitor by name
- TCP_NODE_PORT=4404
depends_on:
meshmonitor:
condition: service_healthy # Wait for port 4404 to be listening
networks:
- meshtastic_net
networks:
meshtastic_net:
driver: bridgeThe proxy uses a modular architecture with clean separation of concerns:
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Meshtastic │ ◄─────► │ MQTT Proxy │ ◄─────► │ MQTT Broker │
│ Node │ │ │ │ │
└─────────────┘ └──────────────┘ └─────────────┘
TCP/Serial mqttClientProxy MQTT Protocol
│
┌────────┴────────┐
│ │
MessageQueue SafeInterfaceMixin
(Rate Limiting) (Crash Prevention)
mqtt-proxy.py- Main orchestrator and entry pointconfig.py- Centralized configuration managementhandlers/mqtt.py- MQTT connection and message handlinghandlers/meshtastic.py- Meshtastic interface with SafeInterfaceMixinhandlers/queue.py- Message queue for rate limiting and reliability- SafeInterfaceMixin - Prevents crashes from malformed packets, detects implicit ACKs
- MessageQueue - Thread-safe queue with configurable rate limiting
- Node → MQTT: Proxy receives
mqttClientProxyMessagefrom node and publishes to MQTT broker - MQTT → Node: Proxy subscribes to MQTT topics and forwards messages to node as
mqttClientProxyMessage - Implicit ACK Handling: The proxy detects when the MQTT broker echoes a message sent by the local node and forwards it back. This fulfills the firmware's requirement for a delivery confirmation, ensuring the node generates a local "Implicit ACK" rather than waiting 45 seconds and timing out (which causes Red X delivery failures in MeshMonitor).
- Transparent Operation: Node firmware handles encryption, channel mapping, and routing
- Python 3.9+
- Docker & Docker Compose
- Meshtastic node with MQTT enabled and
proxy_to_client_enabled: true
- meshtastic==2.7.7
- paho-mqtt==2.1.0
- pubsub==4.0.7
- protobuf>=3.20.0,<6.0.0
TCP Connection Fails:
- Verify node IP and port
- Check firewall rules
- Ensure node is running
Serial Connection Fails:
- Check device path (
ls /dev/tty*) - Verify device permissions
- Ensure privileged mode is enabled
No MQTT Traffic:
- Verify MQTT is enabled on node:
meshtastic --get mqtt - Check
proxy_to_client_enabled: true - Verify MQTT broker is accessible
Messages Not Appearing:
- Check MQTT broker logs
- Verify channel configuration
- Review proxy logs for errors
# Install dependencies
pip install -r requirements.txt
# Run locally
python mqtt-proxy.pydocker compose buildBecause this repository enforces Pull Request requirements for the master branch, follow this workflow to release a new version:
-
Create a Release Branch:
git checkout -b release/v1.6.4
-
Run the Release Script: Use the provided automation script to bump the version in
version.pyandREADME.md:python scripts/release.py 1.6.4
This will create a local "chore: release v1.6.4" commit and a local
v1.6.4tag. -
Push and Open a PR: Push the branch and open a Pull Request to
master.git push origin release/v1.6.4
-
Merge and Tag: Once the PR is merged into
master, push the local tag to GitHub to trigger the release pipeline:git checkout master git pull git push origin v1.6.4
The GitHub Actions will automatically detect the new tag, build the Windows executable, and publish the Docker images.
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
This project's source code is released under the MIT License. See the LICENSE file for details.
while the source code of this proxy is MIT licensed, it depends on third-party libraries that use different licenses:
- meshtastic-python: Licensed under GPLv3.
- paho-mqtt: Licensed under EPL-2.0 / BSD.
Important
GPL Compatibility Note:
Because the proxy imports and links with meshtastic (GPLv3), any distributed binary or Docker image containing these components is effectively subject to the terms of the GPLv3.
If you are building upon this project and plan to distribute it, ensure you comply with the requirements of the GPLv3 for the combined work.
- Built for the Meshtastic project
- Compatible with MeshMonitor
- Implements the mqttClientProxyMessage protocol from Meshtastic firmware
- Issues: GitHub Issues
- Meshtastic Discord: Join
- Version: 1.6.4
- Documentation: Configuration Guide | Architecture
- BLE Interface Support - Requires custom bleak implementation (see meshtastic-ble-bridge for reference)
- Metrics and monitoring endpoints
- Web UI for configuration
- Multi-node support
Status: Production Ready ✅
This application was developed with Antigravity and the help of Gemini