Files
meshcore-gui/docs/MULTI_INSTANCE.md
T
pe1hvh af74caf766 fix(route): default reply channel to message channel, add date to timestamps
WHAT
- route_page: _default_reply_channel() derives the reply select default
  from msg.channel instead of the first entry in the channel list
- models: new Message.date field, now_date() and display_timestamp();
  format_line() and incoming()/outgoing() use them
- message_archive: persist "date" alongside "time" and "timestamp_utc"
- room_server_panel + route_page info header use display_timestamp()
- VERSION 1.22.2 -> 1.22.3

WHY
The route page exists to show one specific message, so that message —
not the ordering of the channel list — is the correct source for the
reply default. The date could not be fixed at the render site because
Message had no date at all: now_timestamp() yields HH:MM:SS and the
date lived only as timestamp_utc inside the archive layer, unread by
from_dict(). A first-class field plus a single formatting helper fixes
the cause and keeps the format in one place.

NOTES
No breaking changes. The archive schema gains one additive field;
pre-1.22.3 entries have their date derived from timestamp_utc
(converted to local time) and fall back to time-only when absent.
Message.incoming() gained an optional keyword-only date parameter.
Message display lines are ~11 characters wider.
2026-08-16 08:32:24 +02:00

5.2 KiB

Running Multiple MeshCore GUI Instances

⚠️ WARNING: This guide has not been tested yet. The configuration below is based on the application's architecture and should work, but has not been validated in practice. Please report any issues.

Overview

MeshCore GUI supports running multiple instances simultaneously — for example, to monitor two different MeshCore devices from the same machine. Each instance gets its own web port, serial connection, and all persistent data (cache, archive, logs, pins, room passwords) is automatically separated by device identifier (serial port).

Prerequisites

  • MeshCore GUI v1.9.2 or later (with --port and serial CLI parameters)

Quick Test (foreground)

Before creating services, verify that both instances start correctly:

Terminal 1:

cd ~/meshcore-gui
source venv/bin/activate
python meshcore_gui.py /dev/ttyUSB0 --debug-on --port=8081 --baud=115200

Terminal 2:

cd ~/meshcore-gui
source venv/bin/activate
python meshcore_gui.py /dev/ttyUSB1 --debug-on --port=8082 --baud=115200

Verify both are accessible at http://localhost:8081 and http://localhost:8082.

systemd Service Setup

Service 1

sudo nano /etc/systemd/system/meshcore-gui-device1.service
[Unit]
Description=MeshCore GUI — Device 1 (/dev/ttyUSB0)

[Service]
Type=simple
User=your-username
WorkingDirectory=/home/your-username/meshcore-gui
ExecStart=/home/your-username/meshcore-gui/venv/bin/python meshcore_gui.py /dev/ttyUSB0 --debug-on --port=8081 --baud=115200
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target

Service 2

sudo nano /etc/systemd/system/meshcore-gui-device2.service
[Unit]
Description=MeshCore GUI — Device 2 (/dev/ttyUSB1)

[Service]
Type=simple
User=your-username
WorkingDirectory=/home/your-username/meshcore-gui
ExecStart=/home/your-username/meshcore-gui/venv/bin/python meshcore_gui.py /dev/ttyUSB1 --debug-on --port=8082 --baud=115200
Restart=on-failure
RestartSec=30
[Install]
WantedBy=multi-user.target

Replace your-username and serial ports with your actual values.

Enable and Start

sudo systemctl daemon-reload
sudo systemctl enable meshcore-gui-device1 meshcore-gui-device2
sudo systemctl start meshcore-gui-device1
sudo systemctl start meshcore-gui-device2

Data Separation

All persistent data is automatically separated by device identifier. No additional configuration is needed.

Data Path example (device /dev/ttyUSB0)
Web interface http://host:8081 (via --port)
Cache ~/.meshcore-gui/cache/_dev_ttyUSB0.json
Message archive ~/.meshcore-gui/archive/_dev_ttyUSB0_messages.json
RX log archive ~/.meshcore-gui/archive/_dev_ttyUSB0_rxlog.json
Debug log ~/.meshcore-gui/logs/_dev_ttyUSB0_meshcore_gui.log
Pin state ~/.meshcore-gui/pins/_dev_ttyUSB0_pins.json
Room passwords ~/.meshcore-gui/room_passwords/_dev_ttyUSB0_rooms.json

Useful Commands

Command Description
sudo systemctl status meshcore-gui-device1 Check status of device 1
sudo systemctl status meshcore-gui-device2 Check status of device 2
sudo journalctl -u meshcore-gui-device1 -f Follow live log of device 1
sudo journalctl -u meshcore-gui-device2 -f Follow live log of device 2
sudo systemctl restart meshcore-gui-device1 Restart device 1 (without affecting device 2)
sudo systemctl stop meshcore-gui-device1 Stop device 1 only
sudo systemctl disable meshcore-gui-device1 Prevent device 1 from starting on boot

Removing a Service

sudo systemctl stop meshcore-gui-device2
sudo systemctl disable meshcore-gui-device2
sudo rm /etc/systemd/system/meshcore-gui-device2.service
sudo systemctl daemon-reload

Optionally remove the device's persistent data:

rm ~/.meshcore-gui/cache/_dev_ttyUSB1.json
rm ~/.meshcore-gui/archive/_dev_ttyUSB1_*.json
rm ~/.meshcore-gui/logs/_dev_ttyUSB1_meshcore_gui.log
rm ~/.meshcore-gui/pins/_dev_ttyUSB1_pins.json
rm ~/.meshcore-gui/room_passwords/_dev_ttyUSB1_rooms.json

Troubleshooting

Port already in use

If meshcore-gui fails to start with:

ERROR: [Errno 98] error while attempting to bind on address ('0.0.0.0', 8081): address already in use

a previous instance is still running, or the port hasn't been released yet.

Quick fix — kill previous instance and free the port:

pkill -9 -f meshcore_gui
sleep 3
lsof -i :8081

If lsof returns nothing, the port is free. Start the service again:

sudo systemctl restart meshcore-gui-device1

If the port is still in use after killing, sometimes TCP sockets linger in TIME_WAIT state. Wait 30 seconds:

sleep 30
lsof -i :8081

Running in background with nohup (for foreground tests, not the systemd flow above): always redirect output to a log file so you can diagnose:

# Good - keeps logs for debugging
nohup python meshcore_gui.py /dev/ttyUSB0 --debug-on --port=8081 > ~/meshcore.log 2>&1 &

# Bad - hides all errors
nohup python meshcore_gui.py /dev/ttyUSB0 --debug-on --port=8081 > /dev/null 2>&1 &