← Journal
18 Jul 2026 · 10 min read

Why your MVP needs CI/CD before it needs users

Founders tell me they will set up CI/CD after they get traction. They never do. The first time a bad deploy takes the MVP offline on launch day, the investors watching the demo stop returning emails. I have watched this happen three times this year. Here is why CI/CD is not a scaling concern, it is a launch prerequisite, and what the minimum pipeline for a pre-seed MVP actually looks like.

A founder in Lisbon pitched me last spring. He had built an MVP over six weeks with a contractor. It was a B2B scheduling tool, unglamorous but real. He had a demo lined up with three pre-seed funds the following week. He wanted me to “harden the infrastructure” before the demos. I asked him how he deployed. He said he SSHed into the server, pulled the latest code, ran npm run build, and restarted the Node process. He had done this fourteen times. The last time, he forgot to run the migration and the app crashed for forty minutes while he figured out why the new column was missing. He fixed it by running the migration manually and restarting again. He told me this like it was a normal story. It is a normal story. It is also the story of why his demos went badly.

The first demo went fine for eleven minutes. Then the founder tried to show a new feature he had pushed the night before. The app loaded, the feature worked, and three minutes later the page returned a 502. The process had crashed. He had to apologize, SSH in from his laptop in front of the investors, and restart it. The investors watched him type pm2 restart app into a terminal. The meeting ended five minutes after that. None of the three funds followed up.

He called me the next day. He said the product was not the problem, the infrastructure was. He was half right. The product was fine. The infrastructure was not fine, and it was not fine in a way that made the product look broken. That is what infrastructure does when it is neglected. It does not fail quietly. It fails in front of the people you most want to impress.

The argument I keep having with founders

The argument goes like this. The founder says: “We are pre-seed. We have twelve users. We do not need CI/CD. We need features and distribution. We will set up CI/CD when we have traction.” I have had this conversation enough times that I can predict the next sentence. It is always some version of “CI/CD is a scaling problem, and we are not scaling yet.”

This is wrong in a specific way that matters. CI/CD is not a scaling technology. It is a feedback technology. It shortens the distance between “I wrote the code” and “the code is running in a way I can verify.” That distance is what kills pre-seed startups, not traffic. Pre-seed startups do not die from load. They die from the gap between what the founder thinks is running and what is actually running. They die from the deploy that took the app offline during a demo. They die from the migration that ran on the founder’s laptop but not on the server. They die from the contractor who pushed a change on Friday and flew home, and on Monday the app is broken and nobody knows what changed.

CI/CD closes that gap. It does not close it by adding complexity. It closes it by making the deploy automatic, repeatable, and verifiable. The same pipeline that ships code to a million users ships code to twelve users. The difference is that at twelve users, you can get away with manual deploys until you cannot, and the moment you cannot is usually the moment that matters most.

What a pre-seed MVP actually needs

I am going to describe the pipeline I install for pre-seed startups. Not the pipeline they will have at Series A. The one that survives a demo, a launch, and the first month of real users, without anyone SSHing into anything.

The stack is GitHub Actions, a container registry, and a single VPS running the app in a container. No Kubernetes. No ArgoCD. No Argo anything. The target is a pre-seed team with one to three engineers, a codebase that fits in one repository, and a deployment target that is one server or one small managed container service. The total monthly cost is under ten euros. The total setup time is one afternoon. I have done it eleven times this year.

The pipeline

The pipeline runs on every push to main. It does four things, in order, and it fails the deploy if any of them fail.

# .github/workflows/deploy.yaml
name: deploy
on:
  push:
    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 test -- --coverage
      - run: npm run lint

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to VPS
        run: |
          ssh -o StrictHostKeyChecking=no ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
            "docker pull ghcr.io/${{ github.repository }}:${{ github.sha }} && \
             docker stop app || true && \
             docker rm app || true && \
             docker run -d --name app --restart unless-stopped \
               -p 80:3000 --env-file /opt/app/.env \
               ghcr.io/${{ github.repository }}:${{ github.sha }}"

That is the whole pipeline. Test, build, deploy. No human touches the server. The SSH key is a deploy key scoped to a single user that can only run docker pull and docker run. The .env file lives on the server, not in Git. The image is tagged with the commit SHA, so every deploy is traceable to a specific commit. If something breaks, you know exactly which commit broke it, because the commit SHA is the image tag.

This is not a sophisticated pipeline. It is the minimum pipeline that eliminates manual deploys. It takes an afternoon to set up. I have watched founders spend longer than that debugging a single broken manual deploy.

What this pipeline gives you that manual deploys do not

The pipeline gives you four things that a manual deploy process does not, and each one matters at pre-seed.

