echo6-docs/runbooks/meshtastic-sidecar-node.md
Matt Johnson e9231ac24a Migration: consolidate Echo6 docs to cortex with full infrastructure cleanup sync
- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup)
- Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing
- Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack
- Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure
- Removes 4 deprecated runbook duplicates (canonical versions live in projects/)
- Adds .gitignore for binary archives and editor temp files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 06:02:16 +00:00

17 KiB

Meshtastic Sidecar Node — Modular Deployment Runbook

Deploy a Raspberry Pi node with a real Meshtastic radio and an operator-selected combination of software modules. Each module is self-contained — install only what the site needs.


Input Variables

Fill these in before running any module:

Variable Value Description
HOSTNAME Node hostname (e.g., mt-isr)
NODE_IP Local IP address (e.g., 192.168.1.112)
SSH_USER SSH username (e.g., isr)
SSH_PASS SSH password (from .ref/credentials)
RADIO_DEVICE Serial device for radio (e.g., /dev/ttyACM0)
OS_VERSION debian-12 (bookworm) or debian-13 (trixie)

Module Menu

Select which modules to deploy. Check off as completed:

Meshtastic Sidecar Node Deployment: $HOSTNAME ($NODE_IP)

Select modules to install:
[        ] Module 1: IP Settings — Static IP, hostname, DNS
[        ] Module 2: Tailscale — Headscale VPN registration
[        ] Module 3: meshtasticd — Meshtastic radio daemon
[        ] Module 4: Meshtastic Python — meshtastic CLI + Python API
[        ] Module 5: advBBS — Bulletin board system
[        ] Module 6: Meshing-Around — Mesh bot + WebGUI
[        ] Module 7: MeshMonitor — Feed mesh data to monitoring

Dependencies

Module 1 ─── standalone
Module 2 ─── standalone
Module 3 ─── standalone (but needed by 4, 5, 6)
Module 4 ─── requires Module 3
Module 5 ─── requires Module 3
Module 6 ─── requires Module 3
Module 7 ─── requires Module 2

Module 1: IP Settings

Set hostname

sshpass -p '$SSH_PASS' ssh $SSH_USER@$NODE_IP

sudo hostnamectl set-hostname $HOSTNAME
echo "127.0.1.1  $HOSTNAME" | sudo tee -a /etc/hosts

Configure static IP (if not using DHCP reservation)

For NetworkManager-managed systems (Raspberry Pi OS with desktop):

sudo nmcli con mod "preconfigured" \
  ipv4.method manual \
  ipv4.addresses "$NODE_IP/24" \
  ipv4.gateway "192.168.1.1" \
  ipv4.dns "1.1.1.1,8.8.8.8"
sudo nmcli con up "preconfigured"

For headless systems with /etc/network/interfaces:

sudo tee /etc/network/interfaces.d/eth0 << EOF
auto eth0
iface eth0 inet static
    address $NODE_IP/24
    gateway 192.168.1.1
    dns-nameservers 1.1.1.1 8.8.8.8
EOF
sudo systemctl restart networking

For WiFi-only Pis, use wlan0 instead of eth0 and configure via wpa_supplicant or NetworkManager.

Configure DNS fallback

echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf

Verify

hostname                        # Should show $HOSTNAME
ip addr show                    # Should show $NODE_IP
ping -c 3 1.1.1.1               # Internet connectivity
ping -c 3 192.168.1.1            # Gateway reachable

Module 2: Tailscale

Choose tailnet

Tailnet Headscale URL Prefix Key generation
Echo6 https://vpn.echo6.co 100.64.0.0/10 On Contabo
IdahoMesh https://vpn.idahomesh.com 100.100.0.0/16 On CT 106

Install Tailscale

curl -fsSL https://tailscale.com/install.sh | sh

Generate preauthkey

Echo6 (from cortex or any machine with Tailscale access to Contabo):

ssh root@100.64.0.1 'docker exec headscale headscale preauthkeys create --user 1 --reusable --expiration 1h'

IdahoMesh (from utility Proxmox host):

ssh root@192.168.1.241 'pct exec 106 -- headscale preauthkeys create --user <USER_ID> --expiration 24h'

Users: malice (4), sidpatchy (2), nebra (3)

Register with Headscale

sudo tailscale up \
  --login-server=$HEADSCALE_URL \
  --authkey=$PREAUTH_KEY \
  --hostname=$HOSTNAME

Install DNS bootstrap drop-in (reboot-safe)

Prevents chicken-and-egg DNS failure where tailscaled can't resolve the coordination server after reboot:

