A practical breakdown of how FactoStack runs a multi-tenant manufacturing ERP for paying customers on roughly $20/month.

I'm building FactoStack, a manufacturing ERP for Indian MSMEs. It already has paying customers, live production data, and actual businesses depending on it every day.
The interesting part is that the whole setup is still extremely lean. I’m not doing anything clever for the sake of it. I’ve just tried to keep the architecture boring, cheap, and reliable enough for the current stage.
Here’s the honest breakdown.
The stack
Backend: NestJS (TypeScript), with modules split by domain like inventory, production, compliance, and payroll
Frontend: React + TanStack Router + TanStack Query, shipped as a static build and served through Nginx
Database: Amazon Lightsail Managed PostgreSQL, separate from the app instance with automated backups
API hosting: Amazon Lightsail 512MB instance running NestJS behind Nginx
Admin app: React + TanStack Router, deployed on Cloudflare Pages
Marketing website: Next.js, also on Cloudflare Pages
Email: Nodemailer
Payments: Razorpay, mainly because it fits Indian billing, UPI, and GST invoicing cleanly
Auth: JWT with tenant-aware access and feature flags
Analytics: PostHog free tier
Alerting: Telegram bot for demo bookings, signups, and onboarding events
Total infra spend is roughly $20/month right now. The admin app and marketing site are effectively free on Cloudflare Pages, so almost all of the paid cost is just the app box and managed Postgres.
Why Lightsail over EC2 or Railway
Three simple reasons:
Predictable flat pricing. I don’t want billing surprises while the product is still early.
Managed Postgres removes a category of operational work I don’t want to own yet.
A 512MB box is enough for this stage. NestJS + Nginx idles at around 180MB RAM, so I still have room without paying for headroom I’m not using.
I’ll upgrade when usage or revenue justifies it, not because a startup architecture template says I should.
Why Cloudflare Pages for the admin app and website
The admin app is an internal tool for creating tenants, managing users, and toggling features. It’s a static frontend, so there’s no reason to keep it on the same box as the API.
The marketing site is the same story. Static output, CDN distribution, free hosting. Separating both from the core app keeps deploys safer too. A marketing site change should never be able to affect production API uptime.
The multi-tenant setup
I’m not running a separate database per customer. There’s a single Postgres instance, and every core table carries a tenant_id. Tenant context is injected at the request layer and enforced through the application.
For an early-stage ERP, this has been the pragmatic choice:
One migration path for everyone
Straightforward debugging and support
Flat infrastructure costs as new customers come in
Faster shipping because I’m not maintaining tenant-specific infra
Will this be the forever architecture? Probably not. But it’s the right one for the current stage, and that matters more.
Feature flags are doing real work
Every major module is controlled through flags tied to the tenant record. That includes things like HRMS, e-invoicing, production tracking, and food-processing-specific workflows.
When I onboard a customer on a specific plan, I just enable the right flags from the admin app. No code changes, no separate deployments, no branching logic at the infra level.
That matters because my customers are not identical. A food processing customer paying ₹2,999/month may need raw material traceability and batch tracking. A compliance-only customer paying ₹2,990/year may only need GST tooling. Same product, different surface area.
What this architecture optimises for
It optimises for shipping speed, low fixed cost, and operational simplicity.
That means a few deliberate trade-offs:
I’d rather have one well-understood server than a more distributed setup I barely need
I’d rather rely on managed database backups than spend time pretending I enjoy database operations
I’d rather add complexity after repeated demand, not before the first serious scale problem
There’s enough monitoring to know when something is wrong, enough isolation to keep things manageable, and enough flexibility to support multiple customer types. That is the bar I care about right now.
What I’d do differently
I used to overthink infrastructure. Before launch, I spent far too much time reading about containers, orchestration, and multi-region setups. None of that was the bottleneck.
The real bottleneck was always simpler: can a customer log in, complete work, and trust the product tomorrow morning?
Running on a small VM also forced better discipline. Fewer wasteful queries. Less dependency bloat. More attention to what actually runs in production. Constraints helped the codebase.
The point
You do not need a stack full of trendy services to run a real B2B SaaS. You need a setup you understand, can afford, and can recover when something breaks.
For me, that currently means NestJS, React, a small Lightsail instance, managed Postgres, and Cloudflare Pages.
FactoStack’s lowest plan starts at ₹999/month. Infrastructure is roughly $20/month. That math is completely fine at this stage, and it keeps the pressure where it belongs: on building something customers will keep paying for.
0
1
0