avni — a single-binary control center for your VPS
avni is a fast, dependency-light terminal dashboard (TUI) + CLI for operating a
self-hosted Linux server: see your Docker apps, domains, bandwidth, SSH sessions and
brute-force attackers at a glance — and act on them (start/stop services, ban/blacklist
IPs, manage users) without leaving the terminal.
It shells out to the tools already on the box (docker, fail2ban-client, ipset,
vnstat, who/last, getent, …) — no daemon, no database, no agent. One Rust
binary, ~2% CPU, microsecond redraws.
Built for a small multi-app VPS behind Traefik. It auto-discovers the apps Traefik routes, so the dashboard never goes stale as you add or remove services.
┌ AVNI CLOUD · control center ─────────────────────────────────────────────────────────┐
│ 1.Overview · 2.Services · 3.Domains · 4.Traffic · 5.Security · 6.SSH · 7.Users · 8.Set…│
└────────────────────────────────────────────────────────────────────────────────────────┘
┌ Applications (9/9 enabled) — s enable · x disable · R restart · l logs · ↵ details ─────┐
│ APP STATE SUBDOMAIN PORT HOST PORTS │
│ » ● gitea enabled git.example.com 3000 127.0.0.1:3000->3000 │
│ ● n8n enabled n8n.example.com 5678 127.0.0.1:5678->5678 │
│ ● uptime-kuma enabled uptime.example.com 3001 127.0.0.1:3001->3001 │
│ ○ analytics disabled stats.example.com 3000 - │
└──────────────────────────────────────────────────────────────────────────────────────────┘
s enable x disable R restart l logs ↵ details 1-9/⇄ tabs ↑↓ move r refresh
Table of contents
- Features
- Requirements
- Install
- Using the TUI
- CLI
- Access control — who can run
avni - How it works
- Data & file locations
- Security notes
- Build from source
- Python fallback
- License
Features
A tabbed dashboard (switch with 1–9, Tab, or ←/→):
| Tab | What it shows / does |
|---|---|
| Overview | host, IP, uptime, load/memory/disk gauges, service & attacker summary, dual clock (server + your local time) |
| Services | every Docker container with status + the domain Traefik routes to it; s/x/R start/stop/restart, l logs |
| Domains | every Host() route → backend; Enter checks live HTTP status + TLS cert expiry |
| Traffic | vnstat bandwidth bar chart, 24h / 7d / 30d windows (pins the real uplink interface) |
| Security | a persistent IP registry of everyone who has touched the box (country, attempts, logins, status). Temp-ban (fail2ban), permanent firewall blacklist (ipset+iptables), and a whitelist that can never be banned |
| SSH | live sessions (kick / kick+blacklist) + the effective sshd policy |
| Users | list/create/delete Linux users, grant/revoke sudo, lock/unlock, set login password, and a full per-user activity view (system metadata only) |
| Settings | delegate the tool to other admins (see access control) |
| Apps | the user-facing apps Traefik routes — enable/disable, ports, subdomain, live HTTPS + cert details |
Highlights:
- Auto-discovery — apps and domains come straight from container labels; nothing to configure.
- Persistent attacker registry with incremental log parsing (reads only newly-appended bytes of
auth.log, survives rotation). - Optimistic UI — actions reflect instantly, then reconcile with the real system state.
- Dual time — every timestamp shows a second timezone beside it (configured for IST in the source; see Build to change).
- Scriptable — every view is also a one-shot CLI subcommand.
Requirements
- Linux, run as root (or as a delegated
avni-group member — it self-elevates via sudo). - A recent Rust toolchain (rustup stable; ratatui 0.26 needs a newer rustc than some distros ship).
- Expected on the box (features degrade gracefully if missing):
docker,fail2ban-client,ipset+iptables,vnstat,geoiplookup(geoip-bin),whois, plus coreutils (who,last,lastb,getent,passwd,chpasswd).
Install
git clone https://git.example.com/you/vps-tool-avni.git
cd vps-tool-avni
sudo bash install.sh
install.sh builds the release binary and installs it to /usr/local/bin/avni, installs the
firewall helper to /usr/local/bin/avni-firewall.sh, creates the avni group, and writes a
sudoers drop-in so delegated admins can launch it. Re-run it after pulling changes.
Then just:
avni # full-screen dashboard
avni status # one-shot CLI
Using the TUI
Global keys: 1–9 / Tab / ←→ switch tabs · ↑↓ (or j/k) move · r refresh · q quit.
Per-tab actions are always shown in the footer. The most useful:
- Services / Apps —
sstart/enable ·xstop/disable ·Rrestart ·llogs ·Enterdetails - Security —
[]switch sub-tab ·aadd blacklist ·Aadd whitelist ·bban ·Bblacklist selected ·Wwhitelist selected ·uundo ·wwhois ·EnterIP intel - SSH —
kkick a session ·Kkick + blacklist its IP - Users —
Nnew user ·Pset login/SSH password (masked) ·G/ggrant/revoke sudo ·L/Ulock/unlock ·Ddelete (keeps/home) ·Enteractivity - Settings —
ggrant the tool ·xrevoke
Full reference: docs/guide.md.
CLI (scriptable)
Every subcommand prints the same data the TUI shows:
avni status # host + counts
avni services # all containers
avni apps # user-facing apps: state · subdomain · port
avni ips [attackers|banned|new|all] # the IP registry
avni attackers [N] # top N attackers with country
avni ban <ip> | unban <ip>
avni blacklist [add <ip|cidr|domain>]
avni whitelist [add|del <ip>]
avni geo <ip> | ssh | users
avni adduser <user> [password] # create account (-m home, bash)
avni passwd <user> <password> # set a user's Linux login / SSH password
avni sudo <user> | unsudo <user> # grant / revoke sudo
avni deluser <user> # delete account, KEEP /home
avni activity <user> # logins, failed logins, sudo cmds, processes
avni access | grant <user> | revoke <user> # tool delegation
Access control — who can run avni
The tool is root-only by default. To let a teammate use it:
- They must already be an admin (sudo) —
avni sudo <user>(Users tabG). - Grant the tool —
avni grant <user>(Settings tabg). This adds them to theavnigroup; the sudoers drop-in lets the binary auto-elevate via sudo when they run it.
Anyone not granted gets a polite "not authorised" message. Root can never be revoked.
The single binary at /usr/local/bin/avni is a real root-owned file (mode 0755), so a
delegated user can exec it but cannot modify it.
How it works
- Background gather — a worker thread fans out the independent fetches (docker, auth-log registry, sessions, users, vnstat) across threads, so a full refresh ≈ the slowest single part (~2 s) instead of the sum. The UI never blocks; a loading splash shows on first start.
- Incremental log parsing — stores a byte offset per log inode and reads only the new tail
of
auth.logon each refresh (not the whole multi-MB file). - Auto-discovery — services, domains and apps are parsed from
docker ps+ TraefikHost()labels in a single call. Nothing is hard-coded; add a container and it appears. - Traffic pins the default-route interface (a busy box can have dozens of docker
veth/bridge interfaces;
vnstat'sinterfaces[0]is often a zero-traffic bridge).
Data & file locations
| Path | Purpose |
|---|---|
/usr/local/bin/avni |
the binary |
/usr/local/bin/avni-firewall.sh |
blacklist/whitelist engine (ipset + iptables) |
/var/lib/avni/ips.json |
persistent IP registry (+ per-log byte offsets) |
/etc/avni/whitelist.txt, /etc/avni/blacklist.save |
firewall state |
/etc/fail2ban/jail.d/avni-whitelist.conf |
auto-generated fail2ban ignoreip |
/etc/sudoers.d/avni |
NOPASSWD drop-in for the avni group |
Security notes
- No secrets are stored in this repo or the binary.
avnireads live system state at runtime; it never embeds passwords, IPs or hostnames. (The examples in the code are RFC 5737 documentation addresses.) - Whitelist your own IPs first. A firewall guard and fail2ban
ignoreipthen make it impossible to accidentally lock yourself out under a brute-force burst; an anti-lockout check also refuses to blacklist your current SSH client IP. - Docker-published ports bypass host UFW — the firewall engine adds rules to the
DOCKER-USERchain too, so a blacklist actually blocks container-exposed ports. - The per-user activity view uses only system metadata (logins, sudo audit,
ps) and never reads anyone's home directory or shell history — safe on a multi-tenant box.
Build from source
cargo build --release # at the repo root → target/release/avni
Toolchain: rustup stable. Dependencies are just ratatui, crossterm, serde,
serde_json (see Cargo.toml).
Changing the second timezone: the dual-clock offset is computed in ist_delta_min() in
src/main.rs (currently targets IST, +05:30). Adjust the 330 (minutes) and the
TZ=Asia/Kolkata strings to your zone.
Python fallback
avni.py is an earlier pure-Python (curses) implementation kept as a zero-build fallback.
install.sh installs it as /usr/local/bin/avni-py. The Rust binary is the maintained one.
License
MIT © 2026 Jagadeesh (AVNI Cloud).