Malla (Mesh, in Spanish) is an (AI-built) tool that logs Meshtastic packets from an MQTT broker into a SQLite database and exposes a web UI to get some interesting data insights from them.
Check out some instances with running data from community MQTT servers:
- meshtastic.es (Spain): https://malla.meshtastic.es
- malla.ctmesh.org (Connecticut): https://malla.ctmesh.org
• End-to-end capture – Logs every packet from your Meshtastic MQTT broker straight into an optimised SQLite database.
• Live dashboard – Real-time counters for total / active nodes, packet rate, signal quality bars and network-health indicators (auto-refresh).
• Packet browser – Lightning-fast table with powerful filtering (time range, node, port, RSSI/SNR, type), pagination and one-click CSV export.
• Node explorer – Detailed hardware, role, battery and signal info for every node – searchable picker plus online/offline badges.
• Traceroutes – Historical list view to inspect packet paths across the mesh network.
• Map view – Leaflet map with live node locations, RF-link overlays and role colour-coding.
• Network graph – Force-directed graph visualising multi-hop links and RF distances between nodes / gateways.
• Battery & Power monitoring – Track solar panels, batteries and mains-powered nodes with automated detection, health scores and low-battery alerts.
• Remote node administration – Configure, reboot, and manage nodes directly from the web UI via MQTT, TCP, or serial connections.
• Toolbox – Hop-analysis tables, gateway-compare matrix and "longest links" explorer for deep dives.
• Analytics charts – 7-day trends, RSSI distribution, top talkers, hop distribution and more (Plotly powered).
• Single-source config – One config.yaml (or MALLA_* env-vars) drives both the capture tool and the web UI.
• One-command launch – malla-capture and malla-web wrapper scripts get you up and running in seconds.
- Real-time dashboard with network statistics, packet rates, and health indicators
- Live monitor with streaming packet display
- Alerts & trends view for tracking network issues over time
- Node health page with per-node diagnostics and status
- Node explorer with searchable list, hardware info, battery status, and role badges
- Node detail pages with location history, packet statistics, and telemetry charts
- Archived nodes view for nodes no longer active on the network
- Direct receptions analysis showing which nodes hear each other
- Packet browser with advanced filtering (time, node, port, RSSI, SNR, hop count)
- Packet detail view with full payload inspection and decoding
- Exclude filters to hide specific nodes or packet types from views
- CSV export for offline analysis
- Traceroute history with path visualization and hop analysis
- Traceroute graph (force-directed) showing network topology and link quality
- Mesh topology view of the entire network structure
- Hop analysis tables for understanding message routing
- Gateway comparison matrix to evaluate gateway performance
- Interactive map with node locations, role-based markers, and RF link overlays
- Line of sight analysis between nodes with terrain consideration
- Longest links explorer showing record-breaking RF distances
- Location history tracking for mobile nodes
- Power source detection – Automatically classifies nodes as solar, battery, or mains-powered
- Battery health scoring – 0-100 health score based on voltage stability
- Solar charging analysis – Detects charging issues with cloud/weather indicators
- Low battery alerts – Automatic alerts when nodes drop below thresholds
- Voltage trend charts – Historical telemetry visualization
- Node configuration – Read and modify node settings (device, LoRa, position, etc.)
- Channel management – View and edit channel configurations
- Node reboot/shutdown – Remote power management
- Connection options – Connect via MQTT, TCP (direct), or serial
- Admin toggle – Disable admin features for public read-only deployments
- Multiple decryption keys – Support for comma-separated channel keys
- Data retention – Automatic cleanup of old records
- OpenTelemetry – Optional tracing for debugging and monitoring
- Docker support – Pre-built images with compose files
- Gunicorn support – Production-ready WSGI server
- Python 3.13+
- Access to a Meshtastic MQTT broker
- Modern web browser with JavaScript enabled
The easiest way to run Malla is using Docker. Pre-built images are available from GitHub Container Registry:
-
Copy the environment configuration:
cp env.example .env
-
Edit the configuration:
$EDITOR .env # Set your MQTT broker address and other settings
-
Start the services:
docker-compose up -d
-
View logs:
docker-compose logs -f
-
Access the web UI:
- Open http://localhost:5008 in your browser
For development with local code changes:
# Edit docker-compose.yml to uncomment the 'build: .' lines
# Then build and run:
docker-compose up --build -dManual Docker run (advanced):
# Run the capture service
docker run -d \
--name malla-capture \
-v malla_data:/app/data \
-e MALLA_MQTT_BROKER_ADDRESS=your.mqtt.broker.address \
ghcr.io/zenitram/malla:latest \
/app/.venv/bin/malla-capture
# Run the web UI
docker run -d \
--name malla-web \
-p 5008:5008 \
-v malla_data:/app/data \
ghcr.io/zenitram/malla:latestYou can also install and run Malla directly using uv:
-
Clone or download the project files to your preferred directory
git clone https://github.com/zenitraM/malla.git cd malla -
Install uv if you don't have it installed yet:
curl -LsSf https://astral.sh/uv/install.sh | sh -
Create a configuration file by copying the sample file:
cp config.sample.yaml config.yaml $EDITOR config.yaml # tweak values as desired
-
Start it with
uv runin the project directory, which should pull the required dependencies.# Start the web UI uv run malla-web # Start the MQTT capture tool uv run malla-capture
The project also comes with a Nix flake and a devshell - if you have Nix installed or run NixOS it will set up
uv for you together with the exact system dependencies that run on CI (Playwright, etc.):
nix develop --command uv run malla-web
nix develop --command uv run malla-captureThe system consists of two components that work together:
This tool connects to your Meshtastic MQTT broker and captures all mesh packets to a SQLite database. You will need to configure the MQTT broker address in the config.yaml file (or set the MALLA_MQTT_BROKER_ADDRESS environment variable) before starting it. See Configuration Options for the entire set of settings.
mqtt_broker_address: "your.mqtt.broker.address"You can use this tool with your own MQTT broker that you've got your own nodes connected to, or with a public broker if you've got permission to do so.
Start the capture tool:
uv run malla-captureThe web interface for browsing and analyzing the captured data.
Start the web UI:
uv run malla-webAccess the web interface:
- Local: http://localhost:5008
For a complete monitoring setup, run both tools simultaneously:
Terminal 1 - Data Capture:
export MALLA_MQTT_BROKER_ADDRESS="127.0.0.1" # Replace with your broker
./malla-captureTerminal 2 - Web UI:
./malla-webBoth tools use the same SQLite database concurrently using thread-safe connections.
When using Docker, configuration is handled through environment variables defined in your .env file:
For production deployments, Malla supports running with Gunicorn, a production-ready WSGI server that provides better performance and stability than Flask's development server.
Option 1: Using environment variable (recommended)
# In your .env file:
MALLA_WEB_COMMAND=/app/.venv/bin/malla-web-gunicornOption 2: Using the production override file
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -dOption 3: Direct script execution
# For local development with uv:
uv run malla-web-gunicorn
# Or using the executable script:
./malla-web-gunicornThe Gunicorn configuration automatically:
- Uses multiple worker processes based on CPU cores
- Enables proper logging and monitoring
- Configures appropriate timeouts and connection limits
- Provides better concurrent request handling
Benefits of Gunicorn over Flask dev server:
- Production-ready with proper process management
- Better performance under load
- Automatic worker process recycling
- Proper signal handling for graceful shutdowns
- Enhanced logging and monitoring capabilities
-
Copy the example:
cp env.example .env
-
Configure your settings:
# Required: Set your MQTT broker address MALLA_MQTT_BROKER_ADDRESS=your.mqtt.broker.address # Optional: Customize other settings MALLA_NAME=My Malla Instance MALLA_WEB_PORT=5008 MALLA_SECRET_KEY=your-production-secret-key
MALLA_MQTT_BROKER_ADDRESS: Your MQTT broker IP/hostname (required)MALLA_MQTT_PORT: MQTT broker port (default: 1883)MALLA_MQTT_USERNAME/MALLA_MQTT_PASSWORD: MQTT authentication (optional)MALLA_WEB_PORT: Port to expose the web UI (default: 5008)MALLA_NAME: Display name in the web interface
Data is automatically stored in a Docker volume (malla_data) and persists across container restarts. No manual volume setup is required when using docker-compose.
Malla will automatically look for a file named config.yaml in the current
working directory when it starts. You can point to an alternative file by
setting the MALLA_CONFIG_FILE environment variable.
If the file is not found, all built-in defaults are used (see
config.sample.yaml).
Copy the sample file and customise it:
cp config.sample.yaml config.yaml
$EDITOR config.yaml # tweak values as requiredThe file is git-ignored so you will never accidentally commit secrets such
as your secret_key.
The following keys are recognised:
| YAML key | Type | Default | Description | Env-var override |
|---|---|---|---|---|
name |
str | "Malla" |
Display name shown in the navigation bar. | MALLA_NAME |
home_markdown |
str | "" |
Markdown rendered on the dashboard homepage. | MALLA_HOME_MARKDOWN |
secret_key |
str | "dev-secret-key-change-in-production" |
Flask session secret key (change in prod!). (currently unused) | MALLA_SECRET_KEY |
database_file |
str | "meshtastic_history.db" |
SQLite database file location. | MALLA_DATABASE_FILE |
host |
str | "0.0.0.0" |
Interface to bind the web server to. | MALLA_HOST |
port |
int | 5008 |
TCP port for the web server. | MALLA_PORT |
debug |
bool | false |
Run Flask in debug mode (unsafe for prod!). | MALLA_DEBUG |
mqtt_broker_address |
str | "127.0.0.1" |
MQTT broker hostname or IP address. | MALLA_MQTT_BROKER_ADDRESS |
mqtt_port |
int | 1883 |
MQTT broker port. | MALLA_MQTT_PORT |
mqtt_username |
str | "" |
MQTT broker username (optional). | MALLA_MQTT_USERNAME |
mqtt_password |
str | "" |
MQTT broker password (optional). | MALLA_MQTT_PASSWORD |
mqtt_topic_prefix |
str | "msh" |
MQTT topic prefix for Meshtastic messages. | MALLA_MQTT_TOPIC_PREFIX |
mqtt_topic_suffix |
str | "/+/+/+/#" |
MQTT topic suffix pattern. | MALLA_MQTT_TOPIC_SUFFIX |
default_channel_key |
str | "1PG7OiApB1nwvP+rz05pAQ==" |
Default channel key(s) for decryption (base64). Supports comma-separated list of keys - each will be tried in order until successful. | MALLA_DEFAULT_CHANNEL_KEY |
data_retention_hours |
int | 0 |
Number of hours after which to delete old data (0 = never delete). Automatically cleans up packet_history and node_info records older than specified hours. | MALLA_DATA_RETENTION_HOURS |
battery_alerts_enabled |
bool | true |
Enable battery monitoring and low battery alerts. | MALLA_BATTERY_ALERTS_ENABLED |
battery_critical_voltage |
float | 3.2 |
Critical voltage threshold (V) - nodes may shut down. | MALLA_BATTERY_CRITICAL_VOLTAGE |
battery_warning_voltage |
float | 3.4 |
Warning voltage threshold (V) - battery is getting low. | MALLA_BATTERY_WARNING_VOLTAGE |
battery_check_interval_minutes |
int | 15 |
How often to check battery levels (minutes). | MALLA_BATTERY_CHECK_INTERVAL_MINUTES |
admin_enabled |
bool | true |
Enable/disable remote node administration features. Set to false for public read-only deployments. |
MALLA_ADMIN_ENABLED |
admin_connection_type |
str | "mqtt" |
Connection type for admin operations: mqtt, tcp, or serial. |
MALLA_ADMIN_CONNECTION_TYPE |
admin_tcp_host |
str | "192.168.1.1" |
TCP host for direct node connection (when using TCP). | MALLA_ADMIN_TCP_HOST |
admin_tcp_port |
int | 4403 |
TCP port for direct node connection. | MALLA_ADMIN_TCP_PORT |
otlp_endpoint |
str | null |
OpenTelemetry endpoint for tracing (e.g., http://localhost:4317). |
MALLA_OTLP_ENDPOINT |
Environment variables always override values coming from YAML file.
Malla includes automated battery and power monitoring capabilities to help track battery-powered and solar nodes:
Features:
- Automatic power source detection: Analyzes voltage patterns to classify nodes as solar, battery-only, or mains-powered
- Battery health scoring: Calculates health scores (0-100) based on voltage stability and discharge patterns
- Low battery alerts: Automatically logs warnings when nodes drop below voltage thresholds
- Telemetry tracking: Stores voltage, battery level, and other device metrics over time
- Solar node identification: Detects daily charging cycles to identify solar-powered nodes
Configuration:
Enable battery monitoring in your config.yaml:
battery_alerts_enabled: true
battery_critical_voltage: 3.2 # Volts - node may shut down soon
battery_warning_voltage: 3.4 # Volts - battery getting low
battery_check_interval_minutes: 15The power analysis background thread runs every hour to:
- Update power type classifications for all nodes
- Recalculate battery health scores
- Check for low battery conditions and log alerts
- Store historical telemetry data
Access the battery analytics dashboard at /battery-analytics to view:
- Power source distribution (solar/battery/mains/unknown)
- Battery health overview with color-coded status indicators
- Critical battery alerts for nodes requiring attention
- Historical voltage trends (when sufficient data is available)
Malla includes a powerful remote administration feature that allows you to configure, monitor, and manage Meshtastic nodes directly from the web interface.
Features:
- Read node configuration: View device settings, LoRa parameters, position config, and more
- Modify settings: Update node configurations remotely (requires admin key)
- Channel management: View and edit channel configurations
- Reboot/shutdown: Remote power management for nodes
- Multiple connection types: Connect via MQTT, TCP (direct), or serial
- Retry support: Resilient command delivery with automatic retries for unreliable nodes
- Command logging: Real-time status display showing command progress and results
Connection Types:
- MQTT (default): Uses your existing MQTT broker to relay admin commands
- TCP: Direct connection to a node's IP address (requires network access)
- Serial: Connect to a locally-attached node via USB/serial port
Disabling Admin for Public Deployments:
For public, read-only deployments, you can disable all admin features:
# In config.yaml
admin_enabled: falseOr via environment variable (recommended for Docker):
MALLA_ADMIN_ENABLED=falseWhen disabled:
- The "Admin" navigation link is hidden
- The node connection indicator is hidden
- Accessing
/adminshows a friendly "disabled" message - All admin API endpoints return HTTP 403 Forbidden
Access the admin dashboard at /admin to:
- Select a gateway node for relaying commands
- Choose connection type (MQTT/TCP/Serial)
- Browse administrable nodes with their admin key status
- Send configuration requests, reboot commands, and more
Malla includes an automatic data cleanup feature to help manage database size over time. When enabled, it will:
- Delete packet_history records older than the specified number of hours
- Delete node_info records for nodes that haven't been seen recently and have no packets in the packet_history table
- Repeat the cleanup process every hour in the background
To enable data cleanup, set the data_retention_hours configuration parameter to a positive value:
# Keep data for 7 days (168 hours)
data_retention_hours: 168Or via environment variable:
export MALLA_DATA_RETENTION_HOURS=168Set to 0 (default) to disable cleanup completely.
Feel free to submit issues, feature requests, or pull requests to improve Malla!
This project is licensed under the MIT license.