First, it gives you a deploy that is the same every time. Manual deploys are not the same every time. The founder runs the build, runs the migration, restarts the process. Except the time he forgot the migration. Except the time he pulled from the wrong branch. Except the time the contractor pushed a change and did not tell anyone, and the founder’s next deploy included a half-finished feature. The pipeline removes this variance. The same steps run in the same order every time. The variance is gone because the steps are not chosen by a human under time pressure.

Second, it gives you a deploy that is verifiable. The test job runs before the deploy job. If the tests fail, the deploy does not happen. This is the part founders underestimate. They tell me their tests are bad, so CI would not catch anything. That is sometimes true on day one. It is not true on day thirty, because once CI exists, engineers write tests for the things CI would catch, and within a month the test suite is good enough to catch the regressions that would have gone out in a manual deploy. The pipeline changes the behavior of the team, not just the behavior of the deploy.

Third, it gives you a rollback. With the pipeline above, rollback is docker stop app && docker run ... <previous-sha>. The previous image is still in the registry. You do not have to rebuild anything. You do not have to remember what the code looked like before. You pull the previous image and start it. When the founder is debugging a broken deploy at 11pm before a demo, the ability to get back to the last working version in under a minute is the difference between a demo that works and a demo that does not.

Fourth, it gives you a log of what deployed and when. git log on main tells you what was supposed to deploy. The GitHub Actions run history tells you what did deploy, when, and whether it succeeded. When an investor asks “what is in production right now,” the answer is a commit SHA, and you can show them the run that put it there. When the contractor says “I did not change anything,” the run history shows that he pushed a commit at 2am that deployed a broken build. The log is the evidence. Manual deploys do not produce evidence.

The launch day I keep replaying

A team in Tallinn launched their MVP on a Tuesday in March. They had no CI/CD. The launch got picked up by a niche newsletter and they had two hundred signups in the first hour. The founder, excited, pushed a fix for a signup bug he had noticed. He SSHed in, pulled the code, ran the build, and restarted. The build failed because of a typo he had not caught. The app was down. He fixed the typo on his laptop, pushed again, pulled again, built again, restarted. The app came back up. He had been down for eighteen minutes, in the middle of his launch hour, with two hundred people trying to sign up. The newsletter had linked to the signup page. The signup page returned a 502 for eighteen minutes.

He did not lose all two hundred. He lost most of them. The ones who came back were the ones who really wanted the product. The ones who did not come back were the ones he would have converted with a working signup flow. He will never know how many that was. He does know that his launch hour, the one moment of concentrated attention he had spent six weeks building toward, was dominated by an SSH session and a build error.

I set up the pipeline above for him the next week. It took four hours. He has not SSHed into the server since. His next launch, a feature rollout two months later, went through the pipeline. A test failed, the deploy stopped, he fixed the test, the deploy went out. The whole cycle took twenty minutes and the app was never down. He told me it was the first time he had shipped a feature without his heart rate spiking. That is what CI/CD gives a pre-seed founder. Not scale. Certainty.

The cost framing that founders respond to

Founders respond to cost framing, so here is the cost framing. The pipeline above costs nothing in infrastructure. GitHub Actions is free for private repos up to two thousand minutes per month. A pre-seed MVP does not use two thousand CI minutes in a month. The container registry is free up to five hundred megabytes. The VPS is the server you are already paying for. The SSH deploy key costs nothing. The total additional monthly cost of adding CI/CD to a pre-seed MVP is zero.

The cost of not adding it is the thing founders do not calculate. It is the demo that goes badly. It is the launch hour that goes down. It is the investor who watches you type pm2 restart app and decides you are not ready. It is the contractor who pushes a broken build on Friday and the Monday that follows. These are not hypothetical. They are the standard output of a manual deploy process, and they are the things that kill pre-seed startups before the market ever gets a chance to.

I have never met a founder who regretted setting up CI/CD too early. I have met several who regretted setting it up too late, and the regret always arrived in the same form: a story about a moment that mattered, and an SSH session that ruined it.

What I tell pre-seed founders

When a pre-seed founder asks me what to prioritize, I tell them this. Before you write the next feature, before you run the next user interview, before you book the next demo, make merging to main result in a working deploy without you touching the server. That is the one piece of infrastructure that pays for itself the first time it saves a demo, and it will save a demo. The features can wait an afternoon. The pipeline cannot.

The work is small. A GitHub Actions workflow, a container build, an SSH deploy, a rollback procedure you test once. That is the whole surface area. If you are a pre-seed team reading this and recognizing the gap, Basecamp exists for exactly this reason: digitalaultis.com/basecamp

Your MVP does not need users before it needs CI/CD. It needs CI/CD so that when the users arrive, you are not in a terminal window.