qasim@wiki:~$
Portfolio / Wiki Home / CI/CD Pipeline Series

Building a Production CI/CD Pipeline

Jenkins + SonarQube + Bitbucket, deploying to a shared multi-tenant Linux host. Built and operated in production for a multi-client e-commerce platform.

What this series is. A step-by-step build log of a real pipeline — the commands that worked, the errors that appeared, and the decisions behind each choice. Not a Jenkins tutorial. Every config value, failure mode, and fix here came out of an actual rollout.

The problem being solved

A single staging host runs API servers for a dozen restaurant-brand clients. Each client has its own Linux service account, its own nvm installation with a different Node version, and its own PM2 process tree. Deployments were manual: SSH in, git pull, npm ci, pm2 restart, hope nothing broke.

The goal: automated builds triggered from source control, with code quality and security gates that block a deploy when the code doesn't meet standard — without giving the CI system root, and without exposing the Jenkins controller to the internet.

Final architecture

Bitbucket (source)SCM polling — outbound only, no inbound webhook Jenkins controller 10.10.1.215 (private network) │ ├─ Node version check ──→ enforce .nvmrc matches build tool ├─ npm ci ├─ Dependency audit ────→ notify-only ├─ TypeScript compile ├─ Lint + Format ───────→ BLOCKING ├─ Unit tests + coverage ├─ SonarQube scan ──────→ 10.10.1.231:9000 ├─ Quality Gate ────────→ BLOCKING (abortPipeline: true) Deploy (only when currentResult == SUCCESS) │ ssh ubuntu@staging → sudo -u <client> → source nvm → pm2 restart Staging host 10.10.1.223 — one service user per client

Series contents

PartTopicWhat you'll be able to do
1Jenkins Controller SetupInstall Jenkins on Ubuntu, harden it, pick the right plugin set, configure executors and build retention
2SonarQube & Quality GatesStand up SonarQube, create projects, author a custom quality gate, understand New Code vs Overall Code
3Wiring the ToolchainTokens, credentials, sonar-scanner install, SMTP notifications, the PATH trap that breaks non-interactive shells
4Jenkinsfile AnatomyWrite a declarative pipeline stage by stage, including the notify-only pattern for staged rollouts
5Multi-Client DeploysDeploy to per-user nvm/PM2 environments over SSH with least-privilege sudo, no root anywhere
6Gate EnforcementRoll out blocking gates on a legacy codebase without stopping the business
7TroubleshootingEvery real failure hit during the build — symptom, root cause, fix

Environment reference

Values used throughout the series. Substitute your own.

Jenkins controllerUbuntu 24.04, Jenkins 2.555.3 LTS, Java 17
SonarQube10.5.1 Community Edition, http://10.10.1.231:9000
Scannersonar-scanner CLI 6.2.1 at /opt/sonar-scanner
Staging hostUbuntu, one service user per client, per-user nvm + PM2
CI deploy accountubuntu — scoped NOPASSWD sudo to each service user
Source controlBitbucket Cloud, HTTPS + app-password credential
SMTPZoho smtp.zoho.com:587 STARTTLS
App stackNestJS API, TypeScript, Biome lint, Prettier, Jest

Principles applied