What it is: A production B2B starter with a Go backend and Next.js frontend. Both are fully Dockerized with separate containers. No Vercel. No Supabase. Deploy the whole thing on a $6 VPS, or split frontend and backend across different providers. You own the infrastructure.
The problem I was solving:
Every SaaS starter I evaluated had the same issue: they locked me into someone else's platform. Vercel for hosting. PlanetScale for the database. Serverless functions billing per invocation. Fine for prototypes, but costs become unpredictable at scale and migrating away is painful.
I wanted something I could deploy on any Linux box with docker-compose up. Something where I could host the frontend on Cloudflare Pages and the backend on a Hetzner VPS if I wanted. No vendor-specific APIs buried in my code.
Why Go for the backend:
Go gives me exactly what I need for a SaaS backend:
Tiny footprint. The backend idles at ~50MB RAM. On a cheap VPS, that headroom lets me run more services without upgrading. Concurrency without complexity. Billing webhooks, file uploads, and AI calls run concurrently without callback hell. Compile-time type safety. Using SQLC, my SQL compiles to type-safe Go. If the query is wrong, it fails at build time, not in production. Predictable performance. No garbage collection pauses that surprise you under load. The architecture (Modular Monolith):
I didn't want microservices complexity for a small team, but I needed clean separation. I built a Modular Monolith: features like Auth, Billing, and AI are isolated Go modules with explicit interfaces, but they deploy as a single binary.
This structure also made AI coding tools (Cursor, Claude Code) dramatically more effective. Because every module has strict boundaries, the AI knows exactly where new code belongs and doesn't break other modules.
Full-stack, not just backend:
Backend: Go 1.25 + Gin + SQLC (type-safe SQL, no ORM) + PostgreSQL with pgvector Frontend: Next.js 16 + React 19 + Tailwind + shadcn/ui Communication: The frontend consumes a clean REST API. You can swap Next.js for any framework that speaks HTTP. Infrastructure: Separate Dockerfiles for frontend and backend. Deploy together or apart. What's pre-built:
The boring infrastructure is solved so you can focus on your actual product:
Auth + RBAC: Stytch B2B integration with Organizations, Teams, and Roles. Multi-tenant data isolation enforced at the query level. Billing: Polar.sh as Merchant of Record. Handles subscriptions, invoices, and global tax/VAT. No Stripe webhook edge cases. AI Pipeline: OpenAI RAG using pgvector. The retrieval service enforces strict context boundaries to minimize hallucinations. OCR: Mistral integration for document extraction. File Storage: Cloudflare R2 integration. Each feature is a separate module. Don't need OCR? Remove it. Want Stripe instead of Polar? The billing interface is abstracted.
Real-world proof:
This isn't a template I made for GitHub stars. It's the exact code running apflow.co in production. When I added document OCR, I built it as a new module without touching Auth or Billing. The architecture held.
How to try it:
Clone the repo, read setup.md to check the prerequisite, run ./setup.sh, and you have a working B2B environment locally in minutes.
Feedback I want:
I'd appreciate feedback from Go developers on the module boundaries and cross-module interfaces. Also curious if anyone has suggestions for the Docker setup in production deployments.
GitHub: https://github.com/moasq/production-saas-starter
Live: https://apflow.co
But the frontend and backend arent tightly coupled at all.
You can swap Next for Vite, Nuxt, whatever you want and connect it to the same Go backend.
Only thing you'd need to copy over is some auth and billing related stuff on the frontend side
* It wants to be your back-end. If you have a separate back-end, get ready to write back-end auth code twice, and probably in 2 different languages, and some brittle proxy code that will break the next time the Next guys decide they want to change how middleware works (again).
* The maintainers aren't particularly helpful. Having built a couple sites using Next, many of our questions ended up being answered with some variation of, "You're holding it wrong," but it was clear they just didn't want to support our (and other users' submitting issues) scenarios.
* Whether you are on Vercel or not, the team behind Next is very motivated to get you onto Vercel. You can expect their technical choices to go more towards that path. This is at odds with the goals of this project. Coupled with the above, expect to have little to no agency to raise issues and have them solved beyond simple/obvious bug fixes, even after you've invested your project into their platform.
* Next really struggles in situations where your users are your customers' customers, and your customers want something more white-labeled. As soon as this bleeds into the arena of using custom domains per customer and such, some of the advantages of Next start to become disadvantages.
Many of the pieces Next offers are sort of optional, but if you don't fit their idea of how a piece (such as auth via next-auth or their take on server-side components) should work, you're left to solve on your own. It's not the end of the world to have to implement your own auth flow with oidc-client, but it can be a little risky and my brain doesn't hold onto OIDC or OAuth2 so every time I implement an auth flow from scratch I end up having to look up how it should work.
That said, if you end up having to deal with more than a couple of the above things, Next moves from an ok choice for the project to a poor choice.
You’re right that Next wants to be your backend and thats exactly why I kept the real backend separate in Go.
The Go backend handles all auth, billing, database, everything.
Next is just a frontend that calls the API. So if Next keeps changing things or pushes too hard toward Vercel, you swap it out. The backend doesnt care.
The white-label / custom domain point is interesting, hadnt thought about that edge case. Good to keep in mind.
Honestly the backend is the important part here. The frontend is just one way to consume it