DevOps

How to Build a CI/CD Pipeline in 2025

Every startup eventually reaches the point where deploying code is painful. Someone SSHs into a server, runs a git pull, restarts the application, and hopes nothing breaks. Or a developer merges to main and then manually triggers a build in some dashboard. It works until it does not, and it does not scale.

A proper CI/CD pipeline eliminates deployment anxiety. Code gets tested automatically, built consistently, and deployed predictably. Here is how to build one that works for a small team in 2025.

Choosing a CI/CD platform

The three most popular options for startups are GitHub Actions, GitLab CI, and Buildkite. Here is how to decide:

GitHub Actions is the default choice if your code lives on GitHub. It is free for public repos and gives you 2,000 minutes per month on the free tier for private repos. The marketplace has thousands of pre-built actions. The YAML syntax is straightforward. For most startups, this is the right answer.

GitLab CI is tightly integrated with GitLab and offers a more complete DevOps platform. If you are already using GitLab for source control, issue tracking, and container registry, GitLab CI is the natural choice. It also offers 400 minutes per month on the free tier.

Buildkite is the choice for teams that need more control. You run your own agents, which means faster builds, more customization, and no shared infrastructure. The downside is more operational overhead. Pick Buildkite if you have complex build pipelines or strict security requirements about where your code runs.

The pipeline structure

A good CI/CD pipeline for a SaaS startup has four stages:

1. Lint and format. Run your linter and formatter on every pull request. This catches style issues before code review and keeps the codebase consistent. Use ESLint for JavaScript, Ruff for Python, or whatever the standard tool is for your language. This step should take under 30 seconds.

2. Test. Run your unit tests and integration tests on every push. If you do not have tests, start writing them now. Even 20% code coverage on critical paths is better than 0%. Use test parallelization to keep this step under 5 minutes. If your tests take longer than 10 minutes, developers will stop waiting for them.

3. Build. Build your Docker image, compile your binary, or bundle your frontend assets. Tag the build with the git SHA so you can always trace a deployment back to a specific commit. Push the artifact to a container registry or artifact store.

4. Deploy. Deploy to your staging environment automatically on every merge to main. Deploy to production with a manual approval gate or automatically if you have confidence in your test suite. Use blue-green or rolling deployments to avoid downtime.

Environment strategy

You need at least two environments: staging and production. Staging should be as close to production as possible. Same infrastructure, same configuration, same data shape (with anonymized data). The entire point of staging is to catch issues before they reach production.

Some teams add a "dev" environment for feature branches. This is useful if you have long-running branches or need to demo features to stakeholders. Tools like Vercel and Railway make ephemeral preview environments easy for web applications.

Secrets management

Never put secrets in your CI/CD configuration files. Use your platform's built-in secrets management: GitHub Secrets, GitLab CI Variables, or a dedicated secrets manager like AWS Secrets Manager. Rotate secrets regularly and audit who has access to them.

Monitoring your pipeline

Track three metrics for your pipeline: build time, success rate, and mean time to recovery. If your average build time creeps above 10 minutes, investigate. If your success rate drops below 95%, you have flaky tests that need fixing. If your mean time to recovery after a failed deployment is more than 30 minutes, your rollback process needs work.

A practical example

Here is what a basic GitHub Actions workflow looks like for a Node.js application deployed to AWS ECS:

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 }
      - run: npm ci
      - run: npm run lint
      - run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build and push Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .
          docker push $ECR_REGISTRY/myapp:${{ github.sha }}
      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster production \
            --service myapp \
            --force-new-deployment

This is a starting point. As your team grows, you will add caching, parallelization, security scanning, and more sophisticated deployment strategies. But getting a basic pipeline running is the first step, and it should take less than a day.

If you need help designing a CI/CD pipeline for your specific stack, get in touch. We have built pipelines for everything from monolithic Rails apps to microservices on Kubernetes.

Not ready for a call? Same.

Get the playbook, not a sales pitch

If this was useful, Jacob sends a few short, practical notes on cutting cloud spend and scaling infra the right way. No fluff, unsubscribe in one click. Just reply if you want to talk; it reaches him directly.

From Jacob Masse, founder of traztech. No spam, unsubscribe in one click.

Need help with any of this?

We help startups build secure, scalable infrastructure. Book a free strategy call and let\'s talk about your stack.

Book a free consultation