# Meshtasticd SIM Node Runbook — LXC Deployment ## Overview This runbook covers deploying meshtasticd SIM (virtual) nodes inside LXC containers on Proxmox, each paired with a dedicated service (BBS, MeshSense, etc.). SIM nodes communicate with your real radio node over UDP and appear as normal nodes on the mesh — clients, maps, and other services can't tell the difference. **Design principle:** One container = one SIM daemon + one service. Clean isolation, easy to snapshot, migrate, or tear down without affecting anything else. --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ Proxmox Host │ │ │ │ ┌───────────────────┐ │ │ │ Real Radio Node │ (LXC, bare metal, Pi, or USB device) │ │ │ meshtasticd │ │ │ │ Port 4403 │ │ │ │ /dev/ttyUSB0 │ │ │ │ UDP enabled │ │ │ └────────┬──────────┘ │ │ │ UDP (vmbr0 or dedicated bridge) │ │ │ │ │ ┌─────┴─────┬───────────────┐ │ │ │ │ │ │ │ ┌──▼────────┐ ┌▼────────────┐ ┌▼────────────┐ │ │ │ LXC: BBS │ │ LXC: Sense │ │ LXC: Bot │ ...more as │ │ │ │ │ │ │ │ needed │ │ │ mesht. sim│ │ mesht. sim │ │ mesht. sim │ │ │ │ port 4403 │ │ port 4403 │ │ port 4403 │ │ │ │ + BBS svc │ │ + MeshSense │ │ + bot svc │ │ │ └───────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` Since each SIM daemon is alone in its container, they can all use the default port (4403) internally. No port juggling needed. --- ## Prerequisites - Proxmox host with LXC support - A working real radio meshtasticd instance somewhere on the network with UDP enabled - An LXC template (Ubuntu 22.04/24.04 or Debian 12 recommended) - Network bridge accessible to both the real radio node and LXC containers --- ## Step 1: Create the LXC Container From the Proxmox host CLI: ```bash # Create an unprivileged container with static IP and TUN device for Tailscale pct create local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \ --hostname mesh- \ --memory 512 \ --cores 1 \ --rootfs local-lvm:4 \ --net0 name=eth0,bridge=vmbr0,ip=192.168.1./24,gw=192.168.1.1 \ --unprivileged 1 \ --features nesting=1 \ --start 0 \ --password # Add TUN device for Tailscale (must be done before first start) cat >> /etc/pve/lxc/.conf << EOF lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF pct start ``` Adjust memory/cores/storage to taste. SIM daemons are lightweight — 512MB RAM and 1 core is plenty for the daemon plus most services. ### Bootstrap standard packages ```bash echo6-bootstrap-ct.sh ``` If the script isn't on the Proxmox host, run `echo6-onboard-node.sh` first. See `runbooks/proxmox-onboard-node.md`. --- ## Step 2: Install meshtasticd Inside the Container Install from the Meshtastic OBS (OpenSUSE Build Service) repository: ```bash pct exec -- bash -c " # Add the Meshtastic beta repo (Debian 12) curl -fsSL https://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_12/Release.key | \ gpg --dearmor -o /etc/apt/trusted.gpg.d/network_Meshtastic_beta.gpg echo 'deb http://download.opensuse.org/repositories/network:/Meshtastic:/beta/Debian_12/ /' > \ /etc/apt/sources.list.d/meshtasticd.list apt-get update -qq DEBIAN_FRONTEND=noninteractive apt-get install -y meshtasticd " ``` For Ubuntu 24.04 containers, replace `Debian_12` with `Debian_13` in both URLs above. Verify it's installed: ```bash pct exec -- meshtasticd --version ``` --- ## Step 3: Configure SIM Mode Create or edit the config file. Since this is a dedicated container, you can use the default paths. ### /etc/meshtasticd/config.yaml ```yaml Lora: Module: sim General: # Prevent loading any default config.d overrides # ConfigDirectory: /etc/meshtasticd/config.d/ # REQUIRED: Set a unique MAC address for this SIM node # Last 3 hex pairs = node color in client apps MACAddress: "DE:AD:00:FF:00:01" ``` ### MAC Address Guidelines - **Every SIM node must have a unique MAC.** If two nodes share a MAC, you'll get node ID collisions and unpredictable behavior. - The last 3 byte pairs map to a hex color code displayed in client apps. - Pick a scheme that makes sense for your deployment, e.g.: - `DE:AD:00:FF:00:01` — SIM node 1 (BBS) - `DE:AD:00:00:FF:02` — SIM node 2 (MeshSense) - `DE:AD:00:FF:FF:03` — SIM node 3 (bot) --- ## Step 4: Enable UDP Bridging UDP is what connects SIM nodes to the rest of your mesh. Every meshtasticd instance — real radio and all SIM nodes — needs UDP enabled and must be able to reach each other on the network. Add to your SIM node's config: ```yaml Networking: EnableUDP: true ``` **Also ensure your real radio node has UDP enabled** in its own config. ### Network Considerations For UDP mesh traffic to flow between LXC containers and your real radio node: - All containers and the real radio host must be on the **same Layer 2 network** (same bridge, same subnet) — UDP broadcast/multicast needs to reach all instances. - If your real radio runs on a different host (e.g., a Raspberry Pi), make sure it's on the same VLAN/subnet as the LXC bridge. - Proxmox's default `vmbr0` bridge works fine if everything is on the same network. - If you're running the real radio in its own LXC and passing through USB, the same bridge rules apply. **Firewall note:** If you have Proxmox firewall or iptables rules on the host, ensure UDP traffic between containers is not blocked. Meshtasticd uses UDP broadcast by default — verify your bridge allows broadcast forwarding. --- ## Step 5: Configure the systemd Service The meshtasticd package likely installs a default unit file. If you need to customize it: ```bash sudo systemctl edit meshtasticd --full ``` Or create/verify `/etc/systemd/system/meshtasticd.service`: ```ini [Unit] Description=Meshtastic Daemon - SIM Node After=network.target [Service] Type=simple User=meshtasticd Group=meshtasticd ExecStart=/usr/bin/meshtasticd -d /var/lib/meshtasticd/vfs -c /etc/meshtasticd/config.yaml Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target ``` Since this is the only meshtasticd instance in the container, you don't need custom port flags — the default port is fine. Ensure directory ownership: ```bash sudo mkdir -p /var/lib/meshtasticd/vfs sudo chown -R meshtasticd:meshtasticd /var/lib/meshtasticd sudo chown -R meshtasticd:meshtasticd /etc/meshtasticd ``` Start and enable: ```bash sudo systemctl daemon-reload sudo systemctl enable --now meshtasticd sudo systemctl status meshtasticd ``` --- ## Step 5b: Install Tailscale and Register with Headscale Install Tailscale inside the container: ```bash pct exec -- bash -c " echo nameserver 1.1.1.1 > /etc/resolv.conf echo nameserver 8.8.8.8 >> /etc/resolv.conf curl -fsSL https://tailscale.com/install.sh | sh " ``` Generate a preauth key on Contabo (user ID 1 = echo6): ```bash ssh root@100.64.0.1 'docker exec headscale headscale preauthkeys create --user 1 --reusable --expiration 1h' ``` Register the node: ```bash pct exec -- tailscale up --login-server https://vpn.echo6.co --authkey --hostname mesh- # Verify pct exec -- tailscale status ``` **Note:** The TUN device must already be configured in the container config (done in Step 1). If Tailscale fails to start, verify `/dev/net/tun` exists inside the container. --- ## Step 6: Configure the SIM Node With the daemon running and no service connected yet, configure it via CLI: ```bash # Install the Meshtastic Python CLI pip install meshtastic # Check that the node is up meshtastic --host localhost --info # Set a descriptive name meshtastic --host localhost --set-owner "BBS Node" meshtastic --host localhost --set-owner-short "BBS" # Optionally set a position (makes it appear on mesh maps) meshtastic --host localhost --setlat XX.XXXX --setlon -XXX.XXXX --setalt XXXX # Set the node role as appropriate meshtastic --host localhost --set device.role CLIENT ``` If you don't set a position, the node still functions on the mesh but won't appear on maps. --- ## Step 7: Install and Connect Your Service Now install whichever service this container is dedicated to and point it at `localhost:4403` (or whatever the default meshtasticd API port is). ### Example: BBS ```bash # Install your BBS software of choice # Point it at the local meshtasticd instance # BBS_CONFIG: host=localhost, port=4403 ``` ### Example: MeshSense ```bash # MeshSense supports non-default ports # Configure it to connect to localhost:4403 ``` **Reminder:** One client API connection per daemon. Once the service is connected, don't also try to connect a client app to the same instance. If you need to reconfigure the node, stop the service first. --- ## Provisioning Additional Containers For each new service, repeat steps 1–7 with: 1. A new container ID and hostname (e.g., `mesh-sense`, `mesh-bot`) 2. A **unique MAC address** in config.yaml 3. The specific service installed alongside meshtasticd ### Quick Clone Approach Once you have one container fully set up, you can clone it in Proxmox and just change: - Container hostname - MAC address in `/etc/meshtasticd/config.yaml` - The service installed/configured - Node owner name via `meshtastic --host localhost --set-owner "NewName"` ```bash # Clone from Proxmox CLI pct clone 201 202 --hostname mesh-sense --full pct start 202 pct enter 202 # Update the MAC address nano /etc/meshtasticd/config.yaml # change MACAddress # Restart meshtasticd to pick up new MAC systemctl restart meshtasticd # Reconfigure node identity meshtastic --host localhost --set-owner "MeshSense" ``` --- ## USB Passthrough for Real Radio Container If you want your real radio node in an LXC too (rather than bare metal), you need to pass the USB device through to the container. On the Proxmox host, find the device: ```bash ls -la /dev/serial/by-id/ # or lsusb ``` Add to the container config (`/etc/pve/lxc/.conf`): ``` lxc.cgroup2.devices.allow: c 188:* rwm lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file ``` Adjust the device path and cgroup major number as needed for your hardware. The container will also need a `config.yaml` with the real LoRa module config instead of `Module: sim`. --- ## Troubleshooting ### SIM nodes not seeing the real radio (or each other) - Verify UDP is enabled on **all** instances - Confirm all containers are on the same bridge/subnet - Check for firewall rules blocking UDP broadcast between containers - Test basic connectivity: `ping` between containers ### Node ID collisions / "things get weird" - Every SIM node must have a **unique MAC address** — check each container's config.yaml - After changing a MAC, restart meshtasticd and verify with `meshtastic --host localhost --info` ### Service can't connect to meshtasticd - Is meshtasticd actually running? `systemctl status meshtasticd` - Is another client already connected? Only one API connection per instance. - Check the port: `ss -tlnp | grep 4403` ### Config not taking effect - Make sure `ConfigDirectory` is commented out or pointed somewhere empty so default config.d files don't override your settings - Restart after config changes: `systemctl restart meshtasticd` ### Permission errors - VFS and config directories must be owned by the `meshtasticd` user ```bash chown -R meshtasticd:meshtasticd /var/lib/meshtasticd chown -R meshtasticd:meshtasticd /etc/meshtasticd ``` --- ## Quick Reference ```bash # Inside any SIM container: systemctl status meshtasticd # Check daemon status journalctl -u meshtasticd -f # Follow logs meshtastic --host localhost --info # Node info (only if no service is connected) # From Proxmox host: pct list # List all containers pct enter # Shell into container pct exec -- systemctl status meshtasticd # Check without entering ``` --- ## Container Inventory Template Track your deployment: | CTID | Hostname | MAC Address | Service | Port | Notes | |------|-------------|---------------------|------------|------|-----------------| | 200 | mesh-radio | (hardware) | Real radio | 4403 | USB passthrough | | 201 | mesh-bbs | DE:AD:00:FF:00:01 | BBS | 4403 | — | | 202 | mesh-sense | DE:AD:00:00:FF:02 | MeshSense | 4403 | — | | 203 | mesh-bot | DE:AD:00:FF:FF:03 | Bot | 4403 | — | --- ## Credits Procedure sourced from a community discussion between pdxlocs, tedward, and wehooper4 regarding multi-daemon meshtasticd deployments with SIM mode. Adapted for LXC/Proxmox deployment.