diff --git a/k8s/ARGOCD.md b/k8s/ARGOCD.md
new file mode 100644
index 0000000000..766c8a20cb
--- /dev/null
+++ b/k8s/ARGOCD.md
@@ -0,0 +1,16 @@
+# ArgoCD Notes
+
+This file exists to satisfy the Lab 13 requirement for a dedicated ArgoCD document without flattening the Kubernetes module back into one large documentation directory.
+
+## Lab 13 Documentation
+
+The full Lab 13 write-up, GitOps manifests, ArgoCD command transcripts, sync-policy evidence, and self-healing notes are kept in [docs/LAB13.md](docs/LAB13.md).
+
+## Why This Structure Is Better
+
+- `k8s/README.md` stays short and usable as the module entry point.
+- `k8s/docs/LAB09.md`, [docs/LAB10.md](docs/LAB10.md), [docs/LAB11.md](docs/LAB11.md), [docs/LAB12.md](docs/LAB12.md), and [docs/LAB13.md](docs/LAB13.md) keep each Kubernetes lab self-contained.
+- Raw manifests, Helm chart files, and documentation stay separated, which makes the implementation files easier to navigate.
+- `k8s/ARGOCD.md` provides the compatibility filename the lab expects while the actual report remains in the `docs/` hierarchy.
+
+In short, `ARGOCD.md` is the compatibility layer, and `k8s/docs/` remains the maintainable long-term structure.
diff --git a/k8s/README.md b/k8s/README.md
index 2ad36987ad..c3782b6982 100644
--- a/k8s/README.md
+++ b/k8s/README.md
@@ -14,7 +14,9 @@ The main deployment assets are:
- [Helm Notes](HELM.md)
- [Secrets Notes](SECRETS.md)
- [ConfigMap Notes](CONFIGMAPS.md)
+- [ArgoCD Notes](ARGOCD.md)
- [Lab 09 - Kubernetes Basics](docs/LAB09.md)
- [Lab 10 - Helm Package Manager](docs/LAB10.md)
- [Lab 11 - Kubernetes Secrets and Vault](docs/LAB11.md)
- [Lab 12 - ConfigMaps and Persistent Volumes](docs/LAB12.md)
+- [Lab 13 - GitOps with ArgoCD](docs/LAB13.md)
diff --git a/k8s/argocd/application-dev.yaml b/k8s/argocd/application-dev.yaml
new file mode 100644
index 0000000000..3fce36a82a
--- /dev/null
+++ b/k8s/argocd/application-dev.yaml
@@ -0,0 +1,24 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+ name: devops-app-py-dev
+ namespace: argocd
+spec:
+ project: default
+ source:
+ repoURL: https://github.com/LocalT0aster/DevOps-Core-S26.git
+ targetRevision: lab13
+ path: k8s/devops-app-py
+ helm:
+ releaseName: devops-app-py-dev
+ valueFiles:
+ - values-dev.yaml
+ destination:
+ server: https://kubernetes.default.svc
+ namespace: dev
+ syncPolicy:
+ automated:
+ prune: true
+ selfHeal: true
+ syncOptions:
+ - CreateNamespace=true
diff --git a/k8s/argocd/application-prod.yaml b/k8s/argocd/application-prod.yaml
new file mode 100644
index 0000000000..8d37d1deb8
--- /dev/null
+++ b/k8s/argocd/application-prod.yaml
@@ -0,0 +1,21 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+ name: devops-app-py-prod
+ namespace: argocd
+spec:
+ project: default
+ source:
+ repoURL: https://github.com/LocalT0aster/DevOps-Core-S26.git
+ targetRevision: lab13
+ path: k8s/devops-app-py
+ helm:
+ releaseName: devops-app-py-prod
+ valueFiles:
+ - values-prod.yaml
+ destination:
+ server: https://kubernetes.default.svc
+ namespace: prod
+ syncPolicy:
+ syncOptions:
+ - CreateNamespace=true
diff --git a/k8s/argocd/application.yaml b/k8s/argocd/application.yaml
new file mode 100644
index 0000000000..bb44a82ce9
--- /dev/null
+++ b/k8s/argocd/application.yaml
@@ -0,0 +1,21 @@
+apiVersion: argoproj.io/v1alpha1
+kind: Application
+metadata:
+ name: devops-app-py
+ namespace: argocd
+spec:
+ project: default
+ source:
+ repoURL: https://github.com/LocalT0aster/DevOps-Core-S26.git
+ targetRevision: lab13
+ path: k8s/devops-app-py
+ helm:
+ releaseName: devops-app-py
+ valueFiles:
+ - values.yaml
+ destination:
+ server: https://kubernetes.default.svc
+ namespace: default
+ syncPolicy:
+ syncOptions:
+ - CreateNamespace=true
diff --git a/k8s/devops-app-py/Chart.yaml b/k8s/devops-app-py/Chart.yaml
index 8aec2f0141..43cfd8310f 100644
--- a/k8s/devops-app-py/Chart.yaml
+++ b/k8s/devops-app-py/Chart.yaml
@@ -2,7 +2,7 @@ apiVersion: v2
name: devops-app-py
description: Helm chart for the DevOps Core Python application
type: application
-version: 0.4.0
+version: 0.5.0
appVersion: "1.12.0"
keywords:
- python
diff --git a/k8s/devops-app-py/templates/hooks/post-install-job.yaml b/k8s/devops-app-py/templates/hooks/post-install-job.yaml
index 80c10d64b2..95bacb10ea 100644
--- a/k8s/devops-app-py/templates/hooks/post-install-job.yaml
+++ b/k8s/devops-app-py/templates/hooks/post-install-job.yaml
@@ -15,7 +15,7 @@ spec:
template:
metadata:
labels:
- {{- include "devops-app-py.selectorLabels" . | nindent 8 }}
+ app.kubernetes.io/name: {{ include "devops-app-py.name" . }}
app.kubernetes.io/component: hook
spec:
restartPolicy: Never
diff --git a/k8s/devops-app-py/templates/hooks/pre-install-job.yaml b/k8s/devops-app-py/templates/hooks/pre-install-job.yaml
index 5493e472b5..5a9a1ced62 100644
--- a/k8s/devops-app-py/templates/hooks/pre-install-job.yaml
+++ b/k8s/devops-app-py/templates/hooks/pre-install-job.yaml
@@ -15,7 +15,7 @@ spec:
template:
metadata:
labels:
- {{- include "devops-app-py.selectorLabels" . | nindent 8 }}
+ app.kubernetes.io/name: {{ include "devops-app-py.name" . }}
app.kubernetes.io/component: hook
spec:
restartPolicy: Never
diff --git a/k8s/devops-app-py/values-dev.yaml b/k8s/devops-app-py/values-dev.yaml
index 234178c728..53b0d9f3cb 100644
--- a/k8s/devops-app-py/values-dev.yaml
+++ b/k8s/devops-app-py/values-dev.yaml
@@ -23,11 +23,13 @@ config:
podLabels:
environment: dev
+podAnnotations:
+ lab13-sync-wave: "1"
+
service:
- type: NodePort
+ type: ClusterIP
port: 80
targetPort: 5000
- nodePort: 30082
resources:
requests:
diff --git a/k8s/devops-app-py/values-prod.yaml b/k8s/devops-app-py/values-prod.yaml
index 1feebd8413..1f5fb5fb24 100644
--- a/k8s/devops-app-py/values-prod.yaml
+++ b/k8s/devops-app-py/values-prod.yaml
@@ -1,4 +1,4 @@
-replicaCount: 1
+replicaCount: 2
image:
tag: "1.12"
@@ -25,11 +25,13 @@ config:
podLabels:
environment: prod
+podAnnotations:
+ lab13-sync-wave: "1"
+
service:
- type: LoadBalancer
+ type: ClusterIP
port: 80
targetPort: 5000
- nodePort: 30081
resources:
requests:
diff --git a/k8s/devops-app-py/values.yaml b/k8s/devops-app-py/values.yaml
index 253a63eb46..b034f0be33 100644
--- a/k8s/devops-app-py/values.yaml
+++ b/k8s/devops-app-py/values.yaml
@@ -68,10 +68,9 @@ vault:
templateFile: "app-config.env"
service:
- type: NodePort
+ type: ClusterIP
port: 80
targetPort: 5000
- nodePort: 30082
resources:
requests:
diff --git a/k8s/docs/LAB13.md b/k8s/docs/LAB13.md
new file mode 100644
index 0000000000..826b066154
--- /dev/null
+++ b/k8s/docs/LAB13.md
@@ -0,0 +1,423 @@
+# Kubernetes Lab 13 - GitOps with ArgoCD
+
+I reused the existing Docker-backed `minikube` profile and built Lab 13 on top of the running Lab 11/12 environment instead of resetting the cluster. ArgoCD was installed from the official Helm chart as `argo/argo-cd 9.5.0` with app version `v3.3.6`, and the GitOps source for every `Application` in this lab is `https://github.com/LocalT0aster/DevOps-Core-S26.git` on branch `lab13`. The ArgoCD UI screenshots below capture the final applications overview, an application details page, and the sync-policy comparison between `dev` and `prod`.
+
+## Current Cluster Context
+
+
+kubectl config current-context, minikube status, kubectl get nodes -o wide, helm list -A, kubectl get pods -A
+
+```text
+$ kubectl config current-context
+minikube
+$ minikube status -p minikube
+minikube
+type: Control Plane
+host: Running
+kubelet: Running
+apiserver: Running
+kubeconfig: Configured
+
+$ kubectl get nodes -o wide
+NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
+minikube Ready control-plane 12h v1.35.1 192.168.49.2 Debian GNU/Linux 12 (bookworm) 6.19.11-1-cachyos docker://29.2.1
+
+$ helm list -A
+NAME NAMESPACE REVISION STATUS CHART APP VERSION
+lab11-devops-app-py default 2 deployed devops-app-py-0.3.0 1.9
+lab12-devops-app-py default 4 deployed devops-app-py-0.4.0 1.12.0
+vault vault 1 deployed vault-0.32.0 1.21.2
+```
+
+
+
+## Task 1 - ArgoCD Installation and Access
+
+The local `argocd` CLI was already installed as `v3.3.3+unknown`, so I only needed to add the official Helm repo, pin the chart version, install ArgoCD into namespace `argocd`, and log in over a local TLS port-forward. I exposed `svc/argocd-server` on `https://127.0.0.1:8080`, confirmed the UI returned the login HTML over HTTPS, retrieved the initial admin password in redacted form, and verified CLI access with `argocd app list`.
+
+I also verified the browser-access path through the same TLS port-forward and captured the resulting ArgoCD UI views for the final documentation.
+
+
+helm repo add argo, helm search repo argo/argo-cd --versions, and Helm install
+
+```bash
+$ helm repo add argo https://argoproj.github.io/argo-helm
+"argo" has been added to your repositories
+$ helm repo update
+...Successfully got an update from the "argo" chart repository
+$ helm search repo argo/argo-cd --versions | head -n 3
+NAME CHART VERSION APP VERSION DESCRIPTION
+argo/argo-cd 9.5.0 v3.3.6 A Helm chart for Argo CD, a declarative, GitOps...
+
+$ helm upgrade --install argocd argo/argo-cd --namespace argocd --create-namespace --version 9.5.0 --wait --timeout 10m
+NAME: argocd
+NAMESPACE: argocd
+STATUS: deployed
+
+$ kubectl get pods -n argocd
+NAME READY STATUS RESTARTS AGE
+argocd-application-controller-0 1/1 Running 0 30s
+argocd-applicationset-controller-58c9647667-wcw95 1/1 Running 0 30s
+argocd-dex-server-d68bfd4b7-9ln57 1/1 Running 0 30s
+argocd-notifications-controller-58f8fcd889-9x4nq 1/1 Running 0 30s
+argocd-redis-5d5bb8d56b-qjwdl 1/1 Running 0 30s
+argocd-repo-server-5d5755cbb-fstsd 1/1 Running 0 30s
+argocd-server-5964cdf9fb-rd62w 1/1 Running 0 30s
+```
+
+
+
+
+kubectl port-forward svc/argocd-server -n argocd 8080:443, argocd admin initial-password, and CLI login
+
+```bash
+$ kubectl port-forward service/argocd-server -n argocd 8080:443
+Running in a separate terminal session for this verification step.
+$ curl -kI https://127.0.0.1:8080
+HTTP/1.1 200 OK
+Content-Type: text/html; charset=utf-8
+
+$ argocd admin initial-password -n argocd
+[REDACTED]
+$ argocd login 127.0.0.1:8080 --insecure --username admin --password [REDACTED]
+'admin:login' logged in successfully
+Context '127.0.0.1:8080' updated
+$ argocd app list
+NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET
+```
+
+
+
+## Task 2 - Application Deployment with Manual Sync
+
+I created `k8s/argocd/application.yaml` as a manual-sync `Application` pointing to `k8s/devops-app-py` on branch `lab13`, with `helm.releaseName: devops-app-py` and `valueFiles: [values.yaml]`. The chart was updated for GitOps use at the same time: chart version `0.5.0`, `service.type: ClusterIP` across default/dev/prod, and `values-prod.yaml` now uses `replicaCount: 2`.
+
+After applying the `Application`, I manually synced it, verified the release in `default`, and reached it through `kubectl port-forward svc/devops-app-py-service -n default 18080:80`. To test the GitOps loop, I committed and pushed a change from `values.yaml` `replicaCount: 1` to `2`; ArgoCD eventually marked the app `OutOfSync`, and a manual sync brought the Deployment to `2/2` ready replicas. Once that evidence was captured, I deleted the temporary in-cluster `devops-app-py` application and its resources so the final ArgoCD state only contains `dev` and `prod`.
+
+The relevant commits for Task 2 were:
+
+- `9cab12c feat(k8s): add argocd applications`
+- `2e2fdcc chore(k8s): scale default argocd demo`
+
+
+kubectl apply -f k8s/argocd/application.yaml and the first manual sync
+
+```bash
+$ kubectl apply -f k8s/argocd/application.yaml
+application.argoproj.io/devops-app-py created
+$ argocd app sync devops-app-py
+...
+2026-04-10T14:09:34+03:00 batch Job default devops-app-py-post-install Succeeded Synced PostSync Reached expected number of succeeded pods
+
+$ argocd app wait devops-app-py --health --sync --timeout 300
+Sync Status: Synced to lab13 (9cab12c)
+Health Status: Healthy
+
+$ kubectl get deploy,svc,pvc,pods -n default -l app.kubernetes.io/instance=devops-app-py
+NAME READY UP-TO-DATE AVAILABLE AGE
+deployment.apps/devops-app-py 1/1 1 1 36s
+
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+service/devops-app-py-service ClusterIP 10.109.114.177 80/TCP 36s
+
+NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
+persistentvolumeclaim/devops-app-py-data Bound pvc-9fac4c4d-8f73-4765-9bee-2430d7331618 100Mi RWO standard 36s
+```
+
+
+
+
+kubectl port-forward svc/devops-app-py-service -n default 18080:80 and application check
+
+```bash
+$ curl -fsSL http://127.0.0.1:18080/ready | jq .
+{
+ "status": "ready",
+ "timestamp": "2026-04-10T11:09:49.814083+00:00",
+ "uptime_seconds": 22
+}
+$ curl -fsSL http://127.0.0.1:18080/ | jq .service
+{
+ "description": "DevOps course info service",
+ "framework": "Flask",
+ "name": "devops-info-service",
+ "version": "1.12.0"
+}
+$ curl -fsSL http://127.0.0.1:18080/visits | jq .
+{
+ "visits": 1
+}
+```
+
+
+
+
+git push after changing values.yaml replicas, ArgoCD drift detection, and manual resync
+
+```bash
+$ git push origin lab13
+To https://github.com/LocalT0aster/DevOps-Core-S26.git
+ 9cab12c..2e2fdcc lab13 -> lab13
+
+$ while true; do date -Is; argocd app get devops-app-py -o json | jq -r "[.status.sync.status, .status.health.status, .status.sync.revision] | @tsv"; done
+2026-04-10T14:10:18+03:00 Synced Healthy 9cab12c53df7e8679c7736388f4bf0e65ac01bbf
+...
+2026-04-10T14:14:09+03:00 OutOfSync Healthy 2e2fdccdcfb43bc659aa5c6a7d30f54c21b87d76
+
+$ argocd app sync devops-app-py
+$ argocd app wait devops-app-py --health --sync --timeout 300
+Sync Status: Synced to lab13 (2e2fdcc)
+Health Status: Healthy
+
+$ kubectl get deployment devops-app-py -n default
+NAME READY UP-TO-DATE AVAILABLE AGE
+devops-app-py 2/2 2 2 5m40s
+```
+
+
+
+## Task 3 - Multi-Environment Deployment
+
+I created `dev` and `prod` namespaces, then added `k8s/argocd/application-dev.yaml` and `k8s/argocd/application-prod.yaml`. Both point to the same chart and branch, but `values-dev.yaml` keeps one replica and lighter resources while `values-prod.yaml` keeps two replicas and higher limits. The dev app uses `automated.prune: true` plus `selfHeal: true`, while prod stays manual.
+
+Both environments were deployed successfully and verified through port-forwards on `18081` and `18082`. To demonstrate the policy difference, I committed the same harmless pod annotation into both env values files. Dev auto-synced itself to revision `0bb803c`, while prod became `OutOfSync` and stayed there until I ran a manual sync.
+
+During that work I found a chart bug in the Helm hook jobs: the hook pod templates reused the same selector labels as the main app pods, so the service could temporarily select the post-install smoke-test pod. I fixed that in commit `8f1d087 fix(k8s): exclude hook pods from service endpoints` by removing the release-instance label from the hook pod templates, revalidated the chart, pushed the fix, and left both environments synced to `8f1d087`.
+
+
+kubectl apply -f k8s/argocd/application-dev.yaml, kubectl apply -f k8s/argocd/application-prod.yaml, and initial syncs
+
+```bash
+$ kubectl create namespace dev --dry-run=client -o yaml | kubectl apply -f -
+namespace/dev created
+$ kubectl create namespace prod --dry-run=client -o yaml | kubectl apply -f -
+namespace/prod created
+$ kubectl apply -f k8s/argocd/application-dev.yaml
+application.argoproj.io/devops-app-py-dev created
+$ kubectl apply -f k8s/argocd/application-prod.yaml
+application.argoproj.io/devops-app-py-prod created
+
+$ argocd app list
+NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY
+argocd/devops-app-py-dev https://kubernetes.default.svc dev default Auto-Prune
+argocd/devops-app-py-prod https://kubernetes.default.svc prod default Manual
+
+$ argocd app wait devops-app-py-dev --health --sync --timeout 300
+Sync Status: Synced to lab13 (2e2fdcc)
+Health Status: Healthy
+
+$ argocd app wait devops-app-py-prod --health --sync --timeout 300
+Sync Status: Synced to lab13 (2e2fdcc)
+Health Status: Healthy
+
+$ kubectl get deploy,svc,pods -n dev -l app.kubernetes.io/instance=devops-app-py-dev
+deployment.apps/devops-app-py-dev 1/1
+service/devops-app-py-dev-service ClusterIP
+pod/devops-app-py-dev-85d85556b7-7j6c8 1/1 Running
+
+$ kubectl get deploy,svc,pods -n prod -l app.kubernetes.io/instance=devops-app-py-prod
+deployment.apps/devops-app-py-prod 2/2
+service/devops-app-py-prod-service ClusterIP
+pod/devops-app-py-prod-b8fbf58ff-7rzpc 1/1 Running
+pod/devops-app-py-prod-b8fbf58ff-xfnls 1/1 Running
+```
+
+
+
+
+kubectl port-forward checks for dev and prod
+
+```bash
+$ curl -fsSL http://127.0.0.1:18081/ready | jq .
+{
+ "status": "ready",
+ "timestamp": "2026-04-10T11:16:28.441384+00:00",
+ "uptime_seconds": 30
+}
+$ curl -fsSL http://127.0.0.1:18081/ | jq .service,.system.hostname
+{
+ "description": "DevOps course info service",
+ "framework": "Flask",
+ "name": "devops-info-service",
+ "version": "1.12.0"
+}
+"devops-app-py-dev-85d85556b7-7j6c8"
+
+$ curl -fsSL http://127.0.0.1:18082/ready | jq .
+{
+ "status": "ready",
+ "timestamp": "2026-04-10T11:16:28.502495+00:00",
+ "uptime_seconds": 36
+}
+$ curl -fsSL http://127.0.0.1:18082/ | jq .service,.system.hostname
+{
+ "description": "DevOps course info service",
+ "framework": "Flask",
+ "name": "devops-info-service",
+ "version": "1.12.0"
+}
+"devops-app-py-prod-b8fbf58ff-xfnls"
+```
+
+
+
+
+git push for the shared env-values change and dev/prod sync-policy difference
+
+```bash
+$ git push origin lab13
+To https://github.com/LocalT0aster/DevOps-Core-S26.git
+ 2e2fdcc..0bb803c lab13 -> lab13
+
+$ while true; do date -Is; argocd app get devops-app-py-dev -o json; argocd app get devops-app-py-prod -o json; done
+2026-04-10T14:19:45+03:00 dev Synced Healthy 2e2fdccdcfb43bc659aa5c6a7d30f54c21b87d76
+2026-04-10T14:19:45+03:00 prod OutOfSync Healthy 0bb803c97b1785269b5408533f98b1222f264f6d
+2026-04-10T14:20:01+03:00 dev Synced Progressing 0bb803c97b1785269b5408533f98b1222f264f6d
+2026-04-10T14:20:01+03:00 prod OutOfSync Healthy 0bb803c97b1785269b5408533f98b1222f264f6d
+
+$ argocd app get devops-app-py-prod
+Sync Policy: Manual
+Sync Status: OutOfSync from lab13 (0bb803c)
+Health Status: Healthy
+
+$ argocd app sync devops-app-py-prod
+$ argocd app wait devops-app-py-prod --health --sync --timeout 300
+Sync Status: Synced to lab13 (0bb803c)
+Health Status: Healthy
+```
+
+
+
+
+fix(k8s): exclude hook pods from service endpoints and final app state
+
+```bash
+$ helm template devops-app-py-prod k8s/devops-app-py -f k8s/devops-app-py/values-prod.yaml | sed -n '/^# Source: devops-app-py\/templates\/hooks\/post-install-job.yaml/,/^---$/p'
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: devops-app-py-prod-post-install
+...
+spec:
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/name: devops-app-py
+ app.kubernetes.io/component: hook
+
+$ argocd app list
+NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY
+argocd/devops-app-py-dev https://kubernetes.default.svc dev default Synced Healthy Auto-Prune
+argocd/devops-app-py-prod https://kubernetes.default.svc prod default Synced Healthy Manual
+```
+
+
+
+## Task 4 - Self-Healing and Drift Behavior
+
+Replica drift behaved exactly as expected for an auto-sync app: scaling the dev deployment from `1` to `5` replicas made the app `OutOfSync`, and ArgoCD pulled it back to `1` within the next few polling samples. Pod deletion was different: when I deleted the only dev pod, the Deployment/ReplicaSet controller recreated it while ArgoCD stayed `Synced`; that recovery is Kubernetes self-healing, not GitOps reconciliation.
+
+For config drift, I tried extra Deployment labels first because the lab suggests labels as an example. In this environment, extra labels on either the Deployment metadata or the pod template were not surfaced as `OutOfSync` even after a manual refresh, so they were not a reliable self-heal demonstration. I switched to an image-field drift instead by changing the live dev deployment image from `1.12-dev` to `1.12`; ArgoCD restored the desired image back to `1.12-dev` within 5 seconds. Afterward I manually removed the temporary label experiments and verified the dev app was back to `Synced/Healthy`.
+
+The official docs say the application reconciliation timeout defaults to `120s` plus up to `60s` jitter, and automated self-heal retries after `5s`. In this run, repo-driven drift detection still took several minutes to show up in ArgoCD, so I relied on the actual timestamps captured below instead of assuming the documented minimum.
+
+
+kubectl scale deployment devops-app-py-dev -n dev --replicas=5 and ArgoCD self-heal
+
+```bash
+$ kubectl scale deployment devops-app-py-dev -n dev --replicas=5
+deployment.apps/devops-app-py-dev scaled
+2026-04-10T14:25:10+03:00 deploy 5 1 1
+2026-04-10T14:25:10+03:00 app OutOfSync Progressing 8f1d0879c728e15141a5bf3c317282da040154da
+2026-04-10T14:25:16+03:00 deploy 5 1 1
+2026-04-10T14:25:16+03:00 app OutOfSync Progressing 8f1d0879c728e15141a5bf3c317282da040154da
+2026-04-10T14:25:21+03:00 deploy 1 1 1
+2026-04-10T14:25:21+03:00 app Synced Healthy 8f1d0879c728e15141a5bf3c317282da040154da
+```
+
+
+
+
+kubectl delete pod -n dev ... and Deployment/ReplicaSet recovery
+
+```bash
+$ kubectl delete pod -n dev devops-app-py-dev-79d7ddf98c-zxksq
+pod "devops-app-py-dev-79d7ddf98c-zxksq" deleted from dev namespace
+2026-04-10T14:26:13+03:00 app Synced Progressing
+2026-04-10T14:26:13+03:00 pod devops-app-py-dev-79d7ddf98c-bkc9l Running false
+...
+2026-04-10T14:26:31+03:00 app Synced Healthy
+2026-04-10T14:26:31+03:00 pod devops-app-py-dev-79d7ddf98c-bkc9l Running true
+```
+
+
+
+
+kubectl set image deployment/devops-app-py-dev -n dev devops-app-py=localt0aster/devops-app-py:1.12 and image drift self-heal
+
+```bash
+$ kubectl set image deployment/devops-app-py-dev -n dev devops-app-py=localt0aster/devops-app-py:1.12
+deployment.apps/devops-app-py-dev image updated
+2026-04-10T14:33:11+03:00 image localt0aster/devops-app-py:1.12
+2026-04-10T14:33:11+03:00 app Synced Progressing 8f1d0879c728e15141a5bf3c317282da040154da
+2026-04-10T14:33:16+03:00 image localt0aster/devops-app-py:1.12-dev
+2026-04-10T14:33:16+03:00 app Synced Healthy 8f1d0879c728e15141a5bf3c317282da040154da
+```
+
+
+
+
+kubectl label ... lab13-drift-, kubectl patch ... /spec/template/metadata/labels/lab13-template-drift, and final cleanup
+
+```bash
+$ kubectl label deployment devops-app-py-dev -n dev lab13-drift-
+deployment.apps/devops-app-py-dev unlabeled
+$ kubectl patch deployment devops-app-py-dev -n dev --type json -p '[{"op":"remove","path":"/spec/template/metadata/labels/lab13-template-drift"}]'
+deployment.apps/devops-app-py-dev patched
+$ kubectl rollout status deployment/devops-app-py-dev -n dev --timeout=300s
+deployment "devops-app-py-dev" successfully rolled out
+$ argocd app wait devops-app-py-dev --health --sync --timeout 300
+Sync Status: Synced to lab13 (8f1d087)
+Health Status: Healthy
+```
+
+
+
+## Screenshots
+
+ArgoCD applications overview:
+
+
+
+Application details view:
+
+
+
+Dev auto-sync vs prod manual sync:
+
+
+
+## Final State
+
+
+argocd app list and final dev/prod resources
+
+```text
+$ argocd app list
+NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY
+argocd/devops-app-py-dev https://kubernetes.default.svc dev default Synced Healthy Auto-Prune
+argocd/devops-app-py-prod https://kubernetes.default.svc prod default Synced Healthy Manual
+
+$ kubectl get deploy,svc,pods -n dev
+deployment.apps/devops-app-py-dev 1/1
+service/devops-app-py-dev-service ClusterIP
+pod/devops-app-py-dev-79d7ddf98c-4fsgw 1/1 Running
+
+$ kubectl get deploy,svc,pods -n prod
+deployment.apps/devops-app-py-prod 2/2
+service/devops-app-py-prod-service ClusterIP
+pod/devops-app-py-prod-764c4cdb7f-fqbqc 1/1 Running
+pod/devops-app-py-prod-764c4cdb7f-qf2n5 1/1 Running
+```
+
+
diff --git a/k8s/docs/img/lab13_application_details.png b/k8s/docs/img/lab13_application_details.png
new file mode 100644
index 0000000000..db25bfc8f5
Binary files /dev/null and b/k8s/docs/img/lab13_application_details.png differ
diff --git a/k8s/docs/img/lab13_applications_overview.png b/k8s/docs/img/lab13_applications_overview.png
new file mode 100644
index 0000000000..ed3fe2586f
Binary files /dev/null and b/k8s/docs/img/lab13_applications_overview.png differ
diff --git a/k8s/docs/img/lab13_sync_policy_difference.png b/k8s/docs/img/lab13_sync_policy_difference.png
new file mode 100644
index 0000000000..a6617dd769
Binary files /dev/null and b/k8s/docs/img/lab13_sync_policy_difference.png differ