sudo mkdir -p /etc/systemd/system/tailscaled.service.d
sudo tee /etc/systemd/system/tailscaled.service.d/dns-bootstrap.conf << 'EOF'
[Service]
ExecStartPre=/bin/sh -c "grep -q nameserver /etc/resolv.conf || echo nameserver 1.1.1.1 > /etc/resolv.conf"
EOF
sudo systemctl daemon-reload

Verify

tailscale status          # Should show connected
tailscale ip -4           # Should show 100.64.x.x or 100.100.x.x
ping -c 3 100.64.0.1     # Echo6: ping Contabo
ping -c 3 100.100.0.1    # IdahoMesh: ping Headscale

Enable accept-routes (if needed)

Required to reach subnets advertised by the mesh-bridge (CT 107):

sudo tailscale up --login-server=$HEADSCALE_URL --accept-routes

Module 3: meshtasticd

Detect OS and add OBS repo

Debian 12 (bookworm):

curl -fsSL https://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_12/Release.key | \
  sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/network_Meshtastic_beta.gpg
echo 'deb http://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_12/ /' | \
  sudo tee /etc/apt/sources.list.d/meshtasticd.list

Debian 13 (trixie) / Ubuntu 24.04:

curl -fsSL https://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_13/Release.key | \
  sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/network_Meshtastic_beta.gpg
echo 'deb http://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_13/ /' | \
  sudo tee /etc/apt/sources.list.d/meshtasticd.list

Install

sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y meshtasticd

Configure — choose hardware type

Option A: USB radio (/dev/ttyACM0 or /dev/ttyUSB0)

sudo tee /etc/meshtasticd/config.yaml << EOF
Lora:
  Module: sx1262
  DIO2_AS_RF_SWITCH: true
  CS: 8
  IRQ: 22
  Busy: 27
  Reset: 17

Serial:
  Enabled: true
  Device: $RADIO_DEVICE

Networking:
  EnableUDP: true
EOF

Note: If using a USB-connected Meshtastic device (RAK, T-Beam, etc.), the Lora section should be removed entirely and the device is managed via the serial interface. The config becomes:

sudo tee /etc/meshtasticd/config.yaml << EOF
Serial:
  Enabled: true
  Device: $RADIO_DEVICE

Networking:
  EnableUDP: true
EOF

Option B: Nebra SX1262 Pi Hat (e.g., aida-nebra)

sudo tee /etc/meshtasticd/config.yaml << EOF
Lora:
  Module: sx1262
  DIO2_AS_RF_SWITCH: true
  CS: 24
  IRQ: 22
  Busy: 27
  Reset: 17

Networking:
  EnableUDP: true
EOF

Option C: SIM mode (no physical radio, virtual mesh node)

sudo tee /etc/meshtasticd/config.yaml << EOF
Lora:
  Module: sim

General:
  MACAddress: "DE:AD:00:XX:XX:XX"

Networking:
  EnableUDP: true
EOF

Fix permissions

sudo mkdir -p /var/lib/meshtasticd/vfs
sudo chown -R meshtasticd:meshtasticd /var/lib/meshtasticd
sudo chown -R meshtasticd:meshtasticd /etc/meshtasticd

Add user to dialout group (for serial access)

sudo usermod -a -G dialout meshtasticd

Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now meshtasticd

Verify

sudo systemctl status meshtasticd            # Should be active (running)
sudo journalctl -u meshtasticd -n 30         # Check for radio detection
ss -tlnp | grep 4403                          # API port listening

Look for lines like Connected to radio or Detected module: sx1262 in the journal output.


Module 4: Meshtastic Python

Requires: Module 3 (meshtasticd must be running)

Install

sudo apt-get install -y python3-pip python3-venv
pip3 install --break-system-packages meshtastic

Or in a venv if the system enforces PEP 668:

python3 -m venv /opt/meshtastic-cli
/opt/meshtastic-cli/bin/pip install meshtastic
sudo ln -sf /opt/meshtastic-cli/bin/meshtastic /usr/local/bin/meshtastic

Set node identity

# Set long name (visible in client apps and mesh maps)
meshtastic --host localhost --set-owner "$HOSTNAME"

# Set short name (4 chars max, shown in compact views)
meshtastic --host localhost --set-owner-short "${HOSTNAME:0:4}"

Set device role

Role Use case
CLIENT Default, standard mesh participant
CLIENT_MUTE Receives but doesn't rebroadcast
ROUTER Prioritizes forwarding, minimal local traffic
ROUTER_CLIENT Router + normal client features
meshtastic --host localhost --set device.role CLIENT

