Commerce
Catalog, cart, checkout, orders — the storefront business, and where the Nudge Framework lives.
You don’t need to know the internals to run Nudova — but three ideas explain every decision in these docs: it’s headless, it’s modular, and it runs in one of two footprints.
Nudova is a headless commerce API with two separate front ends in front of it — served on three hostnames from one deployment:
| Host | App | Who uses it |
|---|---|---|
your-domain.com |
Storefront (Next.js) — the shop | your customers |
admin.your-domain.com |
Staff admin (Next.js) — orders, catalog, CRM, content | your team |
api.your-domain.com |
API (Django) + the deep-ops console at /admin/ |
the apps, and you |
A Caddy reverse proxy sits in front, terminates HTTPS (real Let’s Encrypt certificates, issued automatically), and routes each host to the right app. PostgreSQL is the database; a scheduler runs background jobs; a sidecar takes nightly backups.
Nudova is a single platform core plus a set of swappable domain modules. You install only the domains your business needs — the rest never load.
The platform (always present) is the reusable core every store gets: identity and accounts, users and profiles, files and media, notifications (email/SMS/push/in-app), payments, billing and invoicing, subscriptions and plans, discounts and referrals, an integration/webhook framework, an audit log, tenant configuration, and reporting.
The domains (install what you need):
Commerce
Catalog, cart, checkout, orders — the storefront business, and where the Nudge Framework lives.
CRM
Sales pipeline — accounts, contacts, leads, opportunities. A staff tool, no public storefront.
CMS
Content — pages, blocks, site settings, email templates, media.
Blog · Helpdesk · Kanban
A blogging vertical, a support/knowledge-base vertical, and a project/task board — each self-contained, installed only where wanted.
One environment variable composes the install:
ENABLED_DOMAINS=product,crm # platform + Commerce + CRMENABLED_DOMAINS |
You get |
|---|---|
| unset | all domains (the default) |
product |
platform + Commerce only |
product,crm |
platform + those two |
"" (empty) |
platform only, zero domains |
The same image and code run in one of two footprints — a single flag,
INFRA_PROFILE, decides the behaviour. You never re-platform to scale; you resize.
| Lite (default) | Full (opt-in) | |
|---|---|---|
| Services | API + Postgres (+ Caddy) | + Redis (Valkey) + Celery worker + beat + websockets |
| Background jobs | inline, strict timeouts | async via Celery |
| Scheduled jobs | cron loop (scheduler service) |
celery-beat (same job code) |
| Real-time | off — the admin polls REST | websockets (live feeds) |
| Sized for | a 2 GB VPS | a 2 vCPU / 8 GB box |
docker compose up -dThe shipped docker-compose.yml is the lite stack.
docker compose -f docker-compose.yml -f docker-compose.full.yml up -dThe overlay adds Redis, a Celery worker + beat, and a websockets process;
retunes Postgres; and raises the memory limits. The lite scheduler is scaled
to 0 — beat owns the schedule.
Start lite for most single-tenant shops at low-to-medium volume. Choose full for high volume, real-time admin feeds, or when inline background work starts adding latency. Moving lite → full later is a one-flag change on the same data and image — see Upgrading → Moving lite → full.
The lite claim — “the whole product runs on a 2 GB VPS” — is a verified number, not a promise. A shipped check boots the lite stack under a realistic load (40 concurrent shoppers, browse/cart/checkout, plus a maximum-size image upload and concurrent server-rendering) and fails if usage crosses the budget or anything is OOM-killed.
| Footprint | Hard limits | Measured peak under load | OOM kills |
|---|---|---|---|
| Lite (2 GB box) | 1952 MiB | ~1037 MiB | 0 |
| Full (8 GB box) | 5696 MiB | ~1635 MiB | 0 |