Medusa v2 as our hosted commerce + POS product (2026-10-13)
Medusa v2 as our hosted commerce + POS product (2026-10-13)
Companion to medusa-nextjs-commerce-study (stack choice: Medusa won, MIT end-to-end). Young's brief added the product shape: two features — sell services online + POS — delivered as e-commerce website hosting with POS functions, multi-hosting environments on Docker. This note covers the deployment/tenancy half, verified against the live docs (.md agent endpoints, fetched 2026-10-13).
Summary
- Medusa's official Docker path is a 4-service compose stack (Postgres 15 + Redis 7 + medusa backend + Next.js storefront) built on the dtc-starter pnpm monorepo — one Dockerfile serves backend and storefront. Verified publishable topology; MIT licence means we can repackage it as our hosting template without restriction.
- Production is not the dev compose: docs mandate a server + worker split (
MEDUSA_WORKER_MODE=server|worker, admin disabled on the worker) against shared Postgres + Redis, plus production infrastructure modules (Redis event-bus/caching/locking/workflow-engine, S3 file, SendGrid notification). ≥2GB RAM per host recommended. - Tenancy for a hosting product: per-tenant stack (compose project per client: own DB, own Redis, own containers) is the clean fit — blast-radius isolation, per-tenant secrets, per-tenant Admin, straightforward backup/restore. Shared cluster multi-tenancy (
medusa start --cluster/--servers --workers, v2.11+) is for scaling one tenant bigger, not for isolating many tenants.
Verified deployment facts
- Base image:
node:20-alpine; Medusa requires Node ≥ 20.19.0. Backend ports 9000 (API/Admin), 5173 (admin HMR dev); storefront 8000. - Dev compose (docker-install doc): postgres:15-alpine, redis:7-alpine,
medusa(build .),storefront(build . +start-storefront.shentrypoint). pnpm-workspace gotcha documented: anonymous volumes overnode_modulesdirs are required or bind-mounts clobber installed deps. Names/ports/volumes must be re-suffixed per project — the docs' own multi-project guidance maps 1:1 onto per-tenant stacks. - Production build:
medusa buildoutputs to.medusa/server; start command per docs:cd .medusa/server && npm install && npm run predeploy && npm run start, wherepredeploy=medusa db:migrate(runs migrations + syncs links on every update — the tenant-update pipeline hook). - Env parity server/worker: shared
DATABASE_URL,REDIS_URL,COOKIE_SECRET,JWT_SECRET(docs require generated secrets in production),STORE_CORS/ADMIN_CORS/AUTH_CORS;DISABLE_MEDUSA_ADMIN=falseon server,trueon worker. - Cluster mode (v2.11+):
npx medusa start --cluster 4 --servers 25% --workers 75%— splits processes across cores of ONE tenant's host. - Health/lifecycle:
<APP_URL>/health→OK; Admin at<APP_URL>/app; admin user seeded vianpx medusa user -e … -p …. - Storefront deployment is separate (any Node host); docs warn Vercel's free plan is personal/non-commercial only — for our hosting product the storefront ships in our own Docker env anyway.
- Services-selling: native in the data model — products/variants without stock + price lists; bookings/subscriptions layered as a custom module (the framework layer is exactly for this). POS: fork Agilo POS (MIT, Admin REST over HTTPS); its roadmap gaps remain: no payments integration yet (manual payment plugin path exists), no offline mode.
Proposed hosting product (our Docker offer)
Per-tenant stack, one template, fleet-managed:
| Layer | Per tenant | Shared (our infra) |
|---|---|---|
| Postgres 15 | own container + volume | backup cron → offsite |
| Redis 7 | own container | — |
| Medusa server | own container, WORKER_MODE=server | reverse proxy + TLS (nginx, per-tenant domain) |
| Medusa worker | own container, WORKER_MODE=worker, admin off | — |
| Storefront (Next.js) | own container or static export | same proxy |
| Agilo POS (client's tablets) | SaaS-style client to their own backend | — |
- Onboarding = one script: generate compose project + secrets (
openssl rand), set domain in proxy,predeploy, create admin user, health-check/health. Mirrors our probe-pattern: no stack is "live" until/healthreturns OK and the admin loads. - Updates: git-tagged tenant template; update = pull new tag →
predeploy(auto-migrate) → container restart → health re-check. Checkout-before-cutover: keep old volumes; rollback = prior tag. - Sizing: docs floor 2GB RAM/tenant (server+worker+storefront+PG+Redis comfortably in 4GB); small tenants can bin-pack 2–4 compose stacks per VPS node, isolated at compose-project level. Start conservative: 1 tenant per 4GB until measured.
- Multi-env: staging + production per client = two compose projects, same image tag, separate DBs/Redis — cheap to offer and matches the docs' "multiple testing environments" pitch (which Medusa Cloud sells — our version is the self-hosted equivalent).
Capacity on prod Oracle ARM (measured 2026-10-13, Young confirmed specs)
Node: Oracle Ampere A1, aarch64 (Neoverse-N1) × 4 OCPUs, 23Gi RAM, 200GB disk (180G free) — verified live via lscpu/free/df. Current residents: OS (1.4Gi baseline) + solos-docs + pulse-site.
- All official Medusa base images (
node:20-alpine,postgres:15-alpine,redis:7-alpine) are multi-arch — the whole stack runs unmodified on ARM64. - Conservative capacity (read + measured, using docs floor + breathing room):
- ~4 full tenant stacks (4.8 GB steady each: server 2 + worker 1 + storefront 1 + PG 0.7 + Redis 0.1), or
- ~6 small tuned stacks (3 GB each: server 1.2 + worker 0.6 + storefront 0.7 + PG 0.4 + Redis 0.1).
- CPU: 4 vCPU shared with the two Nuxt sites — thin tenants (~0.3–0.5 vCPU steady) fine; count builds, not just runtime:
medusa build/Next.js builds spike hard, so image builds happen once per template release, shared image for all tenants — rebuilding per-tenant would saturate the 4 vCPUs trivially. - Disk: 180G free ≫ shared image layers (~2G) + per-tenant PG/Redis volumes (small); backup/storage budget, not RAM, is the ceiling long-term on this node.
- Scaling path: node 1 = pilot (2–3 tenants max, colocated with solos-docs/pulse) → dedicated node 2 for tenants when pilot stabilises (same ARM profile, more RAM). Per-tenant stacks make node-to-node evacuation = compose project move.
Implications / next steps
- Build the tenant template repo (Ace lane): dtc-starter fork + prod-optimised multi-stage Dockerfile (docs' Dockerfile is dev-oriented: bind-mounts, dev server) +
docker-compose.prod.yml(server/worker split) + onboarding script +/health-gated cutover script. - Per-tenant secrets register follows fleet hygiene: 0600, never in compose files.
- Services catalogue module (custom module) is the differentiator for "sell services online"; scope it after first tenant ships with physical/simple goods.
- POS pilot caveat: Agilo POS works today for cart/draft-order flow on client tablets; payments-in-POS is the known gap — in-store payments can start via the manual-payment path while the payment-terminal roadmap lands.
- Medusa Cloud is the competitor bench-mark, not a dependency: their pitch (push-to-deploy, preview envs, storefront hosting) is our feature list for the self-hosted product.
Sources
- docs.medusajs.com
learn/installation/docker(.md agent endpoint) — full compose/Dockerfile/start scripts, multi-project port/volume suffixing guidance. learn/deployment+learn/deployment/general— server/worker split, production modules, env matrix, predeploy, health check, admin Bootstrap.learn/production/worker-mode—workerModeconfig, admin-disable pattern, cluster mode--servers/--workers(v2.11+).- GitHub API + READMEs (2026-10-13): medusajs/medusa v2.21.2 MIT; medusajs/dtc-starter MIT, active; Agilo/medusa-pos-starter MIT, FAQ payment/offline gaps.
- Prior study medusa-nextjs-commerce-study for stack comparison + licence matrix.
Medusa × EVE integration pattern (2026-10-13)
Young's synthesis direction: Medusa + EVE is a great match. Verified every claim in the brief against live sources — all check out, including two I initially doubted and then confirmed wrong. This note registers the combined architecture on top of hermes-eve-cohosting-study and medusa-hosting-docker-study.
Next.js Commerce + Medusa ecosystem study (2026-10-13)
All repo stats pulled live from the GitHub API on 2026-10-13; blog + docs pages fetched live. Extends saleor-study (technical half) into the Medusa stack and the extension/auth model.

