CI/CD interview questions
CI/CD interview questions from fresher to senior — pipeline fundamentals, practical pipeline design, deployment strategies, and secure CI/CD, each answer tagged by experience level.
Fresher 0–1y · Junior 1–3y · Mid 3–6y · Senior 6+y — every answer is tagged so you can prep for your level.
What is CI and what is CD?Fresher
Continuous Integration means every change is merged and automatically built and tested frequently. Continuous Delivery keeps the build always deployable with a manual release gate; Continuous Deployment removes that gate and ships automatically.
CI stops at a tested artifact; CD (delivery) adds a manual gate, CD (deployment) ships automatically.
What are the typical stages of a pipeline?Fresher
Build (compile/package), test (unit, integration, lint, security scans), publish an artifact, and deploy/promote through environments. Each stage should fail fast and give clear feedback.
stages: [build, test, deploy] build: stage: build script: [make build] test: stage: test script: [make test]
What is an artifact and why store it?Junior
The immutable output of the build (jar, container image, binary). You build it once, store it in a registry with a version, and promote that same artifact across environments — never rebuild per environment.
docker build -t registry/app:$CI_COMMIT_SHA . docker push registry/app:$CI_COMMIT_SHA # deploy that exact tag to dev, then stg, then prod
What is a runner or agent?Junior
The worker that executes pipeline jobs. It can be shared or dedicated, ephemeral or long-lived; ephemeral runners give a clean environment per job and reduce the blast radius of a compromised build.
Ephemeral runners (a fresh container or VM per job) are strongly preferred: no state leaks between jobs and a compromised build cannot persist. Long-lived runners are faster but accumulate secrets, caches, and drift — a bigger blast radius.
What triggers a pipeline?Fresher
Common triggers are a push or merge request, a tag (for releases), a schedule (nightly), a manual or API call, or an upstream pipeline. Scope each job to the right trigger with rules.
deploy:
script: [./deploy.sh]
rules:
- if: '$CI_COMMIT_BRANCH == "main"'How do you speed up a slow pipeline?Mid
Cache dependencies, run independent jobs in parallel (and test matrices), split long test suites, use incremental/affected builds in monorepos, and keep images small. Measure stage timings before optimizing.
Measure stage timings first, then attack the biggest: cache dependencies, run independent jobs in parallel and split slow test suites, and in a monorepo build only what changed. Warm caches and small images matter more than clever tricks.
test:
parallel: 4
cache:
key: $CI_COMMIT_REF_SLUG
paths: [.venv/, node_modules/]
script: [make test]How should secrets be handled in CI?Mid
Store them in the platform’s secret store or a vault, inject as masked env vars at run time, never echo them, scope them to the jobs that need them, and prefer short-lived OIDC tokens over static credentials.
Keep secrets in the platform store or a vault, inject as masked variables at run time, scope them to the jobs and branches that need them, and never echo them. Prefer short-lived OIDC tokens so there is nothing static to leak or rotate.
deploy:
script:
- vault kv get -field=token secret/deploy # fetch at runtime
# never: echo "$DEPLOY_TOKEN"
environment: productionCaching vs artifacts — what is the difference?Mid
Cache speeds up future runs by reusing inputs (dependencies) and is best-effort. Artifacts are outputs of a job passed to later stages (a built binary, a report) and are guaranteed present. Do not use cache to hand build output to deploy.
build:
script: [make build]
artifacts:
paths: [dist/]
expire_in: 1 weekHow do you promote a build across environments?Mid
Promote the same versioned artifact with environment-specific config injected at deploy time. Gate production behind approvals and automated checks so dev/staging/prod run identical bits, differing only in configuration.
# one image tag, config injected at deploy helm upgrade app ./chart -f values-prod.yaml --set image.tag=$CI_COMMIT_SHA
How do you design pipelines for a monorepo?Senior
Detect changed paths and build/test only affected components (Nx, Bazel, Turborepo, or path filters), share caches, and fan out per-service pipelines — otherwise every commit rebuilds everything and CI becomes the bottleneck.
Detect which paths changed and run only the affected components (rules:changes, or a build graph like Nx/Bazel/Turborepo), share caches, and fan out per-service child pipelines. Otherwise every commit rebuilds the whole repo and CI becomes the bottleneck.
api-test:
script: [make -C services/api test]
rules:
- changes: [services/api/**/*]How do you make a job reusable?Mid
Factor shared config into templates — GitLab extends/include, GitHub reusable workflows or composite actions — so pipelines stay DRY and a fix lands everywhere at once. Hidden keys act as base templates.
.base-test: image: python:3.12 before_script: [pip install -r req.txt] unit: extends: .base-test script: [pytest -q]
Blue-green vs canary vs rolling?Mid
Blue-green flips all traffic between two identical environments (instant rollback); canary shifts a small percentage first and watches metrics before ramping; rolling replaces instances gradually. Choice trades speed, cost, and risk control.
Blue-green keeps two full environments and flips the router — instant rollback, double the infra. Canary shifts a small slice and watches metrics before ramping — safest, needs good telemetry. Rolling replaces instances in place — cheapest, slower to roll back.
How do you roll back a bad deploy?Mid
Re-deploy the previous known-good artifact (or flip blue-green/canary weight back). Keep deploys immutable and versioned so rollback is a redeploy, and decouple risky DB migrations so they are backward compatible.
kubectl rollout undo deploy/web # previous ReplicaSet helm rollback app 42 # previous release # blue-green: point the router back to blue
How do feature flags change deployment?Mid
They separate deploy from release — you ship code dark and enable it per cohort at runtime, enabling gradual rollout, instant kill-switch, and testing in production without a redeploy. The cost is flag lifecycle management.
Flags decouple deploy from release: ship code dark, then enable per cohort at runtime — gradual rollout, an instant kill switch, and testing in prod without a redeploy. The cost is lifecycle: stale flags become tech debt and must be retired.
How do you gate production deploys?Mid
Use protected environments with required approvals, required status checks, and a manual promotion step. The pipeline can reach prod only after review and green checks, and only from the main branch.
deploy-prod:
stage: deploy
when: manual # requires a human click
environment: production
rules:
- if: '$CI_COMMIT_BRANCH == "main"'How do you handle database migrations in a pipeline?Senior
Make them backward compatible and run them as a separate, ordered step (expand/contract): add columns before code uses them, deploy code, then remove old columns in a later release — never a destructive migration coupled to a rolling deploy.
Use expand/contract so schema and code are always compatible: deploy an additive migration (new nullable column), then the code that uses it, backfill, and only a later release drops the old column. A destructive migration coupled to a rolling deploy breaks the old pods still running.
What is pipeline injection and how do you prevent it?Senior
Untrusted input (PR titles, branch names, fork code) executed by the pipeline can run attacker commands. Mitigate by not running privileged jobs on untrusted PRs, quoting/validating inputs, and isolating fork builds without secrets.
Untrusted input (PR title, branch name, fork code) interpolated into a shell runs with your pipeline privileges. Never run privileged jobs on untrusted PRs, pass input via env vars instead of inlining it into the script, and quote and validate everything.
# UNSAFE: an expression with attacker-controlled text
# inlined straight into a run: shell can execute commands
# SAFE: bind it to an env var, then use "$TITLE" quoted
- run: echo "title is $TITLE"
env:
TITLE: the-untrusted-valueWhy use OIDC instead of stored cloud keys?Senior
The pipeline exchanges a short-lived, workload-bound identity token for temporary cloud credentials, so there are no long-lived secrets to leak or rotate and access is scoped per repo/branch. It is the modern default for CI-to-cloud auth.
The pipeline presents a short-lived, workload-bound identity token to the cloud, which its trust policy exchanges for temporary credentials scoped to that repo/branch. Nothing static is stored, nothing to rotate, and access can be pinned to one workflow.
How do you secure third-party actions/plugins?Senior
Pin them to a full commit SHA (not a mutable tag), review and minimize them, restrict which are allowed, and treat them as code with the same trust as your own — a compromised action runs with your pipeline’s privileges.
# tag is mutable and can be moved under you uses: actions/checkout@v4 # pin to an immutable commit instead uses: actions/checkout@8f4b7f8a1c...
What is SLSA / build provenance?Senior
A framework of levels for supply-chain integrity; provenance is signed, tamper-evident metadata about how and from what an artifact was built. Higher SLSA levels require hardened, non-falsifiable build systems so consumers can verify origin.
Provenance is signed, tamper-evident metadata recording how, from what source, and by which builder an artifact was produced. SLSA is a tiered framework; higher tiers require a hardened, non-falsifiable builder so a consumer can cryptographically verify origin before deploy.
What is a poisoned pipeline execution (pwn request)?Senior
A fork PR that triggers privileged CI — with secrets or a write token — can exfiltrate them or push malicious code (the pull_request_target class of bug). Fix it by never exposing secrets to fork builds, requiring approval to run CI on forks, and separating untrusted build from trusted deploy.
What belongs in a secure pipeline beyond tests?Mid
SAST, dependency/SCA scanning, secret scanning, container and IaC scanning, SBOM generation, and artifact signing — with a sane failure policy so criticals block and noise does not, plus least-privilege runners.
security:
stage: test
script:
- gitleaks detect # secret scanning
- trivy fs --exit-code 1 . # deps + image CVEs
- checkov -d . # IaC misconfig
- syft . -o cyclonedx > sbom.json