Most portfolio sites list “Docker, GCP, Supabase” as chips. This article is how I actually scale client apps: what each layer is for, what I refuse to put on it, and how that showed up on SVSFood and Ezlearn.
The shape of the stack
I treat infrastructure as three contracts:
- Docker is how software is packaged. If it is not in an image, it is not what production runs.
- Google Cloud Platform is where images run and where files live. Cloud Run, Cloud SQL or a VPC path to Supabase, Cloud Storage, Secret Manager, Cloud Build.
- Supabase is production PostgreSQL with auth, Row Level Security, and realtime when the product needs live order or presence updates - accessed from app code through Prisma.
Frontends are often Next.js on Vercel when they are content and dashboard surfaces (Jara, LeetRun). APIs, workers, and anything that must stay warm or talk to hardware stay in Docker on GCP. Mixing those two is intentional, not indecision.
Docker as the unit of deploy
I containerize Node and Fastify services with multi-stage builds: a build stage with devDependencies, a runtime stage with only what the process needs. The image listens on a port, exposes a `/health` route, and reads secrets from the environment. Cloud Run starts that process, routes HTTPS, and scales the container count with traffic.
Why not a bare VM
A Compute Engine VM is fine as a jump box or for a stateful exception. It is a poor default for app servers. Patch levels drift. Node versions drift. “I SSH’d and npm installed” is not a rollback plan. Docker plus Cloud Run gives me immutable deploys: revert means serving the previous digest. That matters when you are on-call for a restaurant kiosk at dinner rush.
What goes in the image
- The compiled or bundled API (Fastify + TypeScript).
- Prisma client generated against the production schema.
- A non-root user and a pinned Node LTS base.
- No .env files. Secrets arrive from Secret Manager at runtime.
Local `docker compose` runs the API next to a Postgres that matches production (or a Supabase local stack). CI builds the same Dockerfile. The promotion path is an image tag, not a new ritual.
GCP as the runtime
I chose Google Cloud Platform for Ezlearn over AWS and over “put the whole product on Vercel” for three reasons that still hold:
- Pricing and latency in Asia for Compute Engine and Cloud SQL were better for a bootstrapped EdTech targeting Indian learners.
- Docker is a first-class citizen. Cloud Run is a container product, not an afterthought.
- Cloud Storage plus a CDN is the correct place for course video, certificates, and kiosk assets - not the Node process.
Cloud Run for APIs
SVSFood’s Fastify API - kiosk, admin, website, rider - is the kind of service Cloud Run is for: bursty lunch and dinner traffic, idle overnight, needs HTTPS and a custom domain, must not require me to size a VM. Concurrency is tuned so one instance can hold multiple in-flight orders without melting. CPU is allocated only during request processing unless a WebSocket path needs a minimum instance.
Storage, SQL, and secrets
Ezlearn course video never streamed from the application server. Objects live in a bucket; the app mints URLs. Certificates are rendered server-side as PDFs and stored the same way. Secrets (payment keys, session material, third-party tokens) sit in Secret Manager and are mounted as env on the Cloud Run service. I do not keep production secrets in GitHub or in a Vercel dashboard for the API layer.
Supabase as the data plane
Supabase is PostgreSQL I do not have to baby-sit, plus auth and realtime when I want them. On SVSFood, Prisma talks to Postgres hosted on Supabase. Orders, inventory, menus, coupons, riders, and audit logs are relational on purpose. I will not put that in a document store and then re-implement joins in application code.
Auth and Row Level Security
For products where the client app can talk to the database under a user JWT, Supabase Auth plus RLS is a force multiplier. Admin consoles and payment webhooks still go through Fastify - those paths need server secrets and idempotency. The split is: Supabase for identity and row-level rules; custom API for money and vendors.
Realtime without a second architecture
Kiosk and rider status can move over Supabase realtime or over WebSockets on Fastify. I pick based on who owns the write path. If the write already goes through Fastify (inventory reservation, payment capture), I broadcast from there. If the write is a simple presence or chat-like update, Supabase realtime is enough. I do not run Redis “because scale” on a product that does a few thousand orders.
Case: SVSFood - four clients, one pipeline
SVSFood needed a kiosk, an admin console, an online ordering site, and a rider app. The constraint: one menu, one inventory, one order state. Docker packaged the Fastify API. GCP hosted it. Supabase PostgreSQL was the source of truth. Next.js handled console and website. React Native handled kiosk and rider. PhonePe and Petpooja stayed behind the API so a tablet never held a settlement key.
Scale here is not millions of QPS. Scale is correctness at dinner rush across three outlets, offline kiosks that sync, and an operator who can change a price once. The stack is sized for that. ₹7L+ in month one on owned devices is the outcome that matters.
Case: Ezlearn - EdTech on GCP for 1,000+ MAU
As CTO I owned architecture and shipping for Ezlearn: subscriptions, Hindi-first course delivery, AI learning roadmaps, certificates, a job portal. The platform ran on Next.js, Node, PostgreSQL, and GCP with 99.9% uptime and 1,000+ monthly active users. Video on CDN-backed storage kept the Node process small. Certificates were a server-side PDF pipeline into Cloud Storage. GCP’s Asia pricing made that viable for a bootstrapped company in Noida.
What I do not do
- I do not run Kubernetes for a single API. Cloud Run is enough until it is not; I have not hit that wall on these products.
- I do not put the database in a container on the same Cloud Run service as the API.
- I do not use Supabase as a replacement for payment orchestration or complex vendor adapters.
- I do not multi-cloud a client MVP. One cloud, one database, one container registry.
The architecture, simply
Vishu Pratap scales production apps by packaging APIs in Docker, running them on Google Cloud Run, storing relational data and auth in Supabase PostgreSQL (or Cloud SQL), and putting media on Cloud Storage. The pattern is proven on SVSFood’s food-tech stack and Ezlearn’s EdTech platform. Deeper notes: Docker in production, GCP architecture, Supabase and PostgreSQL.