Inspired by this awesome playlist GitOps and K8s بالعربي.
| # | Challenge | Artifact |
|---|---|---|
| 1 | Build a Go web app that displays deployment metadata (version, commit, build time, env, pod, node) | gitops-sample-app |
| 2 | Create a multi-stage Dockerfile with build args for metadata injection | Dockerfile |
| 3 | Create Kustomize manifests with base + per-environment overlays (dev/prod) | gitops-sample-app-manifests |
| 4 | Inject pod/node metadata automatically using Kubernetes Downward API | base/deployment.yaml |
| 5 | Build a CI pipeline that builds/pushes Docker image and updates the manifests repo | ci.yaml |
| 8 | Trigger CI on PR merge (dev) and semver tags (prod) | ci.yaml |
| 9 | Configure ArgoCD Applications for auto-sync with prune and self-heal | application.yml |
| 10 | Separate namespaces per environment to avoid resource collisions | overlays/ |
- Kubernetes cluster (Minikube, Kind, EKS, GKE, etc.)
- ArgoCD installed and configured
kubectlaccess to the cluster
Note: This project was tested and created on Minikube with Kubernetes v1.35.
- Go 1.25+
- Docker
gitmake- Docker Hub account with access token
The app is simple — it provides information about the current deployment (version, commit_sha, build_time, environment, pod_name, node_name, host_ip, request_count) and runtime details (go_version, uptime).
type AppInfo struct {
Version string `json:"version"`
GitCommit string `json:"git_commit"`
BuildTime string `json:"build_time"`
Environment string `json:"environment"`
PodName string `json:"pod_name"`
NodeName string `json:"node_name"`
HostIP string `json:"host_ip"`
GoVersion string `json:"go_version"`
Uptime string `json:"uptime"`
RequestCount int64 `json:"request_count"`
}| Endpoint | Description |
|---|---|
GET / |
HTML dashboard |
GET /healthz |
Health check (JSON) |
GET /api/info |
All metadata (JSON) |
The pipeline is defined in .github/workflows/ci.yaml and consists of two jobs:
| Event | Target | Image Tag | Updates |
|---|---|---|---|
PR merged to main |
Dev | <commit-sha> |
overlays/dev |
Tag push v* |
Prod | v1.x.x |
overlays/prod |
1. build-and-push
- Checks out the app code
- Extracts build metadata (version, commit SHA, build time)
- Logs in to Docker Hub
- Builds and pushes the Docker image with the appropriate tag +
latest
2. update-manifests
-
Checks out the manifests repo
-
Updates
newTagin the target overlay'skustomization.yamlusingsed:sed -i "s|newTag:.*|newTag: <new-tag>|" kustomization.yamlsed -i— edit the file in-places|pattern|replacement|— substitute (using|as delimiter instead of/)newTag:.*— matchnewTag:followed by anythingnewTag: <new-tag>— replace with the new image tag (commit SHA or semver)
-
Commits and pushes the change, triggering ArgoCD to sync
| Secret | Description |
|---|---|
DOCKERHUB_USERNAME |
Docker Hub username |
DOCKERHUB_TOKEN |
Docker Hub access token |
MANIFESTS_PAT |
GitHub PAT with repo scope for the manifests repo |
PR merged to main ──► build-and-push ──► update overlays/dev ──► ArgoCD syncs dev
Tag v1.x.x pushed ──► build-and-push ──► update overlays/prod ──► ArgoCD syncs prod
The Kubernetes manifests live in a separate repo: gitops-sample-app-manifests.
Shared resources used by all environments: Namespace, Deployment, Service, and ConfigMap. The Deployment uses envFrom to load the ConfigMap and the Kubernetes Downward API to inject POD_NAME and NODE_NAME.
Each overlay sets its own namespace to avoid resource collisions:
| Overlay | Namespace | Image Tag | Replicas | APP_ENV |
|---|---|---|---|---|
dev |
gitops-sample-app-dev |
<commit-sha> |
1 | development |
prod |
gitops-sample-app-prod |
v1.x.x |
3 | production |
Both environments are defined in application.yml with automated sync policy, prune, and selfHeal enabled. ArgoCD watches the manifests repo and automatically applies changes when the CI updates the image tag.
# application.yml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitops-sample-app-dev
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/moabdelazem/gitops-sample-app-manifests.git
targetRevision: main
path: overlays/dev
destination:
server: https://kubernetes.default.svc
namespace: gitops-sample-app-dev
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitops-sample-app-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/moabdelazem/gitops-sample-app-manifests.git
targetRevision: main
path: overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: gitops-sample-app-prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

