I am a working student at BMW and a CS student at LMU Munich, which means my time to build my own things arrives in awkward two-hour slabs between lectures, shifts, and the occasional attempt at a social life. That constraint, more than any blog post or conference talk, is what actually shaped the stack I reach for. When you have two hours, you cannot afford to spend ninety minutes wiring up auth, fighting a build tool, or remembering how your own deployment works. The stack has to get out of the way.
So this is not a "best tools of 2026" listicle. It is the stack I personally run on adatepe.dev and on most of my side projects, with the reasoning for each choice and, more importantly, the reasoning for the things I deliberately do not use. A solo founder's stack is a set of bets about where your scarce attention goes. I would rather make those bets on purpose.
Before I lay out my own choices, I am genuinely curious what you reach for when nobody is watching.
Whatever you picked, hold onto it as you read the rest, because my goal is not to convert you to my exact tools.
The one rule: minimize moving parts you have to think about
Every tool you add to a solo project is not just a dependency. It is a thing you have to upgrade, debug at 1 a.m., and hold in your head when something breaks. A team of ten can absorb a microservice mesh and a bespoke CI pipeline. A solo founder cannot. The cost of a tool is not its install size; it is the rent it charges on your attention.
This is why I am suspicious of stacks that look impressive in an architecture diagram. Kafka, a service mesh, three databases for three access patterns, a separate BFF layer. Each of those is a defensible decision at scale and a self-inflicted wound when you are one person trying to get a product in front of users. The question I ask before adding anything is blunt: if this breaks while I am asleep, can I fix it in the two hours I have tomorrow? If the answer is no, it does not go in.
The corollary is that boring, well-documented, widely-used tools beat clever ones. When something goes wrong with Next.js or Postgres at 11 p.m., the answer is a search away because ten thousand people hit the same wall first. When something goes wrong with the obscure framework you picked because it benchmarked 8% faster, you are the documentation now.
The whole stack falls out of one blunt question. Predict it:
The stack I actually run
Here is the concrete shape of it, the same one running my own site and most of what is on /#projects.
Next.js 16 with the App Router, React 19. I went back and forth on this. The App Router had a rough adolescence and the caching defaults burned a lot of people, including me. But for a solo founder the value is that one framework handles routing, server rendering, server actions, and the API layer. I do not run a separate backend service for most projects. A server component fetches from the database and renders; a server action mutates and revalidates. That collapses two codebases into one, and "one codebase" is the single highest-leverage decision in solo work.
TypeScript in strict mode, no exceptions. Strict mode is non-negotiable for me precisely because I am working in fragmented sessions. When I come back to code after a week of BMW shifts, the types are the notes I forgot to write. strictNullChecks alone has caught more would-be production bugs than any test I wrote on purpose. The cost is real on day one and pays compounding interest forever after.
Drizzle ORM on Postgres, hosted on Supabase. This is the part I am most opinionated about. I use Drizzle instead of Prisma because Drizzle's schema is just TypeScript, the generated SQL is predictable, and the query builder reads like SQL instead of hiding it. I want to know what query is hitting my database. Supabase gives me a real Postgres instance, not a proprietary abstraction I cannot leave, plus auth and storage if I want them.
// A Drizzle query reads close enough to SQL that I trust what it sends.
const recent = await db
.select()
.from(visitors)
.where(gte(visitors.createdAt, since))
.orderBy(desc(visitors.createdAt))
.limit(50);
The one sharp edge worth flagging loudly: migrations are not magic. On my projects, a deploy does not apply schema changes automatically. I generate a migration file with Drizzle and apply it by hand against production. I learned this the way everyone learns it, by merging a schema change, watching every API route that read the new column fall over, and writing a postmortem afterward. Whatever your stack, know exactly when and how your schema reaches production. That gap is where the silent outages live.
Tailwind CSS with CSS variables for theme tokens. I keep my design tokens (--bg, --text, --border-card) as CSS variables and let Tailwind consume them. This means dark mode and theme tweaks are one place, not scattered across a hundred components. For a solo founder with no designer on call, a small constrained set of tokens is the difference between a site that looks intentional and one that looks like it accreted.
When I onboard someone to one of my BMW-adjacent side projects, the first thing they open is package.json, and I want every line in it to justify itself. So here is the dependency core of a solo stack with the reasoning behind each pick, the same lens my LMU coursework trained me to apply to any system.
{
"dependencies": {
"next": "^16",
"react": "^19",
"drizzle-orm": "latest",
"zod": "latest"
}
}Next plus React covers routing, rendering, and the API layer in one mental model. Fewer moving parts means less context-switching when you are the whole team.
Four dependencies is not the whole story, but it is the spine, and everything else hangs off it. With that core in view, the last piece is where all of it actually runs.
Vercel for hosting. Yes, it is more expensive per request than rolling my own. It is also the reason I have never once thought about my deploy pipeline. Push to main, it builds, it ships. The money I spend there buys back hours, and hours are the thing I have least of.
Naming the parts is one thing; seeing how a single request travels through all of them is what makes the choices click. Here is the actual path a request takes through this stack, layer by layer:
A request first hits Vercel's edge, which serves static assets straight from cache and routes everything else to my app. The tradeoff worth knowing: cached HTML is fast but stale until something revalidates it, so I am deliberate about which routes are static and which are dynamic.
Each click trades a little speed for a lot of simplicity, which is the through-line of the whole stack. Here is the same stack as a switcher, with the one reason each piece earns its slot:
One framework handles routing, server rendering, server actions, and the API layer. I do not run a separate backend for most projects.
A server component fetches and renders; a server action mutates and revalidates. That collapses two codebases into one, and one codebase is the single highest-leverage decision in solo work.
Testing: the part solo founders skip and shouldn't
The lazy take is that solo projects do not need tests because you can just click around. That works until you have five projects and cannot remember which one you broke. My own site carries around 1,280 tests running under Bun, and the reason is not discipline for its own sake. It is that tests are how I ship on a Tuesday night without staging the whole thing in my head.
I do not aim for 100% coverage. I aim for coverage of the things that hurt when they break: the database layer, the API routes, the i18n contract, the email pipeline. Pure rendering of a marketing section can go untested; the function that decides whether a visitor email gets sent cannot. Bun as the test runner matters here too because it is fast enough that I run the suite without it being a chore, and a test suite you avoid running is decoration.
The number that converted me was the suite runtime. My 1,280 tests finish in well under ten seconds on Bun, where the equivalent Jest setup on an earlier project took closer to forty. That gap sounds small until you multiply it by how often you run it. A suite that returns in eight seconds gets run on every save; a forty-second one gets run once before a commit, if you remember, which means it stops being a feedback loop and becomes a toll. Speed is not a vanity metric here. It is the difference between tests you trust and tests you route around.
The gotcha I keep relearning is what to mock. Early on I mocked the database in my API tests, and the tests stayed green while the real queries broke, because I had tested my idea of the database, not the database. Now the layer where data actually matters runs against a real Postgres instance, and the lesson generalizes: the Drizzle ORM I reach for is exactly the seam I refuse to mock, because a query that compiles can still be wrong against real constraints. Mock the network and the clock. Do not mock the thing you are actually trying to verify.
prettier --write . && tsc --noEmit && eslint . && bun test
That command is my entire CI philosophy compressed into one line. Format, typecheck, lint, test. If all four are green, I trust the change. If they are not, I fix the cause rather than suppressing the warning, because every // eslint-disable is a note to my future self that says "I gave up here."
This gate did not appear fully formed. It accreted over a couple of years of small, specific pain, and each step earned its place by catching something that had already burned me at least once.
I started with bun test and nothing else, because a fast runner made the suite cheap enough to actually run. That alone caught the regressions that used to ship on Tuesday nights. It did not catch the typos that never reached the test path.
The order matters less than the principle: each layer in that gate exists because something specific got past the layer before it.
Where I deliberately spend complexity
Minimalism is not the same as primitivism. There are a few places I happily add complexity because the leverage is real.
One is internationalization. My site ships in 14 languages with a typed contract that fails the build if any locale is missing a key. That is genuinely more machinery than a solo project "needs," but it forced a discipline that pays off elsewhere: every user-facing string lives in one structured place, which makes the whole app easier to reason about, not just easier to translate.
The other is observability. I log structured one-line JSON for the events I care about and can filter them after the fact. This sounds like overkill for a side project until the first time a user reports "it didn't work" and you have to reconstruct what happened with zero evidence. A little logging up front is the cheapest insurance in software.
Before you argue about which framework is fastest, there is a deeper variable that decides how quickly you actually ship. Predict it:
That answer is worth sitting with, because it cuts against almost every stack post you will read this year.
Why familiarity beats novelty for a solo founder
The honest reason my stack barely changes is that I already know it cold. I can open a Drizzle query, a server action, or a Tailwind token file after a week of BMW shifts and be productive in minutes, because none of it requires me to reload a mental model first. That recall is the real asset, and it is invisible on every benchmark chart.
The hidden cost of learning while shipping is that you pay twice. You pay the time to learn the new tool, and you pay the time you lose to the bugs you write because you have not yet learned it. Worse, those two taxes land exactly when you are trying to make progress on the actual product, so the project that was supposed to move forward stalls while you read documentation for a thing that was already solved by the tool you knew. As a solo founder your scarcest resource is uninterrupted context, and novelty is a context tax disguised as an upgrade.
That does not mean never adopt anything new. It is worth it when the new tool removes a category of pain you keep hitting, not when it is merely newer or marginally faster. The test I use is simple: does this solve a problem I actually have right now, and am I willing to schedule dedicated learning time for it instead of learning it under deadline pressure? If yes, I adopt it on purpose, in a low-stakes corner first. If it is just shinier, I let someone else be the documentation.
The things I tried and dropped
A stack is also a list of graveyards, and mine is honest. I spent a while convinced I needed a separate backend, a proper API service in its own repo, talking to the frontend over a typed client. For a team that boundary is healthy. For me it doubled my deployment surface, doubled the places a change had to land, and bought me nothing a single Next.js app did not already give me through server actions and route handlers. I tore it out and have not missed it once.
I also flirted with edge-everything. Running every function at the edge sounds fast and modern, and for a genuinely global, latency-sensitive product it can be the right call. But the edge runtime is a different, more restricted environment than Node, and as a solo founder I do not want two execution models in my head, each with its own quirks about which libraries work. I run the boring Node runtime for almost everything and reserve the edge for the rare case that actually benefits. The simplicity of one mental model is worth more to me than a few milliseconds nobody is measuring yet.
The last thing I dropped was premature abstraction in my own code. Early on I built generic wrappers around the database, the API, the config, all in anticipation of needs that never arrived. Those wrappers became their own maintenance burden, an extra layer to read through every time I debugged. Now I write the direct version first and only extract an abstraction when the third copy of something appears. Two copies is a coincidence; three is a pattern worth naming. Before three, abstraction is just speculative complexity wearing a respectable name.
A note on AI tooling, since everyone asks
I use AI coding assistants daily, and I want to be unsentimental about where they fit in a solo stack. They are extraordinary at the parts I already understand and want to type faster: boilerplate, test scaffolding, a translation between two formats, the tedious shape of a Drizzle schema I can read at a glance. They are dangerous exactly where my own attention is thin, because a confident wrong answer in the database layer is worse than no answer at all when there is no second engineer to catch it in review.
So the rule I hold is that the quality gate does not relax for AI-generated code; if anything it tightens. Strict types, the lint and test gate, and the habit of actually reading what was produced are not optional just because a model wrote it quickly. The model writes faster than I do. It does not care whether the result is correct, and on a solo project I am the only one who does. The tooling multiplies whatever discipline you already have. It does not supply the discipline.
Before you add the next tool to your solo stack, run it through the one rule. Each unticked box is attention you're renting out that you won't get back:
If you are still not sure which corner of this stack to lock in first, here is the routing I actually use when someone asks me where to start. Answer two questions honestly and it points you at the layer that will pay back fastest for your situation.
Which piece of the solo stack should you lock in first?
Two questions about your constraints, then a concrete place to start.
What kills your momentum most right now?
Wherever that points you, the underlying move is the same: spend your first effort on the layer that is renting the most of your attention today.
Why I pay for managed services instead of self-hosting
People sometimes look at my Vercel and Supabase bills and ask why I do not just run a cheap VPS and host it all myself. On paper they are right: a box somewhere would cost a fraction of what I pay, and I am technically capable of standing up Postgres, Nginx, and a deploy pipeline. That is exactly the trap. I have the skills to self-host. What I do not have is the attention to operate it.
This is the part of the buy-versus-host math that solo founders get wrong. The comparison is not "predictable monthly bill" against "smaller monthly bill." It is a predictable bill against an unpredictable tax on the one resource I cannot buy more of. A VPS does not bill me in euros; it bills me in 1 a.m. pages when the disk fills, in the afternoon I lose to a kernel update that broke something, in the dependency upgrade I keep postponing because there is no managed layer doing it for me. As a working student with two-hour build slabs, that kind of work does not just cost time. It costs the specific time I was going to spend shipping, and it arrives without warning.
So the rule I use is narrow and strict: a managed service earns its price when it removes a category of problem from my head entirely, not when it merely makes that problem a little easier. Vercel does not make deploys easier; it deletes the question of how my deploy pipeline works. Supabase does not make Postgres administration easier; it removes backups, patching, and uptime from the list of things I am personally responsible for. Those are whole worries I never carry into a build slab.
The line I will not cross is paying for a service that locks me in without removing real work. That is why Supabase is plain Postgres I could move, not a proprietary store. I pay to outsource operational anxiety, not to rent a cage. Money I can earn back at BMW. The Tuesday night I would have spent debugging my own infrastructure, I cannot.
The valid stack is the one you measure against yourself
When I show people this stack, the first reaction is often a counter-stack. Someone runs Rails and ships circles around me. Someone else lives in Laravel, or SvelteKit, or a Go backend with a thin frontend, and they are right too. That is the part I want to be honest about: there is no single correct solo stack, there is a wide spread of valid ones, and most of the religious arguments online miss this entirely. The poll at the top of this post exists because I genuinely believe four different answers can all be the right one.
What separates a good choice from a bad one is not the benchmark. It is fluency. The comparison that actually predicts whether you ship is not your framework against the fastest framework, it is your stack against your own recall. Can you open it after a week away and be productive in minutes, or do you have to reload a mental model first? A stack that scores worse on every chart but lives in your fingers will beat a faster one you are still learning, because solo, your bottleneck is your own attention, not requests per second. Measure against yourself, not the leaderboard.
What I would reconsider about this stack next year
Honesty means admitting this stack has an expiration date I cannot see yet. The first thing I would watch is where it stops scaling. Server actions and route handlers in one Next.js app are a gift while I am the whole team, but the day a project actually needs a separate worker for long jobs, a queue, or a second service owned by someone else, the integrated monolith stops being a feature and starts being a wall. I am betting that day arrives later than the internet thinks. If it arrives sooner, the cost is real: pulling a backend out of a tightly coupled Next.js app is harder than building it separate from the start.
The dependencies I bet on could move under me too. I lean hard on Vercel and Supabase, and both are companies, not laws of physics. Pricing changes, a pivot, or an acquisition could each force my hand. I keep that risk bounded on purpose: Supabase is plain Postgres I can move, so my data has an exit even if the vendor does not. Vercel is the one I am most exposed to, because the convenience is exactly the thing that would be painful to unwind. I accept that trade for now, with eyes open.
The cost of staying on the bleeding edge is the quieter tax. Next.js 16 and React 19 are recent enough that I sometimes hit a sharp edge nobody has documented yet, which is the exact opposite of the boring-and-proven rule I preach. If the App Router caching model churns again, or if React's server component story shifts under me, I will have paid for being early.
So before swapping anything, I watch three signals: am I fighting the framework more than the problem, am I postponing features because the data layer cannot keep up, and has a dependency changed its terms in a way I cannot absorb. Until one of those trips, I leave it alone. When the data layer is what wobbles, Drizzle, the ORM I reach for is the piece I trust most to survive a migration, because it is just TypeScript and SQL I can read.
What I'd tell my past self
If you are a solo founder picking a stack in 2026, resist the urge to optimize for a scale you do not have yet. You are not going to get Hacker News-hugged to death next week. You are going to spend the next six months trying to get ten people to care, and the stack that helps you with that is the boring, integrated, well-documented one that lets you ship a change in the two hours you actually have.
Pick tools you can debug alone. Write the types. Test the things that hurt. Know how your database migrations reach production. Spend your complexity budget on the two or three things that genuinely compound, and be ruthless about everything else.
If you want to see this stack in practice rather than in prose, the projects on /#projects are all built on some version of it, and I write up the lessons as I go on /blog. The short version is the only version that has ever mattered to me: fewer moving parts, more shipped software.
Which part of the stack are you wrestling with?
Pick one, I've written up the reasoning behind each choice.
Pick whichever layer you're stuck on, but if you'd rather see the whole stack in motion, every project on adatepe.dev runs some version of this blueprint.