# How to Choose a Side Project Tech Stack You Can Still Maintain
> Choose a side project tech stack you can still maintain: the stack you know, one database, one app, bought login and a six-week test.

```yaml
url: "https://sidehunt.io/studio/how-to-choose-a-tech-stack-for-a-side-project"
markdown: "https://sidehunt.io/studio/how-to-choose-a-tech-stack-for-a-side-project.md"
title: How to Choose a Side Project Tech Stack You Can Still Maintain
slug: how-to-choose-a-tech-stack-for-a-side-project
category: Growth
tags:
  - side projects
  - tech stack
  - side project tech stack
  - boring technology
  - SQLite
  - Postgres
  - monolith
  - dependency updates
  - indie makers
  - Growth
published_at: "2026-10-19T11:05:04.252Z"
updated_at: "2026-10-19T11:05:04.608Z"
featured_image: "https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx9qhF3TKydfDnaYRZuIMoALgyEwj7cr2xOmXWi"
```

## Article

To choose a tech stack for a side project, pick the language and framework you already know, one boring database, one deployable app, and bought (not hand-built) login and payments. Then check that future you could fix a bug after six weeks away. The best stack for a side project isn't the fastest or the newest. It's the one you can still open, run and change on a tired Tuesday evening with an hour to spare. If you want to learn something new, spend that on one piece, or make it a separate learning project.

This guide is for developers and technical makers with a day job and roughly five to ten hours a week. The external facts here (the SQLite guidance, GitHub's Dependabot docs, the Renovate docs and Dan McKinley's essay) were read in October 2026. The example near the end is made up, and so are its numbers.

## Why is "what's the best stack?" the wrong question?

Because "best" usually means best for a team with full-time engineers, real traffic and someone on call. You have a few evenings a week, a brain that's already spent eight hours on someone else's code, and long gaps when life gets in the way.

So the real constraint isn't performance. It's the person who opens the repo after six weeks away and has forgotten everything. That person is you. Every choice you make now either helps them or leaves them a puzzle.

A better question is: what can I ship and keep running with the hours I have, using what I already know? The answer is usually less exciting than whatever's trending. That's fine. Users can't see your stack. They see whether it works and whether you fixed their bug.

## Is this a learning project or a shipping project?

Be explicit about this before you write any code, because the two goals pull in opposite directions.

- A learning project exists to teach you something. New language, new framework, new database: go for it. It's allowed to be slow, half-finished and thrown away. Nobody depends on it.
- A shipping project exists to get something in front of users and keep it running. Every new technology you add is time spent learning instead of shipping, plus new ways for it to break that you don't recognise yet.

Mixing them is how a lot of side projects die. You start a shipping project, decide it's also the perfect excuse to learn three new things, and two months later you're still fighting tooling.

Dan McKinley's essay [Choose Boring Technology](https://mcfunley.com/choose-boring-technology) has a useful model for this. He suggests that "every company gets about three innovation tokens," and that each new or unfamiliar technology spends one. He's writing about companies. He lists Postgres, Python and cron among his boring choices, and says the big advantage of boring tech is that "their failure modes are well understood." My adaptation for a solo side project: you get one token, not three. Spend it on the piece you're most curious about, and keep everything else familiar.

Another idea from the essay works well solo: before adding anything new, ask how you'd solve the problem with what you already have. McKinley's point is that this catches the case where the real "problem" is that you just want to use the new thing. On a side project that's allowed, but say it out loud and count it as your token.

![](https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx93FMtFTy1duKC2cTLjUHtkW4FmRlen8BZa9Jp)

## Which stack decisions should you make first?

Make them in this order. Each one narrows the next, and most of them should take minutes, not evenings.

- Language and framework: the one you know best. If you write Python at work, Django or Flask. Ruby, Rails. PHP, Laravel. TypeScript, whatever full-stack framework you already use at work. Knowing where things go and how to debug them is worth more than any benchmark.
- One database. A relational one, almost always. More on this below.
- Login: buy it, don't build it. Use your framework's built-in auth or a hosted auth provider, so you're not writing password resets and session handling yourself. If you're unsure which sign-in options to offer, this guide on [choosing a login method for a small SaaS](https://indiehunt.io/studio/how-to-choose-a-login-method-for-your-indie-saas) covers it.
- Payments, if you charge: use a payments provider and its hosted checkout. Don't build billing.
- Hosting: somewhere you can deploy with one command. Pick for how little you'll have to think about it. If you're watching the bill too, there's a separate piece on [keeping side project monthly bills under $20](https://sidehunt.io/studio/how-to-keep-your-side-project-monthly-bills-under-20).

Notice what isn't on the list: a message queue, a cache, a search engine, a separate front-end framework, container orchestration. You might need one later. You'll know because something specific will be slow or broken. Add it then, not on day one.

And if you're still deciding what the first version should even do, that's a different decision from the stack. Settle [what goes into a weekend-sized MVP](https://sidehunt.io/studio/how-to-scope-a-side-project-mvp-you-can-ship-on-weekends) first. A small scope makes almost any boring stack good enough.

## Should a side project be one app or several?

One app. One codebase, one deployable, one thing to start locally and one thing to deploy. I'm opinionated about this because the alternative eats evenings you don't have.

Splitting into a separate front end and back end, or into several services, makes sense when different teams need to ship independently or when parts of the system have wildly different scaling needs. For one person with evenings, it mostly means:

- Two or more things to run before you can test a single change.
- An API contract between your own pieces that you have to keep in sync by hand.
- Two sets of dependencies to upgrade, two deploys, two places a bug might live.
- More configuration to forget between sessions.

A server-rendered app with a bit of JavaScript where you need it covers a lot of side projects: trackers, dashboards, booking tools, tools for a club or a niche. If you genuinely need a rich client, keep it in the same repo and deploy it with the same command.

If one part later needs to be separate, a monolith is easy to split. The reverse, merging five half-finished services back together, is a miserable weekend.

![](https://txmhk1zrnc.ufs.sh/f/xSkWTCqmKWx9RhfD3ntTzUGZQ3ClwP6S5EAm9MxFpkuYaN7X)

## Which database should a side project use?

One boring relational database. For most side projects that means SQLite or Postgres.

The SQLite team's page on [appropriate uses for SQLite](https://www.sqlite.org/whentouse.html) is refreshingly direct. It says SQLite "works great as the database engine for most low to medium traffic websites (which is to say, most websites)," and that a site with fewer than 100K hits a day should generally be fine, calling that "a conservative estimate, not a hard upper bound." The same page says when a client/server database like Postgres may work better: many concurrent writers (SQLite allows one writer at a time), data on a different machine from the app, or a site busy enough to need multiple servers.

Translated for a side project:

- Pick SQLite when the app runs on one server with a persistent disk and you like the idea of the whole database being one file.
- Pick Postgres when your host offers it managed, when you already know it from work, or when you expect several app instances writing at once.

Either way, use one. Not a relational database plus a document store plus a separate key-value store because each is "best" at something. Check your framework's docs for how well it supports whichever you pick, and use its migration tool from day one so schema changes are written down, not remembered.

## Managed services or your own VPS?

Think about this in maintenance hours, not money. A virtual server you manage yourself can be cheap and flexible, but it comes with a list of chores that are now yours: operating system updates, security patches, certificate renewals, disk space, backups, firewall rules, and noticing when any of that stops working.

A managed platform does most of that for you, in exchange for less control and a few quirks to learn once.

My rule of thumb: if you already run servers at work and enjoy it, a VPS is fine. If you don't, pick managed hosting and a managed database, and spend those hours on the product. The worst version is a hand-configured server nobody wrote down, which works until the day it doesn't, six months after you last logged in.

Whichever you pick, write the deploy steps in the README. If deploying means remembering a sequence of commands, it should be one script.

## What is the six-week test?

This is the check I'd run on any side project stack before committing to it. Imagine you don't touch the project for six weeks: a busy quarter, a holiday, a new baby. Then a user reports a bug. Could you fix it in one evening?

You pass if four things are true:

- A README with the actual commands. How to install, run, test and deploy. Not prose, commands.
- One-command dev setup. A script or make target that installs dependencies, sets up the database and starts the app. If it takes six manual steps, you'll get one wrong.
- Pinned versions. A committed lockfile ( package-lock.json , poetry.lock , Gemfile.lock or your ecosystem's equivalent) and a pinned language version, so "install" gives you the same thing it did last time.
- An example env file. A committed .env.example that lists every setting the app needs with fake values, so you're not hunting for which variable you forgot.

The README section can be this small:

```
`## Run it locally cp .env.example .env make setup make dev ## Test and deploy make test make deploy`
```

If you can't write that block for your planned stack without hedging, the stack is too complicated for the time you have. Simplify before you build, not after.

## How do you keep dependencies and upgrades cheap?

Every dependency is a small promise to keep it updated. Some are worth it: your framework, your database driver, a mature library for something hard like date handling. Many aren't: a package for something you could write in ten lines, or three libraries doing the same job.

A few habits keep upgrades boring:

- Ask before adding. Could you do it with the standard library or your framework? Is the package actively maintained? Would you notice if it disappeared?
- Commit the lockfile and upgrade on purpose, not by accident.
- Book an upgrade evening. Monthly or quarterly, put one evening in the calendar: update dependencies, run the tests, read the changelog for anything major, deploy. Small regular upgrades are much easier than one giant jump after two years.
- Let a bot open the pull requests. GitHub's docs on [Dependabot version updates](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates) describe them as automated pull requests that keep dependencies current even when there's no known vulnerability, turned on with a dependabot.yml file in the repo. Its options reference lists monthly and quarterly schedules and a way to group updates into fewer pull requests, which suits a monthly upgrade evening. If you're not on GitHub, [Renovate](https://docs.renovatebot.com/) does the same job across several platforms and lets you schedule when its pull requests arrive.

One detail worth knowing from GitHub's docs: if you stop interacting with Dependabot's pull requests, it temporarily pauses updates and tells you. That's another reason to batch them into a regular evening rather than letting them pile up.

## When should you rewrite or switch stacks?

Almost never mid-project. A rewrite feels productive, but users get nothing new until you're back where you started.

It might really be the stack if:

- A core feature is genuinely hard to build in it, and you've written down why (McKinley suggests exactly this exercise).
- The framework or a key library is no longer maintained and has unpatched security problems.

It's probably you, not the stack, if:

- You're bored and a new framework looks fun.
- The code is messy, which a rewrite in a new stack won't fix. A tidy-up in the current one will.
- Someone online said your stack is dead.
- The project has stalled and a rewrite feels like a fresh start.

If you do switch, move one piece at a time while the old version keeps running.

## A decision table for side project stacks

Decision

Default pick for a side project

Deviate when

Language and framework

The one you know best

It's openly a learning project, or this is your one innovation token

App shape

One deployable monolith, server-rendered where possible

You genuinely need a rich client; keep it in the same repo

Database

One relational database: SQLite or Postgres

Many concurrent writers or several servers (pick Postgres)

Login

Framework auth or a hosted auth provider

Almost never build it from scratch

Payments

A payments provider's hosted checkout

Don't build billing yourself

Hosting

Managed platform with one-command deploys

You already run servers and enjoy it

Background jobs

A scheduled command or a jobs table

A real queue only when jobs are heavy or frequent

Dependencies

Few, well-maintained, lockfile committed

A hard problem a mature library already solves

Upgrades

One monthly or quarterly upgrade evening

A security fix lands; do that one now

## A made-up example: Ifeoma and Partpinner

Everything here is made up: the person, the project and every number.

Ifeoma is a hospital lab scientist who sings in a community choir of about 40 people. The choir keeps losing track of who has which printed part and who's coming to rehearsal. She decides to build Partpinner, a small web app where singers mark attendance and see which parts they're holding, with a reminder email the day before rehearsal.

She writes Python scripts at work, has used Django on a course, and has been wanting to learn Go. Her first plan uses everything. Here it is next to the plan she actually built:

Piece

First plan

Trimmed plan

Purpose

Ship Partpinner and learn Go at the same time

Ship Partpinner; learn Go in a separate toy project

App shape

Single-page front end, Go API, separate notifications service

One Django app with server-rendered pages

Database

Postgres plus Redis plus a document store for "flexible" data

SQLite, one file, on one server with a persistent disk

Login

Hand-written auth with email verification

Django's built-in authentication

Reminders

A message queue and worker

A daily scheduled command that sends the emails

Hosting

A small container cluster on rented servers

One managed host, deployed with one command

Vendors

Five SaaS accounts set up before the first user

One email-sending service

Estimated build

"A couple of months"

Three weekends

She spent no innovation tokens on Partpinner, because the choir needed it before the concert season. She learned Go on Sunday mornings with a throwaway project.

Then the six-week test happened (in the story): the choir took a seven-week summer break, and when rehearsals restarted, a singer reported reminders going out at the wrong hour. Ifeoma opened the README, ran setup, found the timezone setting and deployed the fix in one evening. With the first plan, she'd have been restarting four services and trying to remember how the queue worked.

## A pre-build checklist

Check

What good looks like

Project type

You've written down whether this is a learning or a shipping project

Innovation token

At most one new technology, named on purpose

App shape

One repo, one deployable

Database

One relational database, migrations from day one

Login and payments

Bought, not built

Hosting

Deploy is one command or one push

README

Install, run, test and deploy commands written down

Versions

Lockfile committed, language version pinned

Settings

`.env.example` lists every variable

Upgrades

A recurring upgrade evening in your calendar

## Common traps

- Resume-driven stack choices. Picking tools because they'd look good on a CV. Fine for a learning project, but call it what it is.
- Kubernetes for three users. Container orchestration solves problems you don't have yet and brings plenty you will.
- Five SaaS vendors before one user. Every account is another login and dashboard to keep track of. Add them when a real need shows up.
- Chasing a framework's major release. Starting on a brand-new major version or a beta means fewer answers online and more breaking changes. Let it settle unless that's your token.
- Building auth and billing yourself. They're full of edge cases that have nothing to do with your idea.

## FAQ

### What is the best tech stack for a side project?

The one you already know well, kept small: your strongest language and framework, one relational database, one deployable app, bought login and payments, and hosting you can deploy to with one command. There's no universal winner, because the deciding factor is your own experience and the hours you have.

### Should I learn a new language on a side project?

Yes, if it's a learning project and you're fine with it shipping slowly or not at all. If you want users, keep the language you know and spend your one new-technology token on a smaller piece, or learn the new language in a separate project.

### Is SQLite good enough for a side project?

For many, yes. The SQLite docs say it works well for most low to medium traffic websites, and they're clear about when a client/server database fits better: many concurrent writers, data on a different machine from the app, or a site that needs multiple servers. If none of those apply and your app runs on one server with a persistent disk, SQLite is a reasonable default.

## Write it down, then start building

Tonight, open a blank file and write five lines: learning or shipping project, your language and framework, your one database, how login and payments are handled, and how you deploy. Add your one innovation token if you're spending it. Then write the README commands before the first feature. That's an evening's work, and it decides whether this project survives its first long break.

Once it's live, list it on [SideHunt](https://sidehunt.io/). You'll get a public page for the project and a place alongside other builders shipping on nights and weekends.

## Links

- [Studio post (HTML)](https://sidehunt.io/studio/how-to-choose-a-tech-stack-for-a-side-project)
- [AI-friendly Markdown](https://sidehunt.io/studio/how-to-choose-a-tech-stack-for-a-side-project.md)

## Explore

- [Studio index](https://sidehunt.io/studio)
- [Blog](https://sidehunt.io/blog)
- [Launches](https://sidehunt.io/launches)

_HTML version: https://sidehunt.io/studio/how-to-choose-a-tech-stack-for-a-side-project_
