Skip to content
Last updated Give Feedback

Manual install (advanced)

  • VPS or cloud VM with Ubuntu 22.04+ (2 vCPU / 4 GB RAM minimum for a single-tenant install)
  • Node.js 22 LTS installed (CLAUDE.md §2.1 — older majors are not tested)
  • PostgreSQL 16+ running (local or managed)
  • Redis 7+ running (in-memory fallback exists in dev only — production needs a real Redis)
  • Domain name with DNS pointed to your server
  • Valid license JWT (ECOMMUS_LICENSE) issued by Media Design SRL

You must already have the source on disk. Two paths (per ADR-015):

ModeWhat you receiveHow to refresh
Private Git collaboratorRead access to MDLABS-cmd/ecommusgit pull origin <release-tag>
Per-release tarballSigned .tar.gz for each tagged releaseNew tarball on every release; verify the GPG signature

Premium plugins / themes always pull from the private npm registry, gated by your license JWT.

Set these on the server (via .env, your hosting provider’s secrets manager, or systemd EnvironmentFile=):

Terminal window
NODE_ENV=production
DATABASE_MODE=postgres
DATABASE_URL=postgres://ecommus:CHANGE_ME@localhost:5432/ecommus_prod
# Auth — separate access + refresh secrets are mandatory in production.
# The API refuses to boot if either is missing or starts with "dev-".
JWT_ACCESS_SECRET=<min-32-char-random>
JWT_REFRESH_SECRET=<different-min-32-char-random>
# Cache + queues
REDIS_URL=redis://localhost:6379
# License — your customer license JWT (Ed25519 signed by license-server)
ECOMMUS_LICENSE=eyJhbGciOiJFZERTQSI...
# Public URLs
FRONTEND_URL=https://mystore.ro
ADMIN_URL=https://admin.mystore.ro
SUPER_ADMIN_URL=https://superadmin.mystore.ro
# Private npm registry (premium plugin downloads)
NPM_REGISTRY_URL=https://npm.ecommus.cloud
NPM_REGISTRY_TOKEN=<your license-bound npm token>

Generate strong secrets:

Terminal window
node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"

The complete environment reference (every key, default, required-in-prod flag) lives at Environment Variables.

Terminal window
# Create database and user
psql -U postgres <<SQL
CREATE DATABASE ecommus_prod;
CREATE USER ecommus WITH PASSWORD 'CHANGE_ME';
GRANT ALL PRIVILEGES ON DATABASE ecommus_prod TO ecommus;
SQL
# Run migrations
DATABASE_MODE=postgres \
DATABASE_URL=postgres://ecommus:CHANGE_ME@localhost:5432/ecommus_prod \
node --experimental-strip-types packages/db/src/migrate.ts
Terminal window
# Install workspace dependencies (uses your license-bound npm token for premium scopes)
npm ci
# Build storefront
npm --workspace @ecommus/storefront-astro run build
# Build admin (Next.js standalone output)
npm --workspace @ecommus/admin run build
# Build super-admin
npm --workspace @ecommus/super-admin run build
# Build API
npm --workspace @ecommus/api run build

Use a process manager like pm2 (or systemd / Docker). Production must run four processes — API, Admin, Super-admin, Storefront:

Terminal window
npm install -g pm2
pm2 start dist/apps/api/server.js --name ecommus-api --env production
pm2 start "next start -p 3001" --name ecommus-admin --cwd apps/admin
pm2 start "next start -p 3002" --name ecommus-super-admin --cwd apps/super-admin
pm2 start "node ./dist/server/entry.mjs" --name ecommus-storefront --cwd apps/storefront-astro
pm2 startup
pm2 save

If you also self-host the license server, add a fifth process for it on a separate VM (recommended: don’t co-locate with customer tenants).

The framework’s deploy guide ships a Caddyfile — Caddy is the recommended path because it auto-provisions Let’s Encrypt certs and the template already routes the four upstreams. Nginx works too:

/etc/nginx/sites-available/ecommus
server {
listen 443 ssl http2;
server_name api.mystore.ro;
location / { proxy_pass http://127.0.0.1:4000; proxy_http_version 1.1; }
}
server {
listen 443 ssl http2;
server_name admin.mystore.ro;
location / { proxy_pass http://127.0.0.1:3001; proxy_http_version 1.1; }
}
server {
listen 443 ssl http2;
server_name superadmin.mystore.ro;
location / { proxy_pass http://127.0.0.1:3002; proxy_http_version 1.1; }
}
server {
listen 443 ssl http2;
server_name mystore.ro www.mystore.ro;
location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; }
}

Provision certs:

Terminal window
certbot --nginx -d mystore.ro -d www.mystore.ro -d admin.mystore.ro -d superadmin.mystore.ro -d api.mystore.ro
Terminal window
curl https://api.mystore.ro/health # {ok:true, db:true, redis:true}
curl https://admin.mystore.ro/api/health # {ok:true, app:"admin"}
curl https://superadmin.mystore.ro/api/health # {ok:true, app:"super-admin"}

If any returns non-200, the corresponding service is down — re-check pm2 logs <name> first.

The supported upgrade flow is documented in Upgrade procedure (semver policy, pre-upgrade checklist, migrations, rollback). The short manual version:

Terminal window
# Snapshot the DB FIRST (see deployment/backup-restore for the full drill)
pg_dump -Fc ecommus_prod > /backup/ecommus_prod-$(date +%F-%H%M).dump
# Pull the new release
git fetch --tags
git checkout v<new-release>
npm ci
# Re-build the apps (same commands as step 4)
npm --workspace @ecommus/storefront-astro run build
npm --workspace @ecommus/admin run build
npm --workspace @ecommus/super-admin run build
npm --workspace @ecommus/api run build
# Run any new migrations
node --experimental-strip-types packages/db/src/migrate.ts
# Reload all four processes
pm2 reload all

If the release notes mention breaking changes in plugin contracts, run plugin smoke tests before promoting.