Set position (optional, for mesh maps)

meshtastic --host localhost --setlat XX.XXXX --setlon -XXX.XXXX --setalt XXXX

Verify

meshtastic --host localhost --info

Should show node name, ID, role, and radio parameters. If it says "Error connecting," verify meshtasticd is running and no other client is connected to port 4403.

Important: Only ONE client can connect to the meshtasticd API at a time. If a service (advBBS, Meshing-Around) is already connected, disconnect it first before using the CLI.


Module 5: advBBS

Requires: Module 3 (meshtasticd)

Install prerequisites

sudo apt-get install -y python3-pip python3-venv git docker.io docker-compose
sudo systemctl enable --now docker
sudo usermod -a -G docker $SSH_USER

Clone and configure

sudo mkdir -p /opt/advbbs
cd /opt/advbbs
sudo git clone https://forge.echo6.co/advbbs/advbbs.git .
sudo cp config.example.toml config.toml

Edit config.toml:

[bbs]
name = "$HOSTNAME BBS"
callsign = "${HOSTNAME^^}"
admin_password = "CHANGE_THIS"

[meshtastic]
connection_type = "tcp"
tcp_host = "localhost"
tcp_port = 4403

[database]
backup_path = "/data/backups"
backup_interval_hours = 24

[sync]
enabled = true
# Add federation peers as needed:
# [[sync.peers]]
# node_id = "!abc12345"
# name = "REMOTE-BBS"
# protocol = "advbbs"
# enabled = true

Deploy with Docker

Standard (x86):

cd /opt/advbbs
sudo docker-compose up -d

Raspberry Pi (memory-optimized):

cd /opt/advbbs
sudo docker-compose -f docker-compose.rpi.yml build
sudo docker-compose -f docker-compose.rpi.yml up -d

Deploy without Docker (native, for minimal Pi setups)

cd /opt/advbbs
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Create systemd service
sudo tee /etc/systemd/system/advbbs.service << EOF
[Unit]
Description=advBBS Meshtastic BBS
After=network.target meshtasticd.service
Requires=meshtasticd.service

[Service]
Type=simple
User=$SSH_USER
WorkingDirectory=/opt/advbbs
ExecStart=/opt/advbbs/venv/bin/python -m advbbs
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now advbbs

Verify

sudo docker-compose logs -f          # Docker
sudo journalctl -u advbbs -f         # Native

# From another mesh node, DM the BBS node:
# !help
# Should get a response listing commands

Module 6: Meshing-Around

Requires: Module 3 (meshtasticd)

sudo apt-get install -y docker.io docker-compose
sudo systemctl enable --now docker

sudo mkdir -p /opt/meshing-around
cd /opt/meshing-around

# Clone from upstream
sudo git clone https://github.com/SpudGunMan/meshing-around.git .

# Create config — point at local meshtasticd
# Edit mesh.ini or config file as needed:
# interface_type = tcp
# hostname = localhost
# port = 4403

sudo docker-compose up -d

WebGUI accessible at http://$NODE_IP:8085

Option B: Native Python (for RPi Zero 2 W or low-memory nodes)

sudo mkdir -p /opt/meshing-around
cd /opt/meshing-around
sudo git clone https://github.com/SpudGunMan/meshing-around.git .
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Create systemd service
sudo tee /etc/systemd/system/meshing-around.service << EOF
[Unit]
Description=Meshing-Around Mesh Bot + WebGUI
After=network.target meshtasticd.service
Requires=meshtasticd.service

[Service]
Type=simple
User=$SSH_USER
WorkingDirectory=/opt/meshing-around
ExecStart=/opt/meshing-around/venv/bin/python mesh_bot.py
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now meshing-around

Verify

sudo docker-compose logs -f                  # Docker
sudo journalctl -u meshing-around -f         # Native
curl -s http://localhost:8085 | head -5       # WebGUI responds

Module 7: MeshMonitor

Requires: Module 2 (Tailscale, to reach MeshMonitor at 100.64.0.7)

MeshMonitor runs on CT 100 (192.168.1.100 / 100.64.0.7:8080). This module configures the sidecar node to report mesh data to the MeshMonitor instance.

Configure MeshMonitor connection

MeshMonitor connects to sidecar nodes via the meshtasticd TCP API. The sidecar node needs to be reachable from MeshMonitor over Tailscale.

  1. Ensure meshtasticd is running and listening on port 4403 (Module 3)
  2. Ensure Tailscale is connected (Module 2) so MeshMonitor can reach this node
  3. Register the node in MeshMonitor's web UI:
