- 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>
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
Lorasection 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)
Option A: Docker (recommended for nodes with >1GB RAM)
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.
- Ensure meshtasticd is running and listening on port 4403 (Module 3)
- Ensure Tailscale is connected (Module 2) so MeshMonitor can reach this node
- 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