The Tools I Use: The Emmanuel Asika Tech Stack (2025 Edition)
My complete 2025 tech stack for shipping fast. From Next.js App Router and Supabase to AWS and containerization. A deep dive for Indie Hackers.
The rain is hitting my window here in Ireland, and my laptop fan is spinning up. It’s 2025. The landscape of software engineering has shifted beneath our feet, and if you’re not auditing your toolkit right now, you’re already writing legacy code.
I’ve spent the last few years grinding through high-volume WordPress projects. It paid the bills. It taught me how to deal with clients. But that chapter is closing. My focus now is singular: building scalable SaaS products and mastering the Cloud. I’m deep in a Masters in Cloud Computing, and my late nights are spent wrestling with AWS architectures and Next.js hydration errors.
This isn't just a list of software. This is the operating system for a one-person army. This is how I ship fast, keep my sanity, and build systems that can scale from one user to one hundred thousand without falling over.
Here is the Emmanuel Asika Tech Stack, 2025 Edition.
The Philosophy: Speed vs. Scale
There is a massive tension between "shipping today" and "surviving tomorrow."
When I was doing freelance work, the goal was to hand over the keys and run. Now, as an Indie Hacker and Cloud Engineer, I own the code forever. If the database locks up at 3 AM, that’s my problem. If the AWS bill hits $5,000 because of a memory leak, that’s my money.
My stack is optimized for two things:
- Velocity: I need to go from idea to deployment in hours, not weeks.
- Scalability: I don't want to rewrite the backend just because we got on the front page of Hacker News.
The Frontend: Next.js (The Only Choice)
I have strong opinions here. If you are building a SaaS in 2025 and you aren't using Next.js, you are fighting gravity.
I use the App Router. I know, I know. It was rough in 2023. It was buggy in 2024. But now? It’s the standard. The mental model shift from client-side React to Server Components is the single biggest unlock for performance I’ve seen in years.
Here is why I stick with it:
Server Actions are the killer feature.
Remember creating an API route file, defining a handler, parsing the request, and then fetching that API from the client with useEffect? I don't do that anymore. With Server Actions, my backend logic lives right inside my component (or a separate file) and runs on the server.
It looks like this:
// actions.ts 'use server' import { supabase } from '@/lib/supabase' export async function saveEmail(formData: FormData) { const email = formData.get('email') if (!email) return { error: 'Email is required' } const { error } = await supabase .from('waitlist') .insert({ email }) if (error) return { error: error.message } return { success: true } }
You call that function directly from your form. No API layer to manage. No serialization headaches. It just works. It feels like magic, but it's just standard web fundamentals wrapped in a nice DX.
The UI Library: Shadcn/ui + Tailwind CSS
I used to write custom SASS. I used to fight with BEM naming conventions. Never again.
Tailwind CSS is non-negotiable. Once you memorize the utility classes, your CSS file size stops growing. You stop worrying about class name collisions. You just style and move on.
But raw Tailwind can be slow to build complex components. That is where shadcn/ui comes in.
It is not a component library. You don't install it as a node dependency that locks you in. You copy and paste the code into your project. You own it.
If I need a DatePicker, I run a command, and boom - I have a fully accessible, keyboard-navigable DatePicker component in my /components/ui folder. If I don't like how it looks, I open the file and change the Tailwind classes.
It gives me the speed of Bootstrap with the flexibility of writing it from scratch.
The "Backend": Supabase
I am studying Cloud Computing. I know how to spin up an EC2 instance, install Postgres, configure the firewall, and manage backups.
But for Indie Hacking? I absolutely refuse to do that.
Supabase is my backend. It calls itself an open-source Firebase alternative, but that sells it short. It’s just Postgres wrapped in a super-suit.
Authentication
Auth is hard. Security is terrifying. Supabase Auth handles magic links, social logins (Google, GitHub), and email/password out of the box. It integrates perfectly with Next.js middleware to protect routes.
The Database (Postgres)
Since Supabase is just Postgres, I can use all the SQL power I want. But the real superpower is Row Level Security (RLS).
Instead of writing backend logic to check if user_id == resource_owner_id, I write a policy on the database itself.
create policy "Individuals can view their own todos." on todos for select using ( auth.uid() = user_id );
Now, even if I screw up my frontend query, the database literally won't return data that doesn't belong to the user. That is peace of mind.
Edge Functions
When I need to run some heavy logic - like processing a Stripe webhook or generating an image with AI - I spin up a Supabase Edge Function (Deno). It’s serverless, fast, and sits right next to my data.
The Cloud Layer: AWS & Azure
This is where my Masters degree kicks in. While Supabase handles the "SaaS" data, you often need heavy infrastructure for specific tasks.
I don't just stick to the Vercel happy path. Sometimes you need raw power.
AWS S3 & CloudFront
For file storage, Supabase Storage is great, but sometimes I go direct to AWS S3 for massive archives or video hosting. Configuring CloudFront distributions gives me granular control over caching that higher-level abstractions sometimes hide.
Azure Container Apps
I’ve been experimenting heavily with Azure. For long-running background jobs (like a scraper that runs for 20 minutes), serverless functions time out.
I containerize these scripts using Docker.
FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . CMD ["node", "worker.js"]
Then I throw them onto Azure Container Apps. It scales to zero when not in use, so I'm not paying for a VPS that sits idle 90% of the time. This is the sweet spot between "Serverless" and "Managing a Server."
Infrastructure as Code (IaC)
Clicking buttons in the AWS console is for amateurs. It’s untrackable and dangerous. I am forcing myself to define infrastructure in code.
I use Terraform.
It’s a steep learning curve. But being able to tear down an entire environment and rebuild it with terraform apply is a superpower. It documents your infrastructure. If I lose my laptop or my AWS account gets weird, the blueprint for my kingdom is safe in a Git repo.
The Editor & Local Environment
I live in VS Code. I’ve tried JetBrains, I’ve tried Vim (I’m not cool enough yet). VS Code just has the ecosystem.
Extensions I can't live without:
- Pretty TypeScript Errors: TypeScript errors are usually unreadable garbage. This extension makes them look like human language.
- Tailwind CSS IntelliSense: Essential for seeing the generated CSS.
- GitLens: I need to know who wrote this buggy code. Spoiler: It was me, three weeks ago.
The Terminal
I switched to Warp. It’s a terminal built for the 21st century. It has AI built-in, so when I forget the tar command syntax for the 500th time, I just ask Warp in plain English. It also allows for block selection, which makes copying output way easier than the standard Mac terminal.
AI: The Junior Developer
I don't use AI to write my whole app. That results in spaghetti code. I use AI as a junior developer sitting next to me.
I use GitHub Copilot for autocomplete. It’s scary good at predicting the next three lines of code based on the context of the file. It removes the friction of typing out boilerplate.
For complex architectural questions or debugging, I use ChatGPT (Plus) or Claude 3.5 Sonnet.
Claude is currently winning for coding tasks. I can paste in a massive React component and say, "Refactor this to use a custom hook for the form logic," and it does it with 95% accuracy. It saves me hours of cognitive load.
Deployment: The Vercel vs. Coolify Debate
For early-stage projects and MVPs, I deploy to Vercel.
The DX is unbeatable. You push to Git, and it’s live. The preview deployments for every pull request are essential for testing changes before they hit production.
However, Vercel gets expensive. If a project starts getting real traffic, or if I have a client who wants to own the infrastructure, I look at Coolify.
Coolify is an open-source, self-hosted Heroku/Vercel alternative. I grab a cheap VPS from Hetzner (costs like €5/month), install Coolify, and I can host my Next.js apps, databases, and Docker containers for a fraction of the cost of the big clouds. It’s a bit more maintenance, but that fits the "Indie Hacker" ethos of keeping burn rate low.
Design & Management
I am not a designer, but I have to play one on TV.
Figma is where the UI starts. I don't do high-fidelity mocks anymore. I build a rough wireframe to get the layout right. I use a "UI Kit" file that matches shadcn components so I'm not designing things that are impossible to build.
For task management, I keep it simple. Linear.
Linear is opinionated software. It forces you to work in a specific way - issues, cycles, backlog. It’s fast, keyboard-centric, and beautiful. Jira makes me want to quit the industry. Linear makes me want to close tickets.
Payments: Stripe
There is no alternative. If you want to get paid, you use Stripe.
I wrap Stripe in a library or use a starter kit (like ShipFast, though I usually roll my own configured boilerplate now) to handle the webhooks. Handling webhooks is the most fragile part of any SaaS. You have to handle failed payments, upgrades, downgrades, and cancellations.
I always log stripe events to Supabase immediately so if my logic fails, I have a record of the event payload to replay later.
The "Secret" Sauce: TypeScript
I mentioned it earlier, but it deserves its own section.
I strictly use TypeScript. No any types allowed.
When you are working alone, you forget how your own code works. Types serve as documentation. If I try to pass a string to a function that expects a number, the editor yells at me before I even run the code.
Combined with tools like Zod for schema validation, I can ensure type safety from the database all the way to the frontend UI.
import { z } from "zod" const UserSchema = z.object({ username: z.string().min(3), age: z.number().optional(), }) type User = z.infer<typeof UserSchema>
This robust typing saves me from 90% of the runtime errors that used to plague my WordPress PHP days.
Conclusion: Just Ship It
The tools change. Next year, there might be something faster than Next.js or cheaper than Supabase.
But right now, in 2025, this stack gives me the power of a tech giant with the agility of a startup. It lets me focus on the product, not the plumbing.
The most important tool in the stack isn't the database or the framework. It’s the mindset. It’s the discipline to sit down, ignore the noise, and turn caffeine into code.
Stop bike-shedding over which button component to use. Pick a stack, learn it deeply, and build something valuable.
I've got a Masters to finish and a SaaS to ship. Back to work.
Read Next
My 5-Year Vision: Where Emmanuel Asika is Going Next
Emmanuel Asika breaks down his 5-year plan: transitioning from WordPress freelancing to Cloud Engineering and Indie Hacking. A raw look at tech stacks, strategy, and freedom.
ReadWhy 'Perfect' is the Enemy of 'Shipped'
Stop polishing code nobody uses. Perfection is a liability in SaaS. A deep dive into why technical debt is actually leverage when you're starting out.
Read