This CI/CD pipeline setup guide shows you how to automate the path from a code commit to production—building, testing, and deploying every change without manual steps. You'll learn what the often-confused terms actually mean, the stages every pipeline shares, a worked example you can adapt, and the deployment strategies that let you ship frequently without breaking things. The payoff is faster, safer releases and far less time spent on manual deploys.
What CI/CD actually means
Three terms hide behind the abbreviation, and people conflate them constantly. Getting them straight clarifies what you're actually building.
Continuous Integration (CI) is the practice of merging code into a shared branch frequently—ideally many times a day—and automatically building and testing every change. Its goal is to catch integration problems within minutes of writing them, while the context is fresh and the change is small.
Continuous Delivery (CD) extends CI so that every change which passes the pipeline is automatically prepared for release and deployable at the push of a button. The final step to production is a human decision.
Continuous Deployment goes one step further: every change that passes all automated checks deploys to production with no manual approval at all. The distinction is just that last gate—delivery keeps a human in the loop, deployment removes it.
The shared purpose is to make releases small, frequent, and low-risk. A ten-line change that ships on its own is trivial to review, test, and—if something goes wrong—roll back. A three-month batch of changes released at once is a high-stakes event. The well-known DORA (DevOps Research and Assessment) framework measures exactly this through four metrics worth tracking from day one: deployment frequency, lead time for changes (commit to production), change failure rate, and time to restore service. Elite teams deploy on demand, move a change to production in under a day, and recover from incidents in under an hour—outcomes a good pipeline makes possible.
The anatomy of a CI/CD pipeline
A pipeline is an ordered series of stages, each a quality gate. If a stage fails, the pipeline stops and nothing downstream runs. The typical stages, in order:
- Trigger. An event starts the pipeline—usually a push or a pull request, which is why your pipeline is only as disciplined as the Git workflow conventions that feed it. Small, frequent commits produce fast, meaningful pipeline runs.
- Build. Compile the code and resolve dependencies, producing a build artifact—a compiled binary, a bundled app, or most commonly a container image. Build once and promote that same artifact through every later stage, so what you tested is exactly what you ship.
- Test. Run automated checks in fast-to-slow order: linting and unit tests first (seconds), then integration tests, then slower end-to-end tests. Failing fast on the cheap checks saves time and compute.
- Deploy to staging. Push the artifact to a production-like environment for final verification—smoke tests, integration against real services, sometimes manual QA.
- Deploy to production. Release to users, using one of the deployment strategies below. In continuous delivery this waits for approval; in continuous deployment it's automatic.
The principle tying the stages together is fail fast: order checks so the quickest, most likely-to-fail ones run first. A developer shouldn't wait ten minutes for end-to-end tests to discover a lint error that a five-second check would have caught.
Building your first pipeline: a worked example
Pipelines are defined as code—YAML files committed to your repo—so they're versioned and reviewable alongside the application. Here's a complete build-test-deploy pipeline. This example uses GitHub Actions syntax; our dedicated guide on how to use GitHub Actions covers the platform in depth, and the same shape applies to GitLab CI, CircleCI, or Jenkins.
name: Pipeline
on:
push:
branches: [main]
pull_request:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- run: npm ci
- run: npm run lint # fast checks first
- run: npm test # unit tests
- run: npm run build # produce the artifact
deploy-production:
needs: build-and-test # only runs if tests passed
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production # gate with required approval
steps:
- uses: actions/checkout@v6
- run: ./scripts/migrate.sh # run database migrations first
- run: ./scripts/deploy.sh
Two things make this safe rather than reckless. The needs: build-and-test line ensures deployment only happens if every test passed. The environment: production key lets you require a human approval and other protections before the deploy job runs. Note the migration step runs before the deploy—schema changes are the most dangerous part of any release, so they belong in the pipeline as a deliberate, reversible step rather than a manual afterthought. Choosing the right tool for that matters; our comparison of database migration tools covers the tradeoffs in ordering, rollback, and zero-downtime changes.
Deployment strategies and safe releases
How you push code to production determines how much a bad release hurts. Four strategies cover most cases.
| Strategy | How it works | Rollback | Best for |
|---|---|---|---|
| Recreate | Stop the old version, start the new | Redeploy old (downtime) | Simple apps that tolerate brief downtime |
| Rolling | Replace instances gradually | Roll back instance by instance | Default for most clustered apps |
| Blue-green | Two identical environments; switch traffic | Instant—switch back | Fast rollback, needs double infrastructure |
| Canary | Release to a small % of users, then expand | Stop and revert the small slice | Highest-risk changes, needs traffic routing |
Rolling updates are the sensible default—they avoid downtime without extra infrastructure. Blue-green keeps a second identical environment so you can flip traffic instantly and flip back just as fast if something breaks; the cost is running two environments. Canary releases expose a new version to a small fraction of users first, monitor error rates and latency, then widen the rollout—the safest option for risky changes, but it requires traffic-splitting and good metrics.
The most powerful safety technique sits above all of these: decouple deploying from releasing. By wrapping new functionality in feature flags that you toggle independently of deploys, you can ship code to production turned off, enable it for a few internal users, then roll it out gradually—and kill it instantly without a redeploy if it misbehaves. Deploying the code and releasing the feature become two separate, low-risk events.
Practices that keep a pipeline fast and trustworthy
A pipeline only helps if developers trust it and it returns answers quickly. A few practices separate pipelines people rely on from ones they route around.
- Keep CI feedback under about 10 minutes. Past that, developers context-switch and stop paying attention. Parallelize independent jobs, cache dependencies, and run only affected tests where you can.
- Make the pipeline the only path to production. If people can deploy manually around it, the guarantees evaporate. Enforce it with branch protection and required status checks.
- Treat pipeline config as code. Version it, review it, and reuse shared steps so the pipeline itself is maintainable.
- Build the artifact once. Rebuilding at each stage risks shipping something subtly different from what you tested. Build, then promote the identical artifact through staging to production.
- Make deploys and migrations idempotent and reversible. Running the same deploy twice should be safe, and every change should have a rollback path.
- Never hardcode secrets. Use your platform's encrypted secret store and scope credentials to least privilege.
- Add observability. Emit logs, metrics, and alerts so you know immediately when a deploy degrades production, and track the DORA metrics to see whether your pipeline is actually improving.
Common mistakes to avoid
Slow pipelines. A 40-minute CI run kills the fast-feedback loop that makes CI valuable. Profile it, parallelize, and cache aggressively.
Flaky tests. Tests that fail intermittently train developers to ignore red builds, defeating the entire purpose. Quarantine and fix flaky tests immediately—a pipeline you don't trust is worse than none.
Manual steps in the middle. Any human step in the automated path is a place releases stall and errors creep in. Automate the whole path; keep human judgment only at explicit approval gates.
Rebuilding artifacts per stage. Building separately for test and production means you ship something you never tested. Build once, promote the same artifact.
Ignoring database migrations. Treating schema changes as a manual side task causes some of the worst production incidents. Run them in the pipeline, in order, with a rollback plan.
No rollback plan. Hoping nothing breaks isn't a strategy. Decide your rollback mechanism—blue-green switch, feature-flag kill switch, redeploy—before you need it.
Frequently asked questions
What's the difference between continuous delivery and continuous deployment? Both automate the pipeline up to production. Continuous delivery stops at a manual approval gate—a human decides when to release. Continuous deployment removes that gate, so every change passing all automated checks goes live automatically.
What tools do I need to set up a CI/CD pipeline? A source host and a CI/CD platform are the core. GitHub Actions, GitLab CI/CD, CircleCI, and Jenkins are common choices, with tools like Argo CD or Flux for GitOps-style Kubernetes deployments. Most teams already on GitHub start with GitHub Actions since it's built in.
How long should a CI/CD pipeline take? Aim to keep the CI feedback portion (build and tests) under roughly 10 minutes so developers stay in flow. Full deployment pipelines can take longer, but the part a developer waits on after pushing should be fast.
What are the stages of a CI/CD pipeline? Typically trigger, build, test, deploy to staging, and deploy to production, with each stage acting as a gate that stops the pipeline on failure. Tests usually run fast-to-slow so cheap checks fail first.
Do database migrations belong in the pipeline? Yes. Running migrations as an explicit, ordered, reversible pipeline step—before the application deploy—is far safer than doing them manually. Pairing migrations with feature flags lets you change schema and code independently.
The takeaway
This CI/CD pipeline setup guide comes down to one principle: automate the entire path from commit to production so releases become small, frequent, and boring. Your next step is to create a single pipeline file that builds, tests, and deploys on every push to main, add a staging gate, and make it the only way code reaches production. Start with build-and-test today, layer on automated deploys once you trust it, and add a deployment strategy and feature flags as your release volume grows.