A lightweight web dashboard to discover, run, update, and back up Python Discord bots from one place. Designed to run headless on a Linux box (e.g. a Proxmox LXC) and be operated from any browser on your LAN.
- Auto-discovers bots inside a chosen root folder (treats the root itself as a bot if it contains an entry file)
- Detects the entry file automatically:
main.py,bot.py,run.py, orapp.py - Skips the manager's own folder so it never tries to run itself
- Uses each bot's own virtual environment when available (
.venv,venv, orenv), checking both Windows (Scripts/python.exe) and POSIX (bin/python) layouts - One-click Start / Stop / Restart per bot
- Per-bot health metrics in the dashboard table: PID, CPU%, RSS memory, uptime (via
psutil) - Crash detection + bounded auto-restart: opt-in per bot via a dashboard checkbox. Logs the exit code, retries up to 3 times within a 10-minute window, then self-disables and asks for manual intervention to break restart loops. Intentional stops (SIGTERM) are distinguished from real crashes.
- Daily auto update checks against
origin/mainby default (configurable; floor is 60 s) - Manager self-update: checks its own git checkout for updates and offers a one-click pull + restart from the dashboard
- Auto update + restart: pulls
mainand restarts running bots when changes are detected - Scheduled per-bot data backups to local zip archives (gitignored)
- Per-bot backup health badge:
Healthy,Due Soon,Overdue, orFailed - Per-bot backup storage size shown in the table
- In-browser config-file editor with subfolder support: edit
.json,.env,.yaml,.yml,.toml,.ini,.cfg,.txt(and.env.*) up to 1 MB; create folders + files from the UI - Binary file uploads for
.pkl,.pickle,.csv,.db,.db3,.sqlite,.sqlite3,.xls,.xlsm,.xlsx(up to 25 MB) — useful for shipping trained models / persisted state directly into a bot's folder - Download + delete any listed file from the dashboard (auth-protected; path-traversal safe)
- Live, per-bot log stream in the browser over WebSocket
- Browse and download backup zips directly from the dashboard
- Settings persisted to
manager_config.json
- Python 3.10 or newer
- Git available in
PATH(for update checks) - Four pip packages:
fastapi,uvicorn[standard],jinja2,psutil(see requirements.txt)
Tkinter / python3-tk is no longer required.
git clone https://github.com/<your-user>/Bot_Manager.git
cd Bot_Manager
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtpython3 app.pyThen open http://<host>:28473/ in any browser on the LAN.
Port note. The dashboard listens on TCP 28473 by default. This port was chosen deliberately: it is IANA-unassigned, not used by any common service, and sits below Linux's default ephemeral range (
net.ipv4.ip_local_port_range= 32768-60999), so the kernel will never auto-allocate it to an outbound socket. Override withBOTMGR_PORTif you need to.
On Linux / inside the LXC:
./run_manager.shOn Windows (dev): double-click run_manager.bat.
| Variable | Default | Purpose |
|---|---|---|
BOTMGR_HOST |
0.0.0.0 |
Bind address. Use 127.0.0.1 to restrict to localhost. |
BOTMGR_PORT |
28473 |
TCP port. Default is intentionally obscure (see Port note above). |
BOTMGR_TOKEN |
(unset) | Optional shared-secret. When set, the dashboard + API + WebSocket require it. When unset, the server logs a warning and runs open (LAN-only use). |
Example (Linux):
export BOTMGR_TOKEN="$(openssl rand -hex 32)"
export BOTMGR_PORT=28473
./run_manager.sh- Settings — enter the Bots Root Folder (an absolute path on the server).
Optionally set a fallback Python Executable, the Update Interval (default
86400= once per day; minimum 60 s), and the Backup Interval. Click Save Settings. - Click Scan Bots to (re-)discover bots under that folder.
- Click a row to select a bot. The action bar reflects what's possible for that bot, including an Auto-restart on crash checkbox.
- Start / Stop / Restart / Update Now act on the selection.
- Check Updates runs a fetch + compare against
origin/mainfor every git bot. - Backup Now zips the selected bot's data files; Backup All queues them all.
- Show Backup Status opens a per-bot summary; Browse Backups lists archives for the selected bot and links each one for download.
- Config opens the bot's file panel:
- Click any text file to open the in-browser editor (max 1 MB, UTF-8).
- Create / Open File with a subpath like
local_data/notes.txtcreates intermediate folders as needed. - Create Folder makes a folder (
mkdir -p). - Upload Binary uploads a
.pkl/.sqlite/.xlsx/ etc. into a chosen destination path (max 25 MB). - Each row has ↓ (download) and × (delete) buttons. All operations are scoped to the bot's folder and path-traversal-safe.
- The Logs panel streams every bot's stdout + system messages live over WebSocket. The last ~2000 log lines are kept in memory and replayed when a new browser opens. Crash exit codes and auto-restart attempts are recorded here.
When the checkbox is ticked for a bot, the manager:
- Logs
Process crashed (exit code N)whenever the subprocess exits non-zero (and the manager didn't ask it to stop). - Schedules an auto-restart after a short delay.
- Caps at 3 crashes within a 10-minute sliding window. On the 4th crash inside that window it logs
Restart loop detectedand self-disables auto-restart for that bot until you start it manually — so a crash-looping bot never hammers your log forever.
The setting is persisted to bot_data/bot_settings.json and survives manager restarts.
A folder qualifies as a bot if its root contains one of: main.py, bot.py, run.py, app.py.
The manager's own folder is skipped during discovery, so adding app.py to that list does not cause the manager to try to run itself.
When starting a bot, the manager picks the interpreter in this order:
- The bot's own venv (
<bot>/.venv,<bot>/venv, or<bot>/env). BothScripts/python.exeandbin/pythonare checked. - The path entered in Python Executable (optional).
- The interpreter running the manager.
This means each bot can have its own isolated dependencies.
- Updates are checked against
origin/mainusinggit fetch+git rev-list HEAD..origin/main. - When Auto update + restart is enabled, detected updates are pulled with
git pull --ff-onlyand any running bot is restarted automatically. - Click Update Now for a one-off update of the selected bot.
The Manager card at the top of the dashboard shows the manager's own git status — branch, current commit, and whether updates exist on origin.
- Check for Updates runs
git fetchagainst the manager's own remote and reports how many commits behindorigin/<branch>you are. The periodic update check (sameupdate_interval_secused for bots) also refreshes this in the background. - Update Manager runs
git pull --ff-onlyon the manager directory. It refuses to run if the working tree is dirty so local edits are never silently overwritten. - Restart Manager stops all running bots, saves config, then re-execs the current Python process via
os.execv. On a systemd-managed deployment the new process replaces the old one in-place; the dashboard automatically reconnects via/healthzpolling once the server is back.
Auto-applying a self-update is intentionally not offered — a bad pull would lock you out of the dashboard. The dashboard checks automatically; you decide when to pull and restart.
User settings are stored in manager_config.json (gitignored). A template is provided in manager_config.example.json:
{
"bots_root": "",
"python_executable": "",
"update_interval_sec": 86400,
"backup_interval_days": 1,
"auto_update_restart": true
}The app updates this file automatically when you save settings in the dashboard. Per-bot settings (currently just restart_on_crash) live alongside the backup status at bot_data/bot_settings.json.
Two layers, intentionally:
manager_core.py— the engine. Discovery, process control, git, backups, scheduling, log buffer. Zerotkinterimports, zero web imports. Could be reused by a CLI in the future.web/server.py— a thin FastAPI adapter that holds a singleBotManagerinstance and maps HTTP/WebSocket calls to engine methods. Blocking operations (git, backups) are dispatched to a threadpool so the event loop stays responsive.
All shared state is guarded by BotManager.bots_lock. Logs are published via a subscriber set, so each open WebSocket gets every new line without polling.
The Tk app was implicitly local-only; a network-exposed dashboard is not. For v1:
- Bind deliberately. Default is
0.0.0.0:28473. Restrict at the firewall or setBOTMGR_HOST=127.0.0.1if you'll reach it through SSH tunneling. - Set
BOTMGR_TOKENfor any deployment beyond a fully trusted LAN. The token is sent viaAuthorization: Bearer …on API calls and?token=…on the WebSocket / backup download links. - Path-traversal safe. Backup downloads, config-file reads/writes, binary uploads, and file deletes all resolve paths against the bot's folder and reject anything that would escape it. Filenames are also restricted by extension allow-lists.
- Binary uploads accept a fixed allow-list (
.pkl,.pickle,.csv,.db,.db3,.sqlite,.sqlite3,.xls,.xlsm,.xlsx) capped at 25 MB. Pickle deserialization is unsafe — only upload pickles you trust. The manager never auto-loads them; it just stores the bytes for the bot to use. - Bot stdout is visible in the log stream. Don't print secrets from your bots, or keep the dashboard behind the token + LAN.
Out of scope (for now): TLS, multi-user accounts, RBAC. Put the dashboard behind a reverse proxy (Caddy / nginx) if you need HTTPS.
The repo ships a ready-to-use unit file: botmgr.service and an environment-file template: botmgr.env.example.
-
Create a service user and lay out the files (inside the container, as root):
adduser --system --group --home /opt/botmgr botmgr git clone https://github.com/<your-user>/Bot_Manager.git /opt/botmgr mkdir -p /opt/bots && chown botmgr:botmgr /opt/bots chown -R botmgr:botmgr /opt/botmgr chmod +x /opt/botmgr/run_manager.sh
-
Install Python deps in a venv owned by the service user:
sudo -u botmgr python3 -m venv /opt/botmgr/.venv sudo -u botmgr /opt/botmgr/.venv/bin/pip install -r /opt/botmgr/requirements.txt
(If you go this route, also edit
run_manager.shto call/opt/botmgr/.venv/bin/pythoninstead of systempython3.) -
Set
bots_rootin/opt/botmgr/manager_config.jsonto/opt/bots(or wherever you want bots cloned). -
Install the environment file and generate a token:
cp /opt/botmgr/botmgr.env.example /etc/botmgr.env sed -i "s/replace-me-.*/$(openssl rand -hex 32)/" /etc/botmgr.env chmod 600 /etc/botmgr.env chown root:botmgr /etc/botmgr.env -
Install and enable the unit:
cp /opt/botmgr/botmgr.service /etc/systemd/system/botmgr.service systemctl daemon-reload systemctl enable --now botmgr.service systemctl status botmgr.service journalctl -u botmgr.service -f -
Firewall (example for
ufw— adapt tonftables/iptablesas you prefer). Allow only your LAN subnet:ufw default deny incoming ufw default allow outgoing ufw allow from 10.0.0.0/24 to any port 28473 proto tcp ufw enableReplace
10.0.0.0/24with your actual LAN subnet (or a/32for a single admin machine). -
Open the dashboard from a LAN machine:
http://<container-ip>:28473/and paste the token from/etc/botmgr.envwhen prompted (or append?token=...to the URL).
Bot_Manager/
├── app.py # Thin uvicorn launcher
├── manager_core.py # Engine (no UI imports)
├── web/
│ ├── __init__.py
│ ├── server.py # FastAPI app + WebSocket
│ ├── templates/
│ │ └── index.html
│ └── static/
│ ├── app.js
│ └── style.css
├── run_manager.sh # Linux launcher
├── run_manager.bat # Windows launcher
├── botmgr.service # systemd unit for Linux/LXC deployment
├── botmgr.env.example # template for /etc/botmgr.env
├── manager_config.example.json
├── requirements.txt
├── bot_data/ # Local backups + bot_settings.json + backup_status.json (gitignored)
├── .gitignore
└── README.md