Self-hosted, one process
Running in about
ten minutes.
Node 22 or newer and a machine you control. No container required, no managed service, and no account with anybody — including us.
Get it and install
Node 22.12 or newer — check with node -v. On Node 20, npx prints a screen of EBADENGINE warnings and create stops with one line saying which version to install. The install pulls the optional image library; let it.
npx astrobaas create my-site
cd my-site
npm installTo work on AstroBaaS itself rather than run it, clone the repository instead: github.com/operator888/astrobaas.
Check the secret
AUTH_SECRET signs every session cookie. Under 16 characters, or missing, and a production build refuses to start rather than pretending to be secure. create already wrote a fresh one into .env; give each server its own.
npx astrobaas init --force # new .env, new secret
npx astrobaas secret # or just print one to pasteCreate your account
Interactive. It replaces the seed admin, so there is no default password left behind — a production build refuses to accept one anyway.
npm run setup
npm run dev # http://localhost:4321/adminTell it its own address
Settings → Address of this CMS. Skip this and media URLs are guessed from whatever host each request arrived on — right for the admin, wrong for a storefront on another domain. The health check warns until it is set.
Point your frontend at it
Everything is JSON over HTTP. The response envelope carries media_base, which is what you join relative image paths to.
curl -s localhost:4321/api/products?limit=3 | jq
curl -s localhost:4321/openapi.json | jq '.paths | keys'Deploy, and assert it
A reference nginx vhost and systemd unit are in deploy/, with limits measured against real traffic rather than chosen. The deep health check answers 503 when anything essential is broken, so a deploy script can simply stop.
npm run build
curl -fsS -H "Authorization: Bearer $HEALTH_TOKEN" \
https://cms.example.com/api/health/deepWorth knowing before you deploy
Four things that
fail quietly.
- Body size
- nginx defaults to 1 MB, so every image upload fails with a 413 the app never sees and cannot explain. The reference vhost raises it per location.
- Readiness
- Poll /readyz, not /healthz, before switching traffic over. Migrations run at boot, and only readyz waits for them.
- Uploads path
- Point it outside the release directory. A deploy that swaps the release takes the uploads with it otherwise, and every image added since the last build 404s.
- Two addresses, not one
- site_url is where the SITE lives — canonicals, sitemap, feed. public_site_urlis where the CMS itself answers, and it is the one that makes media_base right for a storefront on another domain. Setting only the first leaves the deep health check warning and the images resolving against whatever host asked.