Codenovix
Back to blog
Backend & APIs

Building a Real CI Pipeline for a NestJS Backend Using Docker & GitHub Actions

A lint-test-build-push pipeline for a NestJS backend caught a Joi schema bug that never showed up locally, because a .env file was quietly masking it. Docker and CI, run together, close that gap.

Vanshit PatelVanshit Patel
Aug 8, 2026 2 min read
Building a Real CI Pipeline for a NestJS Backend Using Docker & GitHub Actions

CI/CD isn't really about automation for its own sake — it's about trust. Without a pipeline enforcing checks on every change, bugs slip through on the strength of "it worked on my machine," Docker images go unverified until they fail in production, and the whole team is implicitly relying on everyone's local setup staying in sync. A real pipeline makes every change prove itself before it gets anywhere near main.

The Setup

The concrete example here is a NestJS backend backed by PostgreSQL, containerized with Docker, wired up through GitHub Actions. The goal: every push runs linting and tests, verifies the Docker image actually builds correctly, and — once it lands on main — pushes a fresh image to Docker Hub automatically.

Three Jobs, Each a Gate

The pipeline breaks into three sequential stages, and each one is a hard gate — a failure anywhere stops the rest from running:

  1. Lint and test — install dependencies, run ESLint, run the test suite against a real PostgreSQL service spun up for the job, not a mock.
  2. Docker build verification — actually build the image from the Dockerfile and confirm it comes out production-safe, not just "builds locally."
  3. Image publication — authenticate to Docker Hub and push the latest tag, but only after the first two stages pass clean.

Why Docker and CI Need Each Other

Neither piece is complete without the other. Docker without CI means your deployment artifact is never actually verified before it ships — you're trusting that the image works because it was built, not because it was tested. CI without Docker means your tests run in an environment that doesn't match production, so passing tests don't guarantee anything about what actually gets deployed. Run them together and you get a pipeline that validates the exact artifact that ships, in an environment that matches where it's going.

The Bug That Proved the Point

The clearest argument for building this pipeline in the first place: a Joi schema had a .default() call missing its default value. Locally, this never surfaced — a .env file was quietly filling in the gap. Inside the container, with no local .env to paper over it, the app crashed on startup. That's exactly the class of bug environment-parity testing exists to catch, and exactly the kind that "it works on my machine" testing will never find.

Ad space - connect Google AdSense to activate

Found this useful?

Share it with the dev community or cross-post with a canonical link back here.

Cross-posting to dev.to or Hashnode? Use this as your canonical URL: https://www.codenovix.com/blog/nestjs-ci-pipeline-docker-github-actions

Related articles