From a fresh Raspberry Pi to a running dashboard in under 5 minutes. The Setup Wizard is the recommended path — it generates a personalised install command tailored to your hardware and radio. Or follow the complete manual walkthrough below.
Raspberry Pi 2B+ or any ARM/x86 Linux. 512 MB RAM minimum, 1 GB recommended.
Raspberry Pi OS, Ubuntu 20.04+, Debian 11+, or any Linux with Python 3.9+. WSL2 supported.
3.9 or higher. pip3 + venv. The installer auto-installs missing Python packages on Debian/Ubuntu.
Any Meshtastic device via USB Serial, TCP/WiFi, Bluetooth BLE, or Web Serial (browser).
The Setup Wizard at meshdash.co.uk/c2_setup.php asks you a few questions and generates a single personalised curl | bash command. The command is pre-configured with your API key, version selection, and radio connection type. Paste it into your terminal and it handles everything.
The installer script has three template placeholders — {VERSION}, {API_KEY}, and {BASE_URL} — that must be populated before it will run. The Wizard fills these in and generates your personalised command. Running the raw install.sh without these filled in will immediately exit with an error explaining this.
Choose Raspberry Pi, Ubuntu server, or WSL2. The generated command adapts slightly per platform.
USB Serial (auto-detects port), TCP/WiFi, Bluetooth BLE, or Web Serial. Sets MESHTASTIC_CONNECTION_TYPE in your config.
Optional. Off by default. Choose heartbeat-only (community map) up to full operator remote access.
The Wizard outputs a single curl | bash command with your API key, version, and settings pre-baked. Paste it, run it, done.
Live node cards with battery, SNR, RSSI, GPS, and signal bars. SSE-powered — no page refresh. Track every node on your mesh at a glance.
Connect up to 16 Meshtastic radios simultaneously — Serial, TCP, BLE, MQTT, and MeshCore. Each slot gets its own database and data stream.
JWT in HttpOnly cookies, bcrypt hashing, CSRF double-submit protection, and optional TOTP 2FA. Built for exposed networks.
Nine telemetry metrics with 1-hour to 30-day ranges, network averages, and side-by-side node comparison charts.
Drop-in folder architecture with FastAPI router, static file serving, sidebar nav, and lifecycle management. Extend MeshDash without touching core code.
Wireshark-style packet inspector with BPF filter syntax, three-pane detail view, and source evidence scoring.
Five access tiers — heartbeat-only community map up to full operator control. HMAC-signed outbound-only polling, no port forwarding needed.
Regex-triggered auto-replies with dynamic placeholders and per-sender cooldowns. Cron-based message broadcasts and sensor ingress.
On Raspberry Pi OS, Ubuntu, or Debian. The installer tries to do this automatically but if running manually you'll need them first.
sudo apt update
sudo apt install -y wget unzip python3 python3-pip python3-venv \
build-essential python3-dev libxml2-dev libxslt-devOn Raspberry Pi also install: sudo apt install -y libgpiod2
mkdir -p ~/meshdash && cd ~/meshdash
# Download the selected version
wget -O mesh-dash.zip https://meshdash.co.uk/versions/R3.1.2/mesh-dash.zip
unzip -o mesh-dash.zip
rm mesh-dash.zipcd ~/meshdash/mesh-dash # extracted directory
python3 -m venv ../mesh-dash_venv
source ../mesh-dash_venv/bin/activate
pip install --upgrade pip
pip install --no-cache-dir -r requirements.txt--no-cache-dir to avoid RAM exhaustion. The installer uses this automatically.
sudo usermod -aG dialout $USER
# Then log out and back in, or run:
newgrp dialoutSkip this step for TCP, BLE, or Web Serial connection modes.
.mesh-dash_configMeshDash reads its config from .mesh-dash_config in the install directory. The Setup Wizard downloads this automatically. For manual setup, create it yourself:
cat > ~/meshdash/mesh-dash/.mesh-dash_config << 'EOF'
# ── Authentication ───────────────────────────────────────────
AUTH_SECRET_KEY=REPLACE_WITH_64_CHAR_HEX_STRING
AUTH_TOKEN_EXPIRE_MINUTES=10080
# ── Radio Connection ─────────────────────────────────────────
# Serial: MESHTASTIC_CONNECTION_TYPE=SERIAL
# TCP: MESHTASTIC_CONNECTION_TYPE=TCP
# BLE: MESHTASTIC_CONNECTION_TYPE=BLE
MESHTASTIC_CONNECTION_TYPE=SERIAL
MESHTASTIC_SERIAL_PORT=/dev/ttyACM0
# MESHTASTIC_HOST=192.168.1.50 # TCP only
# MESHTASTIC_PORT=4403 # TCP only
# MESHTASTIC_BLE_MAC=AA:BB:CC:DD:EE # BLE only
# ── Web Server ───────────────────────────────────────────────
WEBSERVER_HOST=0.0.0.0
WEBSERVER_PORT=8000
# ── Data Storage ─────────────────────────────────────────────
DB_PATH=meshtastic_data.db
TASK_DB_PATH=tasks.db
MAX_PACKETS_MEMORY=200
HISTORY_DAYS=30
LOG_LEVEL=INFO
PUBLIC_MODE=false
# ── Community / Remote Access ────────────────────────────────
COMMUNITY_API=false
COMMUNITY_API_KEY=
C2_ACCESS_LEVEL=read
HEARTBEAT_INTERVAL_MINUTES=1
C2_SYNC_INTERVAL_SECONDS=15
# ── Location Privacy ─────────────────────────────────────────
SEND_LOCAL_NODE_LOCATION=true
SEND_OTHER_NODES_LOCATION=true
LOCATION_OFFSET_ENABLED=false
LOCATION_OFFSET_METERS=0
# ── Connection Tuning ────────────────────────────────────────
SCHEDULER_MAX_RETRIES=3
SCHEDULER_RETRY_DELAY_SECONDS=10
SCHEDULER_CONNECT_TIMEOUT=10
SCHEDULER_RW_TIMEOUT=30
# ── Initial Admin (consumed once on first boot, then removed) ─
INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=changeme123
EOFpython3 -c "import secrets; print(secrets.token_hex(32))"
cd ~/meshdash/mesh-dash
source ../mesh-dash_venv/bin/activate
python3 meshtastic_dashboard.pyOpen http://<your-ip>:8000 in your browser. Default login: admin / changeme123 (change immediately in Settings).
# Create the service file
sudo tee /etc/systemd/system/mesh-dash.service << EOF
[Unit]
Description=MeshDash Service (Meshtastic Web Dashboard)
After=network.target network-online.target
Wants=network-online.target
[Service]
Type=simple
User=$USER
WorkingDirectory=$HOME/meshdash/mesh-dash
EnvironmentFile=$HOME/meshdash/mesh-dash/.mesh-dash_config
ExecStart=$HOME/meshdash/mesh-dash_venv/bin/python $HOME/meshdash/mesh-dash/meshtastic_dashboard.py
Restart=on-failure
RestartSec=10
TimeoutStopSec=30
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now mesh-dash
sudo systemctl status mesh-dashsudo systemctl status mesh-dash # check running state
sudo journalctl -u mesh-dash -f # live logs
sudo systemctl restart mesh-dash # restart
sudo systemctl stop mesh-dash # stop
Every key in .mesh-dash_config — sourced directly from the MeshDash codebase. Format: KEY=VALUE, one per line, comments start with #. After editing, restart the service: sudo systemctl restart mesh-dash.
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| AUTH_SECRET_KEY | string | — auto-generated | 64-character hex string that signs JWT session tokens. Never share. Changing it invalidates all active sessions. Auto-generated at startup if not set. |
| AUTH_TOKEN_EXPIRE_MINUTES | integer | 10080 (7 days) | Session lifetime in minutes. Tokens silently renew at /api/status when less than half their life remains. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| MESHTASTIC_CONNECTION_TYPE | SERIAL|TCP|BLE|WEBSERIAL | SERIAL | Connection method to the radio. |
| MESHTASTIC_SERIAL_PORT | path | /dev/ttyACM0 | Serial device path. Used when type is SERIAL. Check with: dmesg | grep tty |
| MESHTASTIC_HOST | IP address | 192.168.0.0 | IP of radio for TCP mode. |
| MESHTASTIC_PORT | integer | 4403 | TCP port. 4403 is the standard Meshtastic port. |
| MESHTASTIC_BLE_MAC | MAC address | — | Bluetooth MAC for BLE mode. Example: AA:BB:CC:DD:EE:FF |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| WEBSERVER_HOST | IP address | 0.0.0.0 | Bind address. 0.0.0.0 = accessible on LAN. 127.0.0.1 = local only. |
| WEBSERVER_PORT | integer | 8000 | Port the dashboard listens on. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| DB_PATH | path | meshtastic_data.db | Main SQLite database path. |
| TASK_DB_PATH | path | tasks.db | Task scheduler database path. |
| MAX_PACKETS_MEMORY | integer | 200 | Packets held in the in-memory deque for the live feed. Higher = more RAM. |
| HISTORY_DAYS | integer | 1 | Days of data retained. Records older are pruned hourly. Use 30, 90, etc. for longer history. |
| LOG_LEVEL | INFO|DEBUG|WARNING | INFO | DEBUG is very verbose — use for troubleshooting only. |
| PUBLIC_MODE | true|false | true until setup | When true, auth bypassed, databases in-memory. For demo/public-display only. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| COMMUNITY_API | true|false | false | Enables community map heartbeat and C2 remote access polling. |
| COMMUNITY_API_KEY | string | — | Your unique node API key. Generated by the Setup Wizard. |
| HEARTBEAT_INTERVAL_MINUTES | integer | 1 | How often (minutes) the C2 worker syncs with the community server. |
| C2_ACCESS_LEVEL | off|heartbeat|monitor|read|operator|full | read | What remote operations are permitted. See Remote Access page for tier details. |
| C2_SYNC_INTERVAL_SECONDS | integer | 15 | How often (seconds) to poll for pending remote requests. |
| C2_MAX_REQUESTS_PER_SYNC | integer | 10 | Maximum proxy requests processed per sync cycle. |
| C2_MAX_RESPONSE_KB | integer | 512 | Maximum size (KB) of any single proxied response. |
| C2_BLOCKED_ENDPOINTS | pipe-separated | — | Endpoints always blocked from remote access, overriding tier. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| MQTT_BROKER | hostname | mqtt.meshtastic.org | MQTT broker address. Set to enable MQTT connection mode. |
| MQTT_PORT | integer | 1883 | MQTT broker port. Use 8883 for TLS. |
| MQTT_USERNAME | string | — | Optional username for authenticated MQTT brokers. |
| MQTT_PASSWORD | string | — | Optional password for authenticated MQTT brokers. |
| MQTT_TLS | true|false | false | Enable TLS/SSL for the MQTT connection. |
| MQTT_REGION | region code | EU_868 | Region filter for topic subscription (e.g. EU_868, US_915). Use # for all regions. |
| MQTT_CHANNEL | channel name | # | Channel filter (e.g. LongFast). Use # for all channels. |
| MQTT_NODE_ID | hex node ID | — | Optional: your node ID. If set, matching NodeInfo packets promote that node to local. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| MESHCORE_TRANSPORT | serial|tcp|ble | serial | Transport method for MeshCore connection. |
| MESHCORE_SERIAL_PORT | path | — | Serial device path (when transport is serial). |
| MESHCORE_HOST | IP address | — | IP address for TCP transport. |
| MESHCORE_PORT | integer | 4000 | TCP port for MeshCore connection. |
| MESHCORE_BLE_MAC | MAC address | — | Bluetooth MAC for BLE transport. |
| MESHCORE_BAUD | integer | 115200 | Serial baud rate for MeshCore connection. |
| MESHCORE_LABEL | string | — | Human-readable label for this MeshCore node. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| SEND_LOCAL_NODE_LOCATION | true|false | true | Share your own node's GPS on the community map. |
| SEND_OTHER_NODES_LOCATION | true|false | true | Share positions of other nodes your radio hears. |
| LOCATION_OFFSET_ENABLED | true|false | false | Add a random offset to all reported positions. |
| LOCATION_OFFSET_METERS | float | 0.0 | Maximum fuzzing radius in metres when offset is enabled. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| SCHEDULER_MAX_RETRIES | integer | 3 | Reconnection attempts before backing off. |
| SCHEDULER_RETRY_DELAY_SECONDS | integer | 10 | Wait between retries. |
| SCHEDULER_CONNECT_TIMEOUT | float | 10.0 | Seconds to wait establishing a connection. |
| SCHEDULER_RW_TIMEOUT | float | 30.0 | Seconds to wait for read/write on an active connection. |
| KEY | TYPE / VALUES | DEFAULT | DESCRIPTION |
|---|---|---|---|
| INITIAL_ADMIN_USERNAME | string | — | Consumed on first boot to create the admin account, then auto-removed from config. |
| INITIAL_ADMIN_PASSWORD | string | — | Plaintext password consumed once, bcrypt-hashed into the DB, then removed. Never stored plain after setup. |
The rusjpmd/meshdash-runner:latest image auto-downloads the latest MeshDash version on first boot and keeps your data across updates. For a quick start without an account, just run the container and configure everything through the built-in /setup wizard. Upgrading from the V2.0 Docker container? Use the same meshdash_data volume — the R3.0 runner will detect, back up, and migrate your existing data automatically.
MeshDash ships an official Docker image via rusjpmd/meshdash-runner:latest. Two modes: Standalone (no account — configure via /setup wizard) or C2 Cloud Setup (generate a command at meshdash.co.uk and everything is pre-configured).
services:
meshdash:
image: rusjpmd/meshdash-runner:latest
container_name: meshdash
restart: unless-stopped
network_mode: host
privileged: true
volumes:
- meshdash_data:/app/data
- /dev:/dev
environment:
- WEBSERVER_PORT=8000
- TZ=Europe/London
volumes:
meshdash_data:docker compose up -d # start
docker compose logs -f meshdash # live logs
docker compose pull && docker compose up -d # update
docker compose down # stopdocker run -d \
--name meshdash \
--restart always \
--network host \
--privileged \
--log-opt max-size=10m --log-opt max-file=3 \
-v /dev:/dev \
-v meshdash_data:/app/data \
rusjpmd/meshdash-runner:latest
# Then open http://localhost:8000/setuprestart: unless-stopped in Compose for persistence.
The most reliable method. Plug the device into a USB port.
Connect over LAN when the device has WiFi enabled.
Experimental BLE support requires a Bluetooth adapter on the server.
The browser holds the USB port. No server serial required. Chrome/Edge only.
The documentation covers every common issue — serial permissions, Python version conflicts, radio connection timeouts, and browser WebSerial requirements.