Why "0 Apps" Doesn't Mean What You Think
Server state and the actual server can disagree — neo list and neo status now say so explicitly instead of quietly undercounting.
How state and reality can diverge
/etc/neo/state.json is the record of what Neo believes is deployed. It's usually accurate — but a lost write (a failed neo deploy --all racing another deploy to the same server, before that was fixed), or a container removed with a plain docker rm outside Neo entirely, can leave it out of sync with what's actually running.
What it looked like before
neo list and neo status read counts straight from state. An app running fine, but missing from state, simply wasn't counted — "0 apps" while a container was live and serving real traffic, with no indication anything was wrong.
What they do now
Both commands compare state against docker ps on the server and report the difference:
neo list
→ 1 app(s) running but missing from server state: shop
These containers are serving traffic but neo has no record of them.
Redeploy each one to restore its record: neo deploy --to <environment>
Three categories, each with a different fix: untracked (running, no record — redeploy to restore it), missing (a record exists, no container — redeploy, or neo remove to drop the stale record), and stopped (tracked and present, just not running — neo start <app>).
The root cause that made this necessary
neo deploy --all used to fan every environment out to its own goroutine with no coordination. Two environments happening to share a server each read the old state and wrote back independently — the second write silently overwrote the first one's app entry, even though the container itself kept running fine. Targets sharing a server are now grouped and deployed sequentially specifically to prevent this; the drift detection above is the safety net for every other way state and reality can still diverge.
Checking from a script
neo list --json | jq '.drift'
Returns null when everything agrees, or the same three categories machine-readably — so a monitoring script can tell "an empty server" from "a server whose state was lost" without a human reading the table.
Try it yourself — Neo is free, with every feature unlocked.
Get started free