This is the nginx → Caddy migration release. The reverse proxy and TLS terminator under the WebUI moved from nginx to Caddy. ACME issuance is now driven directly through Caddy (lego dropped), per-app ingress applies at runtime via Caddy's admin API, and the v0.0.7 NetworkManager compatibility scaffolding has been removed — boxes upgrading from 0.0.7 should be reconciled before jumping. Anything still on 0.0.6 or earlier should pass through 0.0.7 first.
Headline changes
-
Caddy replaces nginx as the reverse proxy and TLS terminator. App ingress routes apply through Caddy's admin API at install / remove time — config changes take effect in-process with no file rewrite and no reload. TLS automation is one atomic admin-API PATCH per change, so per-host issuance state shows up live on the TLS page.
-
Per-app subdomain ingress (V1 of #99). Apps can now be served at
app.example.cominstead of (or alongside)/apps/<name>/. Subdomain mode is selectable at install time and editable later, conflicts are detected before submit, and ingress-incompatible apps (whose absolute-path assets break path-prefix mode) auto-detect themselves and surface a clear reason in the install UI. -
Self-signed certs now cover both
nasty.localand the box's LAN / Tailscale IPs. Direct-IP HTTPS (https://10.x.x.x) validates the cert against the IP directly — only the "untrusted CA" warning remains, which clears once you import Caddy's root via the Download CA Root button on the TLS page. Unknown SNI (tailnet*.ts.netnames, anything not on the cert) falls back cleanly to the internal cert. -
Files page learned copy, move, and bulk actions (#88). Per-row Copy / Move icons + multi-select bulk action bar (Copy / Move / Delete) using the existing PathPicker. The same dialog handles files and directories regardless of which bcachefs pool the destination lives on.
-
NetworkManager compatibility scaffolding from v0.0.7 has been removed. The legacy networking layer, the one-shot migration cutover, and the Phase-X comments are gone. A clean reconcile of orphan interfaces + NM profiles runs at startup, per-connection NM apply errors surface individually in the UI, and DBus type encoding for MAC / DNS fields aligns with what NetworkManager expects.
TLS / reverse proxy
- ACME automation driven directly through Caddy — lego dropped, the entire TLS pipeline lives in one process.
- Nine DNS-01 plugins compiled in (Cloudflare, Route 53, Hetzner, Linode, Porkbun, Namecheap, DuckDNS, deSEC, RFC2136); per-provider directive emitter on the engine side.
- DNS-01 challenge knobs in Settings → TLS:
propagation_delay(default 30s) and external resolvers (default 1.1.1.1, 8.8.8.8) — useful for split-horizon DNS, restricted egress, or providers with aggressive negative-TTL caching. - Per-host issuance state on the TLS page: per managed name, issuing / active / failed / pending with the verbatim Caddy log line on failure.
- Cert directory polled after admin-API push, so the UI flips to Active as soon as Caddy lands the cert.
- WebUI exposes Caddy's local-CA root via a Download CA Root button — import once on each client to trust every per-name cert the internal CA issues.
- Caddy version pinned at build time for reproducible upgrades.
Apps & ingress
- New Ingress overview page — every Caddy route in one place (host / path / catch-all, handler kind, upstream, per-row cert status for host-match routes).
- "Subdomain" menu is always present at install time; subdomain ingress survives reboots even for apps whose path-prefix mode was auto-disabled.
- Subdomain conflict detection before submit.
- Compose apps persist
ingress_subdomainreliably across restarts (#247). - Auto-detect apps whose absolute-path assets break path-prefix ingress; engine sets
proxy_disabled_reasonwith a human-readable explanation, honoured on reconcile after restart. - Curated sub-path recipes for Grafana and Vaultwarden — known-working env presets at install time.
- Idle-poll
apps.listso containers crashing or installs from another tab show up without a refresh. - Image inspection fetches registry tokens for ghcr.io and quay.io alongside Docker Hub.
- Docker-paste button on the Apps install form (paste a
docker run …and the form fills itself out). - Treat docker named volumes as auto-managed; tag image-default env vars so Edit can grey them out and only highlight values the operator set explicitly.
- Shell button surfaces
exec_commanderrors directly when invocation fails. - App install / remove robustness: real-world fixes from the Haze launch — better error reporting, idempotent re-install paths.
- Live per-app
apps.statsrewritten for one Docker round-trip per frame; the Apps page renders stats instantly on load. - Deploy WS close verifies app state before reporting failure — transient blips during
docker create_containerresolve correctly once the container reports up (#208). - Port form on the Create/Edit dialog reads
Name | Exposed | Internalleft-to-right, matchingdocker run -p HOST:CONTAINERand every other UI in the ecosystem (#271).
Files
- Copy, Move, and Bulk actions on the Files page (#88). Per-row Copy / Move icons, sticky bulk action bar (Copy / Move / Delete / Clear) when one or more rows are selected, select-all checkbox with indeterminate state.
- Cross-filesystem copy works natively — operators can move data between bcachefs pools mounted under
/fswithout dropping to a shell.
Subvolumes
- Per-row Usage column engine-side (closes #81) — one snapshot of who owns what (NFS, SMB, iSCSI, NVMe-oF, apps, VMs, backup jobs) per subvolume, batched in a single RPC.
- Cascade-delete dialog — deleting a subvolume that backs an iSCSI target / NFS share / SMB share / NVMe-oF subsystem now lists what's in use and offers a single "Delete subvolume + N dependents" button. Apps / VMs / backups are surfaced with a direct link to their lifecycle page so cleanup stays explicit.
- Detail pane surfaces Apps and VMs as subvolume consumers — same dependency tree the Usage column reads.
UPS / NUT
- Remote NUT server mode. NASty can now monitor a UPS attached to a different box (Synology, another NASty, a standalone NUT server) over the network — no USB-attached UPS required on the appliance itself.
Networking
- v0.0.7's compatibility scaffolding removed: legacy networking layer gone, one-shot migration cutover gone, stale Phase-X / cutover comments stripped.
- Orphan interface + NM profile reconcile at startup — removed bridges / bonds get their NM profiles and sysfs interfaces cleaned up automatically.
- NM MAC fields encoded as DBus byte arrays, NM DNS fields encoded as the correct family-specific DBus type — apply paths align with what NetworkManager expects.
- Per-connection NM apply errors surface individually in the WebUI for targeted troubleshooting.
- Discovery daemons (
samba-wsdd,avahi-daemon) restart on every network apply, so newly-added bridges / bonds / VLANs stay visible in macOS Finder, Windows Explorer, and Linux file managers (#270). - Network form validates IP / CIDR before submit (#202).
Backups
- S3 backup profile create form exposes the
regionfield (#212). - Backup profile forms expose SFTP
portand retentionkeep_yearly(#213).
System & updates
nasty-topbumped to 0.0.5 — tuning advisor now showsHINTlines with reasoning instead of one-key-apply suggestions; device error counts are session-aware (pre-existing counts dim, only growth highlights bold red);Ctrl-Cquits from any mode; device list grouped by label and natural-sorted (sda1 < sdz1 < sdaa1,nvme0n1 < nvme10n1).- Glossary additions: Caddy and Audit Log entries on the Help page.
opensslanduv/uvxnow on PATH — cert inspection, TLS handshake debugging, and Python-tool one-shots work directly from the box's shell.
Engine reliability
- State-file handling preserves data on parse failure. Eight state files (
auth.json,settings.json,alerts.json,nut.json,tailscale.json,passthrough.json,tuning.json,rate-limit.json) now back the existing file up as.corrupt.<unix-ts>before falling through to defaults, log a warning, and continue — so a malformed JSON stays recoverable instead of being overwritten. - Engine startup is robust to slow / unreachable OIDC IdPs and Caddy admin APIs — both moved to background spawns with bounded retry budgets, so the engine reaches ready state quickly regardless of upstream latency.
- Audit log coverage expanded:
permission_deniedon role-denied RPCs,terminal_opened/vm_console_opened/log_stream_openedon privileged WebSocket opens, and unsafe-deploy entries carry the actual admin's username. - Six engine paths gained observable warning logs when subprocesses like
bcachefs,losetup,blkid, orstatsurface errors — the journal now explains what fell back to defaults instead of swallowing the cause. - WebSocket robustness: server-initiated ping/pong + exponential client backoff (#207); reconnect overlay debounced 800ms so the UI only signals real disconnects (#205).
CI / infrastructure
- systemd-hardened
nasty-engineandnasty-metrics: NoNewPrivileges, LockPersonality, RestrictSUIDSGID, ProtectClock, RestrictRealtime, KeyringMode=private, RestrictAddressFamilies on both; fullProtect*namespace lockdown plus ProtectSystem=strict on metrics. - Workspace tests went from ~410 to ~440 — three previously-empty crate test harnesses (
nasty-apidoc,nasty-backup,nasty-snapshot) gained meaningful coverage. - Cargo / npm / rnix / rowan all bumped to current.
- HTTPS + WSS + security-header smoke assertions in the appliance-smoke CI;
/apps/<name>/ingress short-circuits the registry pull when the image is already local.
Bug fixes
- Compose apps'
ingress_subdomainwas silently dropped on first set (#247). - PathPicker reverted to root on directory click (#252).
- App deploy WS close mid-
docker create_containerno longer shows a false "Connection closed unexpectedly" modal (#208). - ACME issuance had two separate PATCHes (automation + automate) that could cancel each other mid-flight on rapid changes; collapsed into one atomic PATCH.
Proxmox users: NASty requires UEFI. Switch the VM firmware from SeaBIOS to OVMF before installing, otherwise NASty won't boot after the first restart.
![]()
⬇
![]()