# MeshMonitor admin credentials (from .ref/credentials)
# URL: http://100.64.0.7:8080
# User: admin
# Pass: 7redditGold

# Open MeshMonitor web UI and add this node:
# - Node address: $TAILSCALE_IP:4403
# - Node name: $HOSTNAME

Verify

# Confirm Tailscale can reach MeshMonitor
ping -c 3 100.64.0.7
curl -s http://100.64.0.7:8080 | head -5

# Confirm meshtasticd API is accessible from the network
ss -tlnp | grep 4403

Post-Deploy Checklist

[ ] IP / hostname configured (Module 1)
[ ] Tailscale connected to correct tailnet (Module 2, if selected)
[ ] meshtasticd running, radio detected (Module 3, if selected)
[ ] Node visible on mesh (check from another node)
[ ] Selected service modules running (advBBS, Meshing-Around, etc.)
[ ] All services survive reboot: sudo reboot && verify after
[ ] Credentials logged in .ref/credentials
[ ] Node added to environment.md (IP, Tailscale IP, purpose)
[ ] Node added to services.md (if running services)

Troubleshooting

meshtasticd won't start

sudo journalctl -u meshtasticd -n 50 --no-pager

# Common issues:
# - Serial device not found: check ls -la /dev/ttyACM0 (or ttyUSB0)
# - Permission denied: sudo chown -R meshtasticd:meshtasticd /var/lib/meshtasticd /etc/meshtasticd
# - Wrong OBS repo for OS version: Debian 12 vs 13 repo URL mismatch

Radio not detected

# List serial devices
ls -la /dev/ttyACM* /dev/ttyUSB*

# Check kernel messages for USB events
dmesg | tail -20

# If device was unplugged/replugged, restart meshtasticd
sudo systemctl restart meshtasticd

Meshtastic CLI can't connect

# Only ONE client can connect at a time
# Stop any running services first:
sudo systemctl stop advbbs meshing-around 2>/dev/null

# Then try CLI
meshtastic --host localhost --info

# Restart services when done
sudo systemctl start advbbs meshing-around 2>/dev/null

Tailscale won't connect after reboot

# Check if DNS bootstrap drop-in is installed
cat /etc/systemd/system/tailscaled.service.d/dns-bootstrap.conf

# If missing, install it (Module 2)
# If present, check resolv.conf
cat /etc/resolv.conf

# Force fallback DNS and restart
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
sudo systemctl restart tailscaled
tailscale status

advBBS won't connect to meshtasticd

# Verify meshtasticd is running and API port is open
ss -tlnp | grep 4403

# Check advBBS config points to localhost:4403
grep -A 3 '\[meshtastic\]' /opt/advbbs/config.toml

# Verify no other client is holding the connection
# meshtasticd only allows ONE concurrent API client

Node not visible on mesh

# Check meshtasticd logs for radio status
sudo journalctl -u meshtasticd | grep -i -E "radio|connect|error"

# Verify UDP is enabled (for SIM nodes or multi-daemon setups)
grep -i udp /etc/meshtasticd/config.yaml

# Verify node identity is set
meshtastic --host localhost --info 2>/dev/null || echo "Another client connected — stop services first"

Low memory (RPi Zero 2 W)

The RPi Zero 2 W has ~416MB RAM. Monitor usage:

free -h
# If memory is tight:
# - Use native Python installs instead of Docker
# - Only run ONE service alongside meshtasticd
# - Use advBBS's docker-compose.rpi.yml if using Docker
# - Disable web-reader profile to save memory

Quick Reference

# Service management
sudo systemctl status meshtasticd
sudo systemctl status advbbs
sudo systemctl status meshing-around
sudo journalctl -u meshtasticd -f

# Mesh CLI (stop services first!)
meshtastic --host localhost --info
meshtastic --host localhost --set-owner "NodeName"
meshtastic --host localhost --set device.role CLIENT

# Tailscale
tailscale status
tailscale ip -4
tailscale ping <other-node>

Existing Nodes Reference

Node IP User Radio OS Modules
aida-nebra 192.168.1.253 zvx Nebra SX1262 Hat RPi OS 2 (Echo6), 3, 4
mt-burleybutte 192.168.1.185 bb Nebra SX1262 Hat RPi OS 2 (IdahoMesh), 3, 4
mt-isr 192.168.1.112 isr USB /dev/ttyACM0 Debian 13 3 (installed, inactive)

Last updated: 2026-02-21