Building a Production-Style CI/CD Pipeline with Azure DevOps, ACR, Helm, AKS and PostgreSQL
This guide walks through creating a complete CI/CD workflow that moves code from GitHub into an AKS cluster, using Azure Container Registry, Helm charts, and a private PostgreSQL Flexible Server. It covers resource group setup, networking, DNS, and deployment details.
When you finish learning Docker, Kubernetes, Helm, Azure, and Azure DevOps, the next logical step is to see how they all fit together in a real deployment. The goal of this project is not to build a complex application but to understand the journey of code from a GitHub repository to a live, container‑based service running on Azure Kubernetes Service (AKS).
1. The End‑to‑End Flow
The pipeline starts with a developer committing code to GitHub. Azure DevOps picks up the change, runs unit tests, builds Docker images, pushes them to Azure Container Registry (ACR), and then Helm charts deploy those images to AKS. The application itself consists of a simple front‑end, a back‑end API, and a PostgreSQL database. The database is hosted on Azure Database for PostgreSQL Flexible Server and is accessed over a private network.
2. Architecture Overview
The DEV environment is split into two Kubernetes namespaces: frontend and backend. Each namespace hosts a deployment and a service. The front‑end is exposed through a LoadBalancer service, while the back‑end uses a ClusterIP service. PostgreSQL is placed in a dedicated subnet and is reachable only from the AKS cluster via a private DNS zone.
- GitHub → Azure DevOps → Build & Test → Docker Build → ACR
- Helm Charts → AKS (frontend & backend deployments)
- Private DNS resolves
dessy-k8app-postgres.postgres.database.azure.comto the database’s private IP
3. Setting Up Azure Resources
All resources live in a single resource group named devops-aks-lab-rg-sa in the South Africa North region. Using a resource group keeps permissions, billing, and cleanup simple.
Next, create an Azure Container Registry named dessydevopsacr with the Basic SKU. The admin account is disabled; instead, Azure DevOps authenticates via a service connection. This approach avoids long‑lived credentials and follows best practices for secure CI/CD.
Because PostgreSQL will use private networking, a virtual network is created first. The VNet contains two subnets: aks-subnet for the AKS cluster and postgres-subnet (10.0.2.0/24) for the database. The subnet ranges are non‑overlapping, ensuring proper routing.
After the VNet, a private DNS zone named private.postgres.database.azure.com is created and linked to the VNet. This zone maps the database hostname to its private IP (10.0.2.4).
4. Deploying PostgreSQL Flexible Server
The database server is named dessy-k8app-postgres and runs PostgreSQL 16 on a Standard_B1ms compute tier. During setup, private access is selected, and the server is attached to the postgres-subnet. An administrator user k8appadmin is created, and the password is stored securely in Azure Key Vault (not shown in the guide).
Once the server is live, a database named app is created. The back‑end application will use the connection string:
Host= dessy-k8app-postgres.postgres.database.azure.com; Database=app; Username= k8appadmin; Password= [secure]
5. Building the CI/CD Pipeline
The Azure Pipelines YAML file defines three stages: Build, Test, and Deploy. The Build stage compiles the front‑end and back‑end Dockerfiles, tags the images with the Git commit hash, and pushes them to ACR. The Test stage runs unit tests for both services. The Deploy stage uses Helm to install or upgrade the releases in the frontend and backend namespaces.
Key pipeline features include:
- Service connections for ACR and AKS.
- Environment variables for database credentials.
- Deployment gates that can be added later for UAT and PROD.
6. Verifying Connectivity
To confirm that the AKS pods can reach PostgreSQL, a temporary pod is launched with the official PostgreSQL image. The pod queries the DNS name of the database and attempts a connection using the same credentials the application will use. Successful connection confirms that the private DNS and subnet configuration are correct.
With the pipeline running, any new commit to the GitHub repository triggers a full cycle: tests run, images are rebuilt, and the updated services are rolled out to AKS without manual intervention.
7. Next Steps
Future enhancements could include adding approval gates for UAT and PROD environments, implementing automated database migrations, and integrating monitoring with Azure Monitor and Grafana. The current setup provides a solid foundation for scaling to production workloads.
Why it matters
A well‑structured CI/CD pipeline reduces deployment risk, speeds up release cycles, and ensures consistent environments across development, testing, and production.
Key points
- Use a single resource group to simplify management
- Disable ACR admin account and use Azure DevOps service connections
- Separate AKS and PostgreSQL into distinct subnets for security
- Private DNS resolves database hostnames to private IPs
- Helm charts enable repeatable, versioned deployments
- Verify connectivity with a temporary PostgreSQL pod
Frequently asked questions
Why not run PostgreSQL inside AKS?
Running PostgreSQL as a managed service separates compute from database, improves reliability, and allows Azure to handle backups and scaling.
How do I secure the database credentials?
Store them in Azure Key Vault and reference the secrets in your Helm values or Azure DevOps pipeline variables.
Can I use a different Azure region?
Yes, but check your subscription’s allowed regions and adjust subnet ranges accordingly.





