Deploying a Node.js app
Any directory with a package.json that has a start script (or a main entry) deploys as a Node.js 22 container. No Dockerfile.
What does a deployable app look like?
// package.json
{ "name": "notes", "main": "server.js" }
// server.js
const http = require("node:http");
http.createServer((req, res) => {
res.end("hello from " + (process.env.SMALLCLOUD_APP ?? "smallcloud"));
}).listen(process.env.PORT || 8080);
Then: smallcloud deploy --name notes. Smallcloud runs npm install --omit=dev at build time (network is available during build only), runs your build script if you have one, and starts the app with your start command.
The contract your app runs under
- Listen on
$PORT(8080). The auth proxy fronts every request; authenticated users arrive with anx-smallcloud-userheader. - Write only to
$DATA_DIR(/data, persistent, quota'd) and/tmp(64 MiB scratch). The rest of the filesystem is read-only. - No network egress at runtime — unless granted. By default the app cannot call external APIs. Deploy with
--allow-egress api.example.comand the app receivesHTTP_PROXY/HTTPS_PROXYcredentials for a forward proxy that permits exactly those hostnames (use an env-aware proxy agent, e.g. undici'sEnvHttpProxyAgent). Everything else is refused and audited. - Resource caps: 0.5 CPU, 256 MiB memory, 256 processes.
- Scale-to-zero: after 15 idle minutes the container stops; the next request restarts it transparently in under half a second. Keep startup fast and state in
/data.
How do I debug a misbehaving app?
smallcloud logs notes --tail 200
Crashes on boot usually mean the app tried to write outside /data, bind a port other than $PORT, or reach the network. The security model explains exactly what the sandbox enforces.
Storing data
Use SQLite or the built-in key-value store on your app's private volume — see persistence & KV.