Deployment and operations
A high-level picture of what a running installation consists of and how it is kept healthy. It deliberately contains no hostnames beyond the public domains, no credentials and no server details.
Live addresses on helsen-med.com
| Public address | What it serves |
|---|---|
| helsen-med.com | The storefront (Next.js): Arabic at /ar, English at /en. www.helsen-med.com redirects to it. |
| seller.helsen-med.com | The seller web console (static build). |
| delivery.helsen-med.com | The dispatch and delivery web console (static build), for dispatcher accounts. |
| api.helsen-med.com | The API at /api/v1, its Swagger UI at /docs, the health checks, realtime connections and product media. |
| docs.helsen-med.com | This documentation site: static files served by nginx. |
| Android APKs | Buyer, seller and driver apps, distributed as files by the team (not on Google Play yet). They call api.helsen-med.com and pin its certificate's public key. |
The admin console is built but not published on any public address; that is pending a decision. Administrators use the API's Swagger UI meanwhile. See Live platform & first steps for what works today.
Server layout, process management, TLS, backups, environment variables and access are documented in the operations runbook kept with the team — infra/deploy/README.md in the repository — not on this public site. Nothing here should be read as a deployment procedure.
What has to run
Production is one server. nginx terminates HTTPS with Let's Encrypt certificates that renew automatically and reuse the same key, so the apps' certificate pins stay valid across renewals. nginx also rate-limits per IP, more strictly on the sign-in routes, in front of the API's own per-IP limits. Everything else runs in Docker.
| Process / service | Why |
|---|---|
API (dist/main.js) | Serves every client. |
Worker (dist/worker.js) | Drains the outbox and runs every scheduled sweep. Without it no notification is processed and no scheduled job runs — expired reservations, auto-completion, payouts and reconciliations included. |
| PostgreSQL 16 | System of record, including the outbox and job locks. |
| Redis 7 | Required by configuration; reserved for multi-instance features (presence, socket revocation). |
| Meilisearch 1.12 | Buyer product search. |
| SeaweedFS (S3-compatible) | Object storage for product media, documents and proof-of-delivery photos. MinIO is used only in development. |
| Storefront (Next.js) | Server-rendered storefront. The seller and delivery consoles are static Vite builds served by nginx. |
- imgproxy is not deployed: product images are served as the original uploads, not resized.
- Email, SMS and push drivers are
none. Notifications are created and queued but not sent; in-app notifications work. When SMTP (or an SMS or push account) is configured, the queued backlog goes out. - Card payments: Stripe and Paymob are unset. Cash on delivery only.
- The admin console is not published.
The go-live checklist records that the cache, realtime presence and a few other pieces are in-process today, so they are correct only for a single API instance until Redis-backed adapters exist. Scale vertically until then.
Health checks
GET /health— liveness; the process is up.GET /ready— readiness; reportsreadywhen the database answers,degradedwhen it does not.
Releases
- The API image is built from
infra/docker/api.Dockerfile(multi-stage, non-root) and tagged by commit, neverlatest. - Database migrations are forward-only (Prisma Migrate, applied with
migrate deploy). Schema changes follow expand/contract so old and new code can run side by side — seedocs/zero-downtime-migrations.md. - Rolling back after a release that migrated the database is not a simple image rollback; follow runbook §8.
Incident runbooks
docs/runbooks.md in the repository covers, among others:
- The ledger does not balance
- A payment webhook was dead-lettered
- Checkout is slow
- Database connections saturated
- The outbox is backing up
- Stock is oversold
- Driver cash does not match the ledger
- A deploy needs rolling back
- No drivers on shift
- Location pings are drowning the database
- The worker is down, or a job is not running
The launch readiness status of every item (blockers, data and recovery, observability, security) is tracked in docs/GO_LIVE_CHECKLIST.md.
Publishing this site
The site is plain HTML, one stylesheet and one small script, with no build step. Point an nginx server block for docs.helsen-med.com at the docs/site directory with index index.html; English pages sit at the root and Arabic pages under /ar/.