July 4, 2026
CI/CD for a solo dev: what's worth automating and what isn't

I have set up CI/CD for teams of two people and teams of forty, and the honest answer is that most of what I did for the forty-person team would be actively counterproductive for the two-person one. A staging environment nobody looks at, an approval gate with one approver, a deploy pipeline with six stages when the whole app is one Docker container. It is not that the practices are wrong, it is that they solve problems you do not have yet, and every stage you add is something you now have to maintain, debug, and explain to yourself six months later when it breaks.
So here is what I actually think is worth setting up when it is just you, and what I think is safe to skip until it isn’t.
Automate first: tests and build on every push
This is the non-negotiable part, and it is also the cheapest. If you push code and nobody, including a script, checks whether it compiles or the tests pass, you will eventually merge something broken and not notice until a user does. A minimal GitHub Actions workflow that runs on every push and pull request covers this in about fifteen lines:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- run: npm test
That is it. No matrix builds across three Node versions, no parallel test shards, no artifact caching beyond the npm cache action gives you for free. For a solo project this catches the overwhelming majority of “oops” moments: a typo that breaks the build, a test you forgot to update after changing a function signature, a dependency that does not actually install cleanly on a fresh machine. It runs in under a minute and costs nothing on GitHub’s free tier for a public repo, and not much for a private one either.
Deploy on merge to main is usually enough
The next instinct, especially if you have worked somewhere with a real release process, is to reach for a staging environment: deploy to staging first, poke at it, then promote to production. For a solo dev this is often just friction. You are the only person testing changes, you already tested them locally before pushing, and you are the one who will notice immediately if production breaks, because you are the one using the thing. A second environment means a second set of environment variables to keep in sync, a second database or a shared one with the risks that come with sharing, and a manual promotion step that exists mostly to make you feel safer without actually catching more bugs than your local testing already did.
What I do instead: merge to main triggers a deploy, full stop. The zero-downtime deploy setup I wrote about for this site is a good example, a single script that builds and rsyncs into a fresh release directory, then flips a symlink. Wiring that into a workflow is a small addition to the same CI job:
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- name: Deploy
run: ./deploy/deploy.sh
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
The needs: test line means it only runs after the test job passes, so you get the safety net without a separate approval click. If something does slip through, you fix it and push again, and the fix ships in the same amount of time the bug did. That fast feedback loop is worth more, for a solo project, than a staging gate that mostly just delays the inevitable by twenty minutes.
When it stops being overkill
None of this is a permanent stance, and the trigger for adding more is usually one of a few concrete things showing up, not a vague sense that you “should have better process.”
More than one contributor changes the math immediately. Once someone else can push code you have not personally reviewed, a required review step and a real staging environment stop being ceremony and start being the thing that catches the bug you would not have caught yourself. Compliance or contractual requirements are another clear line, if a customer contract or a regulation requires an audit trail of who approved what before it hit production, that is not optional anymore regardless of team size. And traffic or blast radius matters too, once a bad deploy means real money lost per minute rather than “I will notice and fix it in the next five minutes,” the cost of a canary step or a slower rollout starts to be worth paying.
Until one of those is true, though, I would rather have a pipeline I actually understand end to end and can debug in five minutes than one that looks impressive in a diagram but exists mostly because that is what the last company I worked at did. The test-and-build-on-push job earns its keep from day one. Everything past that should have a reason, not just a precedent.