Angular, NestJS, GitHub Actions, GHCR, Argo CD, and Traefik on a Windows laptop.
Introduction
This project uses a local Kubernetes cluster to deploy an Angular frontend and a NestJS backend.
The architecture separates three responsibilities:
- CI: GitHub Actions builds and publishes container images.
- GitOps: Git stores the desired Kubernetes configuration.
- CD: Argo CD reconciles that configuration with the cluster.
The cluster runs on Windows using VirtualBox, Minikube, and containerd.
1. Architecture Overview

Figure 1 — The complete deployment pipeline, from source code to Kubernetes.
The deployment sequence is:
- A developer pushes Angular or NestJS changes to
main. - GitHub Actions builds both container images and publishes them to GHCR.
- CI verifies the image manifests and downloads.
- The workflow updates the Kustomize image tags using the Git commit SHA.
- Argo CD detects the manifest changes and reconciles Kubernetes resources.
- Kubernetes pulls the requested images and rolls out the Deployments.
- Traefik routes incoming requests to Angular or NestJS.
GitHub Actions does not connect directly to the local Kubernetes cluster. Argo CD manages deployment from the repository.
2. Local Kubernetes Environment
Minikube runs inside a VirtualBox VM, with containerd as the container runtime.
minikube start `
--driver=virtualbox `
--container-runtime=containerd `
--profile=k8s-vbox `
--cpus=2 `
--memory=4096
Verify the cluster:
kubectl get nodes
kubectl get pods -A
All application workloads and supporting controllers run inside this single-node cluster.
3. Monorepo Structure
Application code and Kubernetes configuration share one repository.
project/
├── agapp/ # Angular
├── backend/ # NestJS
├── .github/workflows/
│ └── docker-build.yaml
└── infra/
├── bootstrap/ # Root Argo CD Application
├── app/ # Child Applications
└── k8s/
├── platform/ # Namespace and ConfigMap
└── workloads/ # Deployments and routing
The separation between infra/app and infra/k8s is intentional:
-
infra/appdescribes which components Argo CD manages. -
infra/k8sdescribes the Kubernetes resources those components deploy.
4. CI: Building and Publishing Images
The GitHub Actions workflow uses a matrix to build Angular and NestJS in parallel.
strategy:
matrix:
include:
- name: frontend
context: ./agapp
image: angular-frontend
- name: backend
context: ./backend
image: angular-backend
Docker Buildx builds linux/amd64 images and publishes two tags per application:
ghcr.io/example-org/angular-frontend:latest
ghcr.io/example-org/angular-frontend:<commit-sha>
ghcr.io/example-org/angular-backend:latest
ghcr.io/example-org/angular-backend:<commit-sha>
After publishing, the workflow checks the image manifest and performs a docker pull to validate registry access.
The update-gitops job only runs after both matrix builds have succeeded.
5. GitOps: Updating the Desired Version
Kubernetes does not automatically restart an application when the contents of a latest image change.
Instead, the workflow updates:
infra/k8s/workloads/kustomization.yaml
The images section records an explicit version:
images:
- name: ghcr.io/example-org/angular-frontend
newTag: <commit-sha>
- name: ghcr.io/example-org/angular-backend
newTag: <commit-sha>
The workflow commits this change to main.
This gives Argo CD a manifest change to detect and provides a traceable relationship between application source code and the requested Kubernetes image version.
The update job requires repository write permission, which must be permitted by the GitHub repository and organization policies.
6. CD: Argo CD and App of Apps
Argo CD uses an App of Apps structure.
The root Application tracks:
source:
targetRevision: main
path: infra/app
It manages five child Applications:
| Application | Responsibility |
|---|---|
angular-platform |
Namespace and ConfigMap |
angular-workloads |
Frontend, backend, HTTPRoute and SealedSecret |
traefik |
Gateway and traffic routing |
sealed-secrets |
Secret controller |
prometheus |
Monitoring |
The workload Application reads infra/k8s/workloads.
When an image tag changes, Argo CD updates the Deployment specification. Kubernetes then pulls the new image and performs a rolling update.
Git synchronization and workload readiness are verified independently.
7. Application Routing with Traefik
Angular and NestJS each use an internal Kubernetes Service.
Traefik provides the external entry point through NodePort 30080.
The Traefik Helm values include:
service:
type: NodePort
ports:
web:
port: 8000
exposedPort: 80
nodePort: 30080
expose:
default: true
Gateway API HTTPRoute sends requests to the appropriate Service:
| Request path | Destination |
|---|---|
/api and /api/*
|
NestJS, port 3000 |
/ and other paths |
Angular, port 3000 |
The application is accessible from Windows at:
http://<minikube-ip>:30080/
This avoids exposing separate NodePorts for the frontend and backend or requiring a permanent port-forward session.
8. Secrets and Monitoring
Sealed Secrets
The backend receives configuration through a ConfigMap and credentials through a Kubernetes Secret.
Sealed Secrets stores encrypted secret values in Git. The controller creates the corresponding Kubernetes Secret inside the cluster.
Plaintext database credentials and API tokens are not included in the repository manifests.
Prometheus
Prometheus is installed through an Argo CD Helm Application.
The current configuration uses:
- One Prometheus server replica.
- 24-hour retention.
- No persistent volume.
- Enabled kube-state-metrics.
- Disabled Alertmanager, Pushgateway and node-exporter.
This keeps resource consumption relatively small for the local Minikube environment.
9. Deployment Verification
Each deployment stage is checked separately.
CI: Confirm the GitHub Actions build and GitOps update jobs succeed.
GitOps: Confirm Argo CD synchronization:
kubectl get applications -n argocd
Kubernetes: Verify running Pods and configured images:
kubectl get pods -n angular-local
kubectl get deployments -n angular-local `
-o custom-columns="NAME:.metadata.name,IMAGE:.spec.template.spec.containers[*].image"
Application: Test the Traefik entry point:
http://<minikube-ip>:30080/
http://<minikube-ip>:30080/api/health
These checks distinguish a successfully published image, an applied Kubernetes configuration, and a healthy application rollout.
Conclusion
This project combines GitHub Actions, GHCR, Kustomize, Argo CD, and Kubernetes into a GitOps deployment workflow running on a Windows laptop.
Each component has one responsibility:
GitHub Actions builds artifacts. Git records the desired version. Argo CD reconciles the configuration. Kubernetes runs the workloads. Traefik exposes the applications.
Sealed Secrets and Prometheus support the environment through credential management and metrics collection.
The result is a local Kubernetes platform with automated deployment configuration and a consistent path from application source code to Kubernetes.
Top comments (0)