Setup Packages: the software on the box
The hub is one card per package; a card is built or not, and an unbuilt card is never shown. Each install is one root job the operator watches, asked first with what the package does and does not do. A tool appears in the rail as soon as the box runs its daemon — never on hope.
Web (nginx or Apache) → the Websites tool. Install nginx or Apache; from then on the daemon lives on the Websites tool's Overview (version, at boot, reload/restart/stop, the firewall's word on 80 and 443). Sites as THINGS — a domain, a folder, whether it answers (asked through the box itself), the certificate it presents and for how long, its own logs — not as the web server's files. New site is one click and one job: static files, PHP (a pool), a container, a reverse proxy, or an app on a port; folder, owner, certificate, redirect. A site opens into Overview / Settings / Names & redirects / Protection / Traffic / Backups / Logs (live). Editing is inline: a Lens site has its Settings form and its File, any other site only its File (Lens never rewrites what it did not make); a save asks whether to reload and the bar says so until nginx has read the file. HTTPS from three places: a certificate already on the box, Let's Encrypt through the acme folder every Lens site serves on 80, or the operator's own. Site protection writes one transaction (protect file, zones file, the include line once, the server's own test, everything put back on failure).
FTP (vsftpd or ProFTPD) → the FTP client. The screen says the honest thing first: SFTP already works over SSH with no extra service. For the cases where the other side insists — an old camera, a client's upload script, a device with no SSH — vsftpd is the small, careful one Lens configures from the screen and ProFTPD the bigger one, installed and controlled from the same place: who may log in and where each lands (own folder, shared default, home — the row says it), TLS, the passive range, the daemon's file edited in place and judged by proftpd before it reaches the service. The FTP client (kept on disk, not on the rail today) logs in from this computer through the firewall and the passive range to verify the account works from outside, with the two-pane file manager once in.
Mail (Postfix, DKIM, Dovecot, rspamd) → the Mail tool. Mail is thirty percent software and seventy percent reputation, so the package reads the six facts a receiving server judges the box by — outbound 25, PTR, hostname, SPF, DKIM, DMARC — says each in words and hands over the exact DNS records to copy, re-checked from the box until green. Mode 1, SEND ONLY: Postfix on loopback, DKIM signing, direct delivery or a relay (SES, Mailgun, Postmark, your own), which is what an app server, a WordPress or an n8n needs; who may send through the box (its containers, other servers by address on 25, logins on 587 behind TLS). Mode 2, RECEIVING: Postfix takes mail for the domains on 25 and hands it to Dovecot, which keeps it and serves it on 993; mailboxes and aliases (the catch-all as @domain), the MX checked from the box, clients that set themselves up (autoconfig). Spam filter (rspamd), logins & defence (the jails), activity (today, and what happened to a message), queue & test, find what happened to a message, a sending rate limit, the client's own IP hidden from the headers it sends, the raw log (postfix, dovecot, rspamd, opendkim) with follow-live. Per domain: DKIM key rotation in three steps weeks apart, and DMARC REPORTS — a collector mailbox per domain nobody can log into, the record pointed at it, the daily XML reports from Google/Microsoft/Yahoo/Apple parsed and folded by sending address, each named in words, so a domain owner finally sees who else sends as them. A mailbox's mail goes out to a file or down to this computer and back, and can be pulled in from elsewhere (migration). Mailbox backups ride the Backups tool.
Containers (Docker) → the Containers tool. The runtime: Docker from docker.com (the distro's docker.io lags a year and ships no compose plugin) with compose and buildx, enabled at boot, a daemon.json seeded with log rotation and live-restore before the first start so no box set up here fills a disk with logs. Then: the service and the docker group (offered, not assumed — it is root in all but name), the daemon's keys that decide whether the box is still standing in six months (data root moved as its own job, an outbound proxy for the daemon, a prune schedule), the FIREWALL (docker publishes past ufw unless told not to; the ufw-docker block in after.rules and ufw route rules added and removed here, "to whom" first), and the registries the box is logged in to (the login happens in the terminal, the token never passes through Lens). The tool: Containers (running first, then the trouble; a row opens to its logs, details, stats — the monitor's charts for one process tree — and top), Stacks (compose projects: pull-and-up, the update check, effective config, the editor), Images (pull, build), Volumes (data out or in), Networks (new, attach), Disk. The Run form carries the long tail of docker run — basics, network, storage, resources, security, logging & health — one control a row; any container reads back into it ("Edit & recreate") or into the equivalent command line, and Recreate steps the old one aside under a temporary name until the new one is up. A shell into a container is a real terminal tab with docker exec typed in. Secrets are masked on screen. Stack templates: ready compose files for a small developer — pick the app, answer the three things that matter, running — every choice a ${VAR} written into a mode-600 .env so no password sits in the YAML; databases bound to 127.0.0.1, admin passwords generated, logs rotated, world-facing ports said in their note.

Virtualization (KVM/libvirt, LXD/Incus). Readiness first and honestly: most VPSs cannot run KVM at all, and saying so before 400 MB of packages is worth more than anything below. Then guests as whole machines through virsh on the system connection: create from a cloud image (the distribution's own disk configured on first boot by cloud-init — login, key, hostname; the image downloaded once and kept beside the pool) or from an ISO with a screen (VNC bound to the server's loopback and reached through a tunnel, nothing exposed), start/stop/snapshots, what it is given (CPU, memory, another disk, another card — written to the definition AND to the running machine), its disks and interfaces (a disk grown, a NIC moved to another network), clone, resize, the console over the serial port in a Lens terminal, a backup, and PUBLISHING a port (the guest's address pinned in libvirt's DHCP, a forward that survives reboots through libvirt's hook Lens writes, the host's firewall let in — three things at once, which is why it is a feature). Networks: a libvirt NAT network touches nothing on the host; a BRIDGE rewrites the server's networking and goes through the same armed revert an address change does. Rates on a guest's page are honest differences of libvirt's running counters.
MariaDB, PostgreSQL, MongoDB (the engines). Installed from here like the rest, secured on the way in (root through the socket and the password you give, no anonymous, no test database, bound to 127.0.0.1; PostgreSQL's socket keeps peer auth; MongoDB 8.0 from mongodb.org with the admin made and authorization ON from the first minute). The install offer reads the versions the box can install — its own repository's and the vendor's — live, and lets the operator choose; a box joining a pair or a set is installed at the SAME version the pair runs. Once in, the engine's screen is on the rail under its own name (§9). SQL Server is read on a box that already has it (read-only tabs) but is not installed or built out.
Cache (Redis or Valkey) → the Cache tool. Redis is fast and famous for being left open to the internet, so the install lands it bound to this box, protected mode on, the password you give set on the default user from the first minute, maxmemory a quarter of RAM with allkeys-lru, users kept past restarts (an aclfile under the data dir). Lens's own lens.conf is included LAST from the main file so it wins; the login in /root/.lens-cache. Tabs: Overview with findings, Keys (SCAN pages, open, TTL, delete, flush), Performance (slowlog, clients, commandstats, read every 10 s while shown), Access (ACL users with roles full/app/reader), Replication (the Sentinel set), Connect apps (URL and a snippet per driver, the password never shown), Firewall (the engines' firewall view on the cache's port), Settings (13 keys, live + file, restart-due), TLS beside the plain port on 6380 with the box's own Lens CA, replica-of on the Overview, Save now.
Cache → Sentinel set. One primary, replicas that copy it, a sentinel beside each; when a majority agree the primary is gone one promotes a replica and apps that speak Sentinel follow by themselves. Filed on this computer like the databases' sets (at least three boxes), members added by dropping servers from the tree and read in parallel (one connect for the whole drop, one read across all), the boxes without a cache installed first, built by one job with the firewall step before any replica is told to follow (each wire proved from the replica first); Failover now (the sentinels choose, the tab shows RESYNCING while the demoted primary catches up), Apply the filing (shown only when a box and the filing disagree), Remove, Disband, sentinel-aware connect strings. Redis Cluster (sharding) is deliberately not built.
VPN (WireGuard mesh). A private network between the boxes: every member gets a private address on one subnet (10.99.0.0/24 by default) and every other member as a peer, so replication, backups and admin traffic ride private addresses and nothing crosses the internet in the clear. Filed as a network on this computer; one job on every box at once (tools, keys — the private half never leaves its box, loaded by PostUp and never written in the conf — the interface lens0 (never wg0, so a WireGuard from before Lens is shown and untouched), every other member a peer on 51820/udp, enabled at boot, the port opened between members, every tunnel pinged from both ends); a members table with the tunnels as this box sees them, a ping matrix, live sync when only peers change (the old members never restart), Remove, Take the network down (keys kept), and the rules it opened closed on the way out. The OPERATOR'S OWN MACHINE as a peer: a key pair made in Lens's Go (X25519), the public half given to every box as a peer that dials in, the private half written once into a .conf saved where the operator says and shown once — Lens keeps no copy. The private 10.99 addresses are offered by default on the Cache set's, the SQL pair's and the Mongo replication page's drop rows. The mesh does not touch a box's real network: one extra interface and address, only 10.99 traffic routed through it, one UDP port opened. Proved on a sixteen-box mesh.

Load balancer (HAProxy). One box runs HAProxy and its FRONTS are filed on this console: a front drawn from a filed pair or set is filled at Apply from that filing — a writes port that always reaches the primary, a reads port over the replicas — and is REPOINTED by the same job that moves a primary (Promote, Failover), so apps that dial the balancer never learn there was a switchover; a pool front is a list of web boxes behind one address. Each backend checked every three seconds; HTTPS on the front (443 when on, the plain-http redirect riding along; the boxes speak plain http behind it); a dropped box read for what listens on its port; Apply writes the backends' firewalls from the same job through the shells it can open.