Skip to content

Deployment

This section describes how tenant subdomains are created and served in production, hosting considerations, and the third-party integrations the platform depends on. It contains no secrets or environment-specific addresses.

Domain map

Hostname Serves Where
ndakita.com the company site Vercel, ndakita_group_site
build.ndakita.com the Ndakita Build product site same Vercel deployment, mapped by middleware
{slug}.build.ndakita.com one tenant ERP each the application server, Frappe DNS multitenancy
givehaven.ndakita.com GiveHaven Netlify, givehaven-v2
email.ndakita.com Brevo branded email links Brevo, not a website

<base-domain> below is build.ndakita.com. Everything sits under one registered domain, so a single Search Console domain property covers the lot and the marketing site, the product site and the customer's own instance all read as one brand.

Subdomain provisioning

Tenant subdomains are served through a single wildcard configuration rather than per-tenant DNS entries.

One-time configuration

  1. Wildcard DNS. A wildcard A record (*.<base-domain>) and the apex record point to the application server. Every tenant subdomain, including those not yet created, then resolves.
  2. Wildcard TLS certificate. A single certificate for *.<base-domain> is issued through the DNS-01 challenge and renewed automatically. A wildcard certificate avoids per-certificate issuance limits that would otherwise be reached as the tenant count grows.
  3. DNS-based multitenancy. Frappe is configured for DNS multitenancy so the web server routes each request to the site whose name matches the request host.

Per-tenant steps (automated during provisioning)

bench new-site {slug}.<base-domain> ...
bench setup nginx
systemctl reload nginx

Because DNS and TLS are covered by the wildcard configuration, no per-tenant DNS or certificate action is required. The tenant address becomes reachable immediately after the site is created and the web server is reloaded.

The platform service is granted a narrowly scoped privilege to reload the web server only, rather than broad administrative access.

Hosting

ERPNext is memory-intensive; each tenant maintains its own database while sharing the bench worker processes. A single mid-sized virtual server (for example, 8 vCPU and 16 GB of memory) is sufficient for the platform service, the metadata database, and an initial set of tenants. The system scales first by increasing server capacity, then by separating the database onto a dedicated host and moving provisioning to background workers.

Provider selection balances cost, latency to the target market, and data residency. Providers with an African region offer lower latency and local data residency, which can be a customer requirement, at a higher cost than value-oriented European or North American providers.

Production readiness checklist

  • Point the platform database at PostgreSQL and apply migrations.
  • Enable real provisioning on the host that runs the bench.
  • Configure strong platform secret and API keys, and a restricted list of allowed origins.
  • Configure the transactional email sender and the verification link address so sign-up confirmation emails are delivered.
  • Configure webhook signing secrets and any callback IP allowlists.
  • Enable asynchronous execution: run the Celery worker and the beat scheduler, and set the rate-limit store to the shared (Redis) backend so limits hold across workers.
  • Verify the Frappe scheduler is running on each tenant site. This is a different thing from the platform's Celery beat above, and provisioning does not currently enable it explicitly. Two customer-facing features depend on it and both fail silently without it: the daily low-stock alert to site managers (ndakita_build.custom.reorder_alert) and the 30-day client document expiry reminder. Nothing errors; the emails simply never arrive. Check with bench --site <slug>.<base-domain> doctor, and enable with bench --site <slug>.<base-domain> enable-scheduler.
  • Schedule the tenant backup sweep and configure the off-box backup shipping and retention (see below).
  • Complete the wildcard DNS, wildcard certificate, DNS multitenancy, and scoped web-server reload configuration.
  • Turn on Web Analytics for the marketing-site project in the Vercel dashboard. There is no environment variable: <Analytics /> in app/layout.tsx does the rest, and it only reports from a Vercel deployment. Custom events need the Pro plan; page views do not.
  • Set FORMSPREE_PROJECT_ID on the marketing-site build (Vercel) so lead forms submit; forms are defined in the site's formspree.json and deployed with npx @formspree/cli deploy (needs the secret FORMSPREE_DEPLOY_KEY, which never leaves the developer machine).
  • Set NEXT_PUBLIC_SITE_URL on the marketing-site build. Canonical tags, og:url, og:image and every cross-host link derive from it. Left unset it falls back to https://ndakita.com for metadata, but companyUrl() and buildUrl() stay relative, and a relative company link rendered on the Build host resolves into the /build tree and 404s.
  • Decide whether the apex or www is primary, and make the two agree. Today they disagree: NEXT_PUBLIC_SITE_URL is the apex, so every canonical, og:url, og:image and sitemap entry names https://ndakita.com, while Vercel's domain configuration 308s the apex to https://www.ndakita.com. Nothing is broken for a visitor, but every canonical points at a redirect rather than at the document that serves the page, which is the same class of problem as the site-wide canonical bug fixed in July. Fix it in whichever direction is wanted: set the apex as the primary domain in Vercel, or set NEXT_PUBLIC_SITE_URL=https://www.ndakita.com. Do not fix only one side.
  • Set ASSESSMENT_ENDPOINT on the marketing-site build if ERP-assessment leads should go to Apps Script or the platform rather than to the assessment Formspree form. Unprefixed on purpose; see the security notes.
  • Set NEXT_PUBLIC_PREVIEW_DATA=true on the GiveHaven (givehaven-v2) build for as long as the homes and needs it serves are seeded rather than real, and remove it at launch. It drives the "Preview build — test data" bar at the top of every page. The bar was previously gated on NODE_ENV !== "production", which meant it only ever showed on a developer's own machine: every deployed build is a production build, so the one audience it exists for — anyone opening givehaven.ndakita.com on a phone — never saw it, and the seeded homes read as real. Whether the data is real is a property of the environment, not of the build mode, so it needs its own flag. Local next dev shows the bar with or without the variable; set it to false to suppress it there.
  • Set TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY (Cloudflare Turnstile) on the marketing site so the lead forms are captcha-protected and bots cannot exhaust the Formspree submission quota; without them the forms still work but unprotected. Set both or neither. With the secret set and no site key reaching the browser the widget never renders, /api/lead rejects every submission as a failed captcha, and lead capture silently drops to zero. Set the unprefixed TURNSTILE_SITE_KEY; the build republishes it as NEXT_PUBLIC_TURNSTILE_SITE_KEY, and setting that prefixed name directly has no effect.

Backups and recovery

Every tenant with a live site is backed up on a schedule that runs on the beat scheduler, capturing the database and files and recording each outcome. Local backups on the bench host are shipped off-box for retention; that off-box copy, not the local files, is what satisfies the retention target. Restores and a whole-host rebuild follow a documented procedure, and recovery is validated periodically by restoring into a scratch site. The recovery objectives, the off-box shipping approach, and the exact steps are maintained in the platform repository's disaster-recovery document.

Third-party integrations

Integration Purpose Notes
M-Pesa (Daraja) Primary payment method for the target market Provides sandbox and production environments. Production access requires a registered short code and provider approval.
Stripe International card payments Provides test and live modes.
Transactional email Sign-up verification, welcome messages, receipts Requires sender-domain authentication (SPF and DKIM).

All integration credentials are supplied through environment variables or a secrets manager and are never committed to a repository.