Turn any localhost web app into a self-healing Windows "desktop app".
Run local web tools — LLM proxies, dashboards, dev servers — as native Windows
services that start at boot, restart on crash, and open in a chromeless app
window. No more localhost refused to connect. No Docker, no Electron, no
virtualisation required.
Built by Heaventree Ltd. after one too many dead
localhost:20128 tabs.
Local web apps started from a terminal die when the terminal closes, the machine sleeps, or Node hits an unhandled exception. You find out when your browser shows an error page.
Appify puts four layers between you and that error page:
| Layer | Mechanism | Catches |
|---|---|---|
| 1. Windows service | NSSM | Closed terminals, logoff, reboot — starts before login |
| 2. Auto-restart | NSSM restart policy (3s delay, throttled) | Process crashes |
| 3. Watchdog | Scheduled task, every 5 min | Wedged/zombie processes that are alive but not answering |
| 4. Health-gated launcher | Polls the endpoint before opening the window | Ever seeing an error page at all |
The launcher opens the app in Edge/Chrome --app mode: no tabs, no address
bar, its own taskbar entry. Feels like software, is just your browser.
Requires: Windows 10/11, PowerShell 5.1+, admin rights for install.
git clone https://github.com/heaventree/Appify.git
cd Appify
notepad config.psd1 # point it at your app (see below)
# from an elevated PowerShell:
powershell -ExecutionPolicy Bypass -File .\install.ps1That's it. A shortcut lands on your desktop; your app now survives anything short of a power cut — and comes back after that too.
Everything lives in config.psd1 — the only file you edit:
@{
AppName = "9Router" # shortcut + window name
ServiceName = "9Router" # Windows service name
Url = "http://localhost:20128" # what to health-check and open
Command = "auto:9router" # how to start it (see below)
Arguments = "start"
Environment = @("PORT=20128")
...
}| Value | Meaning |
|---|---|
"auto:9router" |
Auto-detect a globally installed npm CLI (resolves the .cmd shim — services can't use the .ps1 one) |
"C:\path\to\app.cmd" |
Literal path to any executable/script |
"%APPDATA%\npm\whatever.cmd" |
Environment variables are expanded |
9Router (LLM proxy — the default)
AppName = "9Router"; Url = "http://localhost:20128"
Command = "auto:9router"; Arguments = "start"; Environment = @("PORT=20128")Any Node app from a cloned repo
AppName = "MyDashboard"; Url = "http://localhost:3000"
Command = "C:\Program Files\nodejs\npm.cmd"; Arguments = "start"
WorkingDirectory = "C:\code\my-dashboard"| Task | How |
|---|---|
| Open the app | Desktop shortcut / Launch.bat |
| View logs | logs\<ServiceName>.log / .err.log (rotated at 10 MB) |
| Restart manually | Restart-Service <ServiceName> |
| Update the underlying app | Update it (e.g. npm update -g 9router), then Restart-Service |
| Remove everything | powershell -ExecutionPolicy Bypass -File .\uninstall.ps1 (elevated) |
Appify/
├── config.psd1 # your settings — the only file to edit
├── install.ps1 # one-time setup (elevated)
├── uninstall.ps1 # clean removal (elevated)
├── Launch.bat # double-click launcher
├── scripts/
│ ├── common.ps1 # shared helpers
│ ├── launcher.ps1 # heal → wait for 200 → open app window
│ └── watchdog.ps1 # scheduled-task revive
├── bin/ # nssm.exe (downloaded on first install)
└── logs/ # service stdout/stderr (gitignored)
Why not Docker? Docker on Windows needs WSL2 which needs virtualisation enabled in firmware — not always available (corporate machines, some laptops). This works everywhere PowerShell does.
Why not Electron? ~200 MB of Chromium to display a page your browser can
already display. --app mode gives you 95% of the experience for 0% of it.
Does it work with multiple apps? Clone the repo once per app (or copy the
folder) with a different config.psd1 in each. Service names must be unique.
Is NSSM safe? NSSM is a long-established open-source
service wrapper. install.ps1 downloads the official 2.24 release on first
run into bin/.
MIT © Heaventree Ltd. — see LICENSE.