# Copyright © 2025-26 l5yth & contributors # Licensed under the Apache License, Version 2.0 (see LICENSE) # # PotatoMesh Environment Configuration # Copy this file to .env and customize for your setup # ============================================================================= # REQUIRED SETTINGS # ============================================================================= # Public domain name for this PotatoMesh instance (required for webapp) # Provide a hostname (with optional port) that resolves to the web service. # Example: mesh.example.org or mesh.example.org:41447 INSTANCE_DOMAIN="mesh.example.org" # API authentication token (required for ingestor communication) # Generate a secure token: openssl rand -hex 32 API_TOKEN="your-secure-api-token-here" # Mesh radio connection target (required for ingestor) # Common serial paths: # - Linux: /dev/ttyACM0, /dev/ttyUSB0 # - macOS: /dev/cu.usbserial-* # - Windows (WSL): /dev/ttyS* # You may also provide an IP:PORT pair (e.g. 192.168.1.20:4403) or a # Bluetooth address (e.g. ED:4D:9E:95:CF:60). CONNECTION="/dev/ttyACM0" # Mesh protocol to use (meshtastic, meshcore, or reticulum) # Default: meshtastic PROTOCOL="meshtastic" # --- PROTOCOL=reticulum only ------------------------------------------------- # Reticulum (RNS) config directory for the passive announce listener. # Defaults to ~/.reticulum, the stack rnsd already uses. Point it elsewhere # only if the ingestor should read a different RNS config. # RETICULUM_CONFIG_DIR="/home/you/.reticulum" # Restrict announce ingestion to named RNS interfaces (comma-separated, # case-insensitive substring match, e.g. "rnode"). An RNS stack may carry LoRa # and IP interfaces at once, and RNS's default config enables an AutoInterface # (IPv6 link-local multicast) — leaving this empty ingests every announce # reachable over any interface, which on a LAN means the whole local Reticulum # network. Empty is the default. # RETICULUM_INTERFACES="rnode" # INGESTOR_NODE_ID (see below) is an OVERRIDE here, not a requirement. The # ingestor derives its !xxxxxxxx from your primary identity -- the one announcing # the most destinations on this machine -- so the heartbeat and the packets/hour # stats come up unaided. It is not the transport identity. Set INGESTOR_NODE_ID # to override the pick, or where no one identity announces the most. # # Changing the id strands the old row: the previous ingestor stays in # /api/ingestors without heartbeats until it ages out. Leaving the variable # as-is across upgrades is inert. # # CONNECTION does not apply. It names one serial/TCP/BLE endpoint, which an RNS # stack of many interfaces does not have. Its counterparts are the two variables # above: RETICULUM_CONFIG_DIR says which stack, RETICULUM_INTERFACES says which # of its interfaces to ingest from. A set CONNECTION is ignored and said so at # startup, because the shipped image carries a serial default for every protocol. # ----------------------------------------------------------------------------- # ============================================================================= # SITE CUSTOMIZATION # ============================================================================= # Your mesh network name SITE_NAME="My Meshtastic Network" # Meshtastic radio preset shown in the join strip (CHANNEL is a deprecated alias) MESHTASTIC_PRESET="#LongFast" # Meshtastic frequency for your region (FREQUENCY is a deprecated alias) # Common frequencies: 868MHz (Europe), 915MHz (US), 433MHz (Worldwide) MESHTASTIC_FREQ="915MHz" # Meshcore preset and frequency. The join strip hides the Meshcore line until # both are set. # MESHCORE_PRESET="MediumFast" # MESHCORE_FREQ="867.5MHz" # Announcement banner rendered above the header on every page # ANNOUNCEMENT="Net every Tuesday 20:00" # Directory for static, custom-content pages served at /pages/:slug # PAGES_DIR=./pages # Absolute http(s) URL for the social preview image. Replaces the generated # /og-image.png for deployments without Chromium. # OG_IMAGE_URL="https://cdn.example.org/preview.png" # Map center coordinates (latitude, longitude) # Berlin, Germany: 52.502889, 13.404194 # Denver, Colorado: 39.7392, -104.9903 # London, UK: 51.5074, -0.1278 MAP_CENTER="38.761944,-27.090833" # Maximum distance to show nodes (kilometers) MAX_DISTANCE=42 # ============================================================================= # OPTIONAL INTEGRATIONS # ============================================================================= # Community chat link or Matrix room for your community (optional) # Matrix aliases (e.g. #meshtastic-berlin:matrix.org) will be linked via matrix.to automatically. CONTACT_LINK="#potatomesh:dod.ngo" # Enable or disable PotatoMesh federation features (1=enabled, 0=disabled) FEDERATION=1 # Hide public mesh messages from unauthenticated visitors (1=hidden, 0=public) PRIVATE=0 # Comma-separated channel names to ingest. Unset = ingest all channels. # ALLOWED_CHANNELS=LongFast,Ops # Comma-separated channel names to drop. Applied after ALLOWED_CHANNELS. # HIDDEN_CHANNELS=Private # Drop Meshtastic packets and nodes relayed via MQTT (0=keep, 1=drop). # DROP_VIA_MQTT=1 # Live-update SSE stream at /api/events (1=on, 0=off -> clients poll instead) EVENTS=1 # Comma-separated node ids to expose as per-node Prometheus gauges. # Unset exports none. See PROMETHEUS.md. # PROM_REPORT_IDS=!abcd1234,!beef5678 # ============================================================================= # TRANSMIT SETTINGS (your ingestor putting traffic on the air) # ============================================================================= # # By default a PotatoMesh ingestor is a LISTENER: it receives, and it transmits # nothing. Everything below is off unless you deliberately turn it on, because # airtime is a shared resource and your community's norms about automated # traffic are yours to set, not ours. # Master transmit switch (0=receive only, 1=allow transmitting) # Leave at 0 and the ingestor never puts anything on the mesh. # Turning this on enables MeshCore on-air telemetry polling of other nodes, # which is how their battery/voltage readings reach your dashboard. # Does NOT by itself enable announcements — see TX_ANNOUNCE. # Local reads over USB/BLE to your own radio are not transmissions and always # work, whatever this is set to. TX_ENABLED=0 # Periodic activity announcement (0=off, 1=on). Requires TX_ENABLED=1. # Broadcasts ONE line on your default channel, at most once every 24 hours, and # never in the first 24 hours after start: # "Meshtastic activity in the last 24h: 42 active nodes, 118 packets/hour. https://your.domain" # This is unsolicited automated traffic on a channel people read. Check that # your community wants it before enabling. TX_ANNOUNCE=0 # ============================================================================= # ADVANCED SETTINGS # ============================================================================= # Debug mode (0=off, 1=on) DEBUG=0 # Energy saving mode — sleep between ingestion cycles (0=off, 1=on) ENERGY_SAVING=0 # Channel index the activity announcement is sent on (not an ingest filter) CHANNEL_INDEX=0 # Seconds between Meshcore host self-telemetry reads over the companion link # (no airtime). 0 disables. # MESHCORE_SELF_TELEMETRY_SECONDS=3600 # Seconds between Meshcore contact telemetry polls. Requires TX_ENABLED=1 # (polling other nodes transmits). 0 disables on-air polling. # MESHCORE_TELEMETRY_POLL_SECONDS=300 # Comma-separated node ids exempt from the once-per-24h telemetry cooldown; # listed nodes are re-polled every round-robin rotation. Requires TX_ENABLED=1. # MESHCORE_TELEMETRY_POLL_24H_EXEMPT=!abc12345,!def67890 # Default map zoom override # MAP_ZOOM=15 # Docker image architecture (linux-amd64, linux-arm64, linux-armv7) POTATOMESH_IMAGE_ARCH="linux-amd64" # Docker image tag (use "latest" for the newest release or pin to vX.Y) POTATOMESH_IMAGE_TAG="latest" # Docker Compose networking profile # Leave unset for Linux hosts (default host networking). # Set to "bridge" on Docker Desktop (macOS/Windows) if host networking # is unavailable. # COMPOSE_PROFILES="bridge" # ============================================================================= # PASSIVE UDP TRANSPORT # ============================================================================= # Ingest by passively listening to the node's "Mesh via UDP" LAN multicast # instead of holding the radio's single API/serial slot. Enable it on the node # first: `meshtastic --set network.enabled_protocols 1`. Requires host # networking (multicast can't reach a bridged container). See the # "Passive UDP transport" section of README.md. # Transport: "api" (Meshtastic library over serial/TCP/BLE) or "udp" (passive). # TRANSPORT=udp # Ingest only channel 0. In UDP mode filtering is unconditional; this flag only # affects the api/serial transport. # PRIMARY_CHANNEL_ONLY=1 # Base64 primary-channel PSK (Meshtastic default key shown). # PRIMARY_CHANNEL_KEY=AQ== # Name of channel 0 — the preset name (MediumFast/LongFast/...) when the radio # leaves the name blank, as shown by `meshtastic --info`. REQUIRED for UDP # primary-only: it resolves the channel hash that separates the primary channel # from a secondary channel sharing the default key. If unset, UDP mode drops ALL # traffic (fail closed). # PRIMARY_CHANNEL_NAME=MediumFast # Host node id used for the ingestor heartbeat. REQUIRED for TRANSPORT=udp, which # cannot auto-detect "self" -- without it the heartbeat never registers and the # packets/hour stats stay at zero. OPTIONAL for PROTOCOL=reticulum, which derives # its own id from your primary identity; set it there only to override that. # INGESTOR_NODE_ID=!xxxxxxxx # Multicast groups/port for "Mesh via UDP" (defaults shown). The default listens # on both groups; set one address to listen on that group only. # MESH_UDP_GROUP=239.0.0.69,224.0.0.69 # MESH_UDP_PORT=4403 # Frequency and preset shown for Reticulum nodes. Both are read from the first # RNodeInterface in your RNS config; set these only to override that. # RETICULUM_FREQ="867.2MHz" # RETICULUM_PRESET="SF8/BW125/CR5"