How to Build a CI/CD Pipeline Portfolio Project
A CI/CD pipeline portfolio project is expected at nearly every junior cloud and DevOps interview. But “I have a GitHub Actions workflow” is a long way from demonstrating that you understand continuous integration and delivery as a discipline. This guide covers how to build a pipeline project that goes beyond pushing code and calling it CI/CD.
What CI/CD actually means before you build it
Continuous integration means every change is automatically validated before it is merged — not just after. Continuous delivery means deployable software is produced reliably after every successful integration. These are disciplines, not just tools.
A pipeline that only runs when code is merged to main is not continuous integration. A pipeline that deploys directly to production with no validation stage is not continuous delivery. These distinctions matter in interviews.
What a minimal CI/CD pipeline must do:
- Trigger on pull requests — not just on merges to main
- Validate code quality before running tests (lint, format check)
- Run automated tests
- Build and package the application
- Deploy to a non-production environment on successful build
- Gate production deployment on explicit approval or automated verification
What to build
Use a small containerised application as the subject of the pipeline. A Python Flask API, a Node.js Express server, or a Go HTTP service all work well. The application code is secondary — the pipeline is the portfolio piece.
The pipeline (using GitHub Actions) should have these stages:
Stage 1: Validate (runs on every pull request)
- Lint the application code (flake8 for Python, eslint for Node, golangci-lint for Go)
- Run unit tests and output a coverage report
- Scan the Dockerfile for security issues (Hadolint for Dockerfile linting, Trivy for image scanning)
- Run
terraform validateandterraform fmt --checkif infrastructure code is included
Stage 2: Build (runs on merge to main)
- Build the Docker image with a deterministic tag (git SHA, not “latest”)
- Push the image to a container registry (ECR, GCR, or Docker Hub)
Stage 3: Deploy to staging (runs after build)
- Deploy the new image version to a staging environment
- Run a smoke test or health check against the staging deployment
Stage 4: Deploy to production (manual approval or automatic after staging passes)
- Deploy the same image (same tag, same artifact) to production
- Run post-deployment verification
Authentication: do not use static credentials
The single biggest security mistake in CI/CD portfolios is storing cloud provider credentials as GitHub secrets and passing them as environment variables. This works, but it is not current best practice and hiring managers notice it.
The current best practice is OIDC (OpenID Connect) federation:
- GitHub can present a short-lived identity token to AWS, GCP, or Azure
- The cloud provider validates that token and issues temporary credentials
- No long-lived access keys are stored anywhere — not in GitHub, not in the repository
Setting up OIDC federation between GitHub Actions and AWS or GCP requires configuring a trust relationship — a few resources in Terraform. Document this setup clearly in your README. It is a concrete demonstration of security-conscious thinking that many beginners skip.
Image tagging and artifact traceability
Tag Docker images with the Git commit SHA. Never use latest as the production tag. The reason is traceability: if something goes wrong in production, you need to know exactly which commit is running. With latest, you cannot tell which code is deployed without checking the registry. With a SHA tag, you can git show {sha} and see exactly what changed.
In your README, explain why you chose commit SHA tagging. This is a real production pattern that demonstrates deployment thinking.
Adding an infrastructure pipeline
If you want to make this project significantly more impressive, add a separate pipeline for infrastructure changes alongside the application pipeline. The infrastructure pipeline should:
- Run
terraform planon pull requests and post the plan as a pull request comment - Require manual approval before running
terraform apply - Run a scheduled job (weekly or daily) to detect drift between the Terraform state and the real infrastructure
Drift detection — running terraform plan on a schedule with no pending changes, and alerting if plan shows differences — is a real production pattern that almost no portfolio projects include. It signals that you understand infrastructure state management, not just deployment.
Making the pipeline fail
A pipeline that never fails is not a real CI pipeline — it is a script. Demonstrate that your pipeline actually enforces quality by including things that will fail if code quality drops:
- A failing test should block the merge — show a screenshot or describe what happens
- A lint error should block the merge
- An image with a critical CVE should block the build (configure Trivy with a severity threshold)
In your README, describe what conditions cause the pipeline to fail and what a developer is expected to do in each case. This shows you have thought about the pipeline as a team tool, not just a personal script.
What to document in the README
The README should cover:
- Pipeline diagram or description — each stage, what triggers it, and what it does
- Authentication setup — how the pipeline authenticates to the cloud provider, specifically that it uses OIDC and not stored credentials
- What fails the pipeline — the conditions that block a merge or a deployment
- What you would add in production — for example: integration tests, performance tests, automated rollback on failed health checks, Slack notifications
Using Google Cloud Build instead of GitHub Actions
If you are focused on GCP, Cloud Build is the native CI/CD service. The concepts are the same — stages, triggers, artifact management — but the configuration is a cloudbuild.yaml file rather than a GitHub Actions workflow. Cloud Build integrates natively with Cloud Run, GKE, and Artifact Registry.
Cloud Build is less widely recognised outside GCP shops than GitHub Actions, so if the role you are targeting is multi-cloud or AWS-focused, GitHub Actions is the safer portfolio choice. For GCP-specialist roles, Cloud Build demonstrates native platform knowledge.
If you have time, building the same pipeline in both GitHub Actions and Cloud Build and documenting the comparison is genuinely impressive for GCP-focused roles.
Summary
- A real CI pipeline triggers on pull requests — not just on merges to main
- Include lint, test, build, staging deploy, and production deploy stages — each with a clear trigger
- Use OIDC federation for cloud provider authentication instead of static credentials stored in secrets
- Tag Docker images with the Git commit SHA — never use “latest” for production
- Make the pipeline actually fail when code quality drops — a pipeline that never fails is just a script
- Adding drift detection for infrastructure changes elevates this significantly above most portfolio CI/CD builds