Chained Jenkins CI/CD

A chained Jenkins workflow that builds container images and deploys a web app to a VPS.

By Himan MandujaSource reviewed Reviewed commit 3c14646 ↗

On this page
  1. Two jobs, two responsibilities
  2. The request path
  3. Preparing the host
  4. Monitoring and validation
  5. Release tradeoffs
  6. Source files

A container build and a deployment are different operations. This project makes that distinction visible through chained Jenkins jobs: one validates and publishes a Next.js frontend and Flask API, while another updates the running application on a VPS.

The useful engineering story is the handoff between those jobs. The repository contains the Jenkinsfiles, Compose topology, host configuration, and application tests, so the delivery path can be followed beyond a pipeline screenshot.

Two jobs, two responsibilities

The CI Jenkinsfile checks out the source, installs frontend dependencies, runs lint, installs Python dependencies, and executes the backend pytest suite. Only after those stages does it build and push the two Docker images. A successful run triggers the downstream DevOps-CD job.

The deployment job runs in an Ubuntu container. It installs its SSH tooling, connects to the VPS, copies the Compose and Prometheus configuration, writes the image settings, and runs docker compose pull followed by docker compose up -d. Jenkins job creation and webhook setup are manual steps documented in the README; they are prerequisites to this chain.

Chained Jenkins jobs and the VPS application pathCI publishes images and triggers CD; CD pulls the images and updates Compose on the VPS. Monitoring services are commented out in the checked runtime configuration. Components: GitHub webhook, Configured in Jenkins; Jenkins CI, Lint · pytest · build; Docker Hub, Mutable latest images; Jenkins CD, SSH + Compose update; Browser, Application request; Host Nginx, Reverse proxy; Frontend, Host port 57002; Flask API, Container port 5000.VPS / HOST NGINX + DOCKER COMPOSEdeployGitHub webhookConfigured in JenkinsJenkins CILint · pytest · buildDocker HubMutable latest imagesJenkins CDSSH + Compose updateBrowserApplication requestHost NginxReverse proxyFrontendHost port 57002Flask APIContainer port 5000
Architecture reconstructed from the linked implementation.Swipe to inspect →
CI publishes images and triggers CD; CD pulls the images and updates Compose on the VPS. Monitoring services are commented out in the checked runtime configuration.

The request path

On the VPS, host Nginx forwards requests to port 57002, which Compose publishes from the frontend container’s port 3000. The frontend reaches Flask on port 5000 through the container network. Flask provides a small /api/data endpoint and a Prometheus-format /metrics endpoint.

The Compose file separates frontend and backend networks. The backend joins both, and its backend network is marked internal. The backend port is exposed to containers rather than published as a host port. That is a concrete boundary to inspect when understanding which services can be reached externally.

Jenkins DevOps-CD run triggered by an upstream pipeline and deploying to the VPS
The CD run identifies an upstream pipeline trigger and a Deploy to VPS stage. It shows the downstream half of the chained workflow. Repository source ↗

Preparing the host

A separate DevOps-InitDomain job uses Ansible to prepare the domain-facing reverse proxy. The playbook configures Nginx and invokes Certbot. Keeping initialization separate means the normal application release does not need to reinstall that host setup every time an image changes.

There is an operational consequence: provisioning, certificate setup, and application delivery have separate failure modes. The playbook ignores Certbot errors, so a completed initialization job alone does not establish that TLS was issued successfully. Certificate status needs its own verification.

Jenkins initialization job running the Ansible host setup playbook
The separate initialization job runs the Ansible playbook for host preparation. It is distinct from the normal application release. Repository source ↗

Monitoring and validation

The backend tests cover its data and metrics routes, and the repository includes a Prometheus scrape configuration targeting backend:5000. However, the Prometheus and Grafana services are commented out in the checked Compose file. The deploy job copies monitoring configuration, but that does not start a monitoring stack. This snapshot therefore demonstrates instrumentation and prepared configuration, rather than an active end-to-end monitoring deployment.

Jenkins pipeline stage view with recorded CI results
A recorded Jenkins stage view documents captured pipeline runs rather than current service health. Repository source ↗

Release tradeoffs

The release mechanism favors an understandable single-host workflow. Images use the mutable latest tag, and the deploy job uses password-based SSH with host-key checking disabled. Those choices simplify a lab, but make artifact identity and host authentication weaker than a hardened release process.

The Jenkinsfiles also do not implement a health-gated rollout or automatic rollback. The next useful improvements would be immutable image references, verified SSH host keys, an application smoke check after Compose starts, and a known previous version to restore. Those are proposed improvements, not features claimed by this project.

Source files