GitOps CI/CD & DevSecOps Pipeline
Django · Docker · GitHub Actions · Helm · Argo CD · Kubernetes
A complete GitOps delivery workflow for a Django Library Management System: GitHub Actions builds and publishes container images, Helm keeps the deployment declarative, and Argo CD continuously syncs the Kubernetes cluster to the state described in Git.
Overview
This project delivers a Django application to Kubernetes using Git as the single source of truth. Developers push to main; GitHub Actions builds and publishes a Docker image to Docker Hub; the image tag is written back into the Helm values.yaml and committed; Argo CD detects the change and rolls the new version into the cluster. Every piece of the pipeline lives in the repository as code.
Problem
Manual deployments are slow, inconsistent and hard to audit. Container artifacts drift from what's actually running, and there's no single place that describes the true state of production. This project solves it with GitOps: the repository declares both the application code and the desired cluster state, and the pipeline makes deployment an automated, reviewable step.
Architecture
The loop closes because the pipeline writes new image tags back to Git — the deployable state is always in the repository.
Technology Stack
| Layer | Technology |
|---|---|
| Application | Django Library Management System |
| Database | MySQL 8.0 |
| Container | Docker — Python 3.13-slim + Gunicorn WSGI |
| Orchestration | Kubernetes with Helm charts |
| CI | GitHub Actions (build + publish) |
| CD | Argo CD (continuous reconciliation) |
| Registry | Docker Hub |
CI Pipeline (GitHub Actions)
Defined in .github/workflows/docker_build_and_push.yml:
- Trigger — push to
main, or manual workflow dispatch - Build — Docker image with Buildx, tagged with the GitHub run number
- Push — published to Docker Hub (credentials from GitHub secrets)
- Update — Helm
values.yamlrewritten with the new image tag - Commit — changes committed back to Git (with
[skip ci]to avoid re-triggering)
CD Pipeline (Argo CD)
Defined in infra/argocd-app.yaml. Argo CD monitors the Git repository, detects the new image tag in values.yaml, and syncs the application to the Kubernetes cluster:
- Continuously monitors the declared state in Git
- Detects the image-tag update written by CI
- Applies the Helm release to the target namespace
- Deployment status viewable in the Argo CD dashboard
Kubernetes & Helm
Renamed Helm templates (helm/templates/) describe the platform resources:
- Deployment — the Django app with Gunicorn, running as a non-root user
- Service + Ingress — routing into the application
- Liveness / readiness probes — Kubernetes-restartable and traffic-aware
- ServiceAccount — least-privilege identity for the workload
- Horizontal Pod Autoscaler — optional CPU-driven scaling
- MySQL Deployment — infra manifest for the backing database
Security & DevSecOps
The project applies security as configuration rather than afterthought:
- bcrypt password hashing for stored user credentials
- Role-based access control across application user types
- Non-root container execution in the Docker image
- Environment-based configuration — secrets and settings via environment, not committed files
- GitHub Secrets for registry credentials — nothing sensitive in the repository
Testing
Unit tests exist for the Django application (app/library/tests.py), and CI builds the image from a context that includes the full app. As noted in the CI section, wiring the test suite into the automated pipeline is the documented next step for this repository — the pipeline today is scoped to build, publish and deploy.
Challenges & Solutions
- Keeping Git the source of truth — the pipeline writes versioned config back to Git, so Argo CD and humans always reconcile from the same place.
- Propagating image tags — solving "which tag is deployed" with run-number tags plus a Helm values update committed automatically.
- Avoiding pipeline loops — the auto-update commit uses
[skip ci]so the pipeline doesn't re-trigger on its own changes. - Secure by default images — non-root runtime, probes and a ServiceAccount baked into the Helm templates.
What I Learned
GitOps is a discipline, not a tool: the value came from making Git the single source of truth for both code and cluster state, and letting CI + Argo CD do the boring, repeatable work. I learned to think about what an image tag means at each stage of the pipeline, and how to structure a workflow so it can't silently drift from reality.