Base Workloads With Istio and Argo CD
This example shows which files to add for a minimal application + Istio baseline on an already running cluster. It does not create the cluster, install Istio, install Argo CD, or configure public DNS/TLS for a real domain — those are covered in Istio, GitOps, TLS and Certificates, and Ingress Controllers.
Naming: “Base” here means a starting set of workloads and mesh config, not provisioning a new cluster from scratch.
Prerequisites
Section titled “Prerequisites”- Working
kubectland the correct context (kubectl config current-context). - Istio control plane already installed — see Install (istioctl).
- For Argo CD: controller running in the cluster and a Git repo Argo CD can reach; install steps are on GitOps — ArgoCD, not repeated here.
- If Kubernetes YAML is new, skim Core Objects.
Assumptions
Section titled “Assumptions”- Sample app runs in namespace
demo. - Istio ingress gateway exists in
istio-system(default install). - The app image is a public image (
nginxbelow); replace with your registry in production.
Repository Layout
Section titled “Repository Layout”Use a layout like this in Git (paths are illustrative):
examples/ base-with-istio/ # Argo CD spec.source.path points here (workloads + Istio only) namespaces.yaml app/ deployment.yaml service.yaml istio/ gateway.yaml virtualservice.yaml destinationrule.yaml # optional peerauthentication.yaml # optional argocd/ demo-application.yaml # Apply separately; keep outside synced path to avoid recursionFile Roles
Section titled “File Roles”| File / folder | Purpose |
|---|---|
namespaces.yaml | Creates demo and enables Istio sidecar injection on that namespace. |
app/deployment.yaml | Runs the workload (example: nginx). |
app/service.yaml | ClusterIP Service; port name uses an http prefix for Istio protocol detection. |
istio/gateway.yaml | Istio Gateway on the ingress gateway (usually istio-system). |
istio/virtualservice.yaml | Routes from the gateway to the Kubernetes Service. |
istio/destinationrule.yaml | Optional: subsets, TLS mode to upstream, outlier detection. |
istio/peerauthentication.yaml | Optional: mesh mTLS mode for a namespace — be careful with STRICT during migration. |
examples/argocd/demo-application.yaml | Argo CD Application — apply once from outside the synced path (see Path B (Argo CD)). |
Path A (kubectl)
Section titled “Path A (kubectl)”namespaces.yaml
Section titled “namespaces.yaml”apiVersion: v1kind: Namespacemetadata: name: demo labels: istio-injection: enabledFor revision-based Istio installs, replace istio-injection: enabled with the label your platform uses (for example istio.io/rev: <revision>). See Istio.
app/deployment.yaml
Section titled “app/deployment.yaml”apiVersion: apps/v1kind: Deploymentmetadata: name: demo-app namespace: demospec: replicas: 1 selector: matchLabels: app: demo-app template: metadata: labels: app: demo-app spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80app/service.yaml
Section titled “app/service.yaml”Name the Service port so Istio infers HTTP (for example http or http-web):
apiVersion: v1kind: Servicemetadata: name: demo-app namespace: demospec: selector: app: demo-app ports: - name: http-web port: 80 targetPort: 80istio/gateway.yaml
Section titled “istio/gateway.yaml”The Gateway attaches listeners to the ingress gateway workload in istio-system:
apiVersion: networking.istio.io/v1beta1kind: Gatewaymetadata: name: demo-gateway namespace: istio-systemspec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "*"istio/virtualservice.yaml
Section titled “istio/virtualservice.yaml”Bind traffic that enters through the gateway to the Kubernetes Service DNS name (demo-app.demo.svc.cluster.local or short name demo-app inside the same namespace):
apiVersion: networking.istio.io/v1beta1kind: VirtualServicemetadata: name: demo-vs namespace: demospec: hosts: - "*" gateways: - istio-system/demo-gateway http: - route: - destination: host: demo-app port: number: 80Hosts on the VirtualService must align with how clients call the app (SNI/Host header vs in-cluster names). Mismatches often surface as 404 or routing to the wrong service.
istio/destinationrule.yaml (optional)
Section titled “istio/destinationrule.yaml (optional)”apiVersion: networking.istio.io/v1beta1kind: DestinationRulemetadata: name: demo-app namespace: demospec: host: demo-app.demo.svc.cluster.local trafficPolicy: tls: mode: ISTIO_MUTUAListio/peerauthentication.yaml (optional)
Section titled “istio/peerauthentication.yaml (optional)”Using STRICT before every client has a sidecar can break traffic. Prefer PERMISSIVE until the mesh is ready.
apiVersion: security.istio.io/v1beta1kind: PeerAuthenticationmetadata: name: default namespace: demospec: mtls: mode: PERMISSIVEApply order (kubectl)
Section titled “Apply order (kubectl)”kubectl apply -f namespaces.yamlkubectl apply -f app/deployment.yaml -f app/service.yamlkubectl apply -f istio/gateway.yaml -f istio/virtualservice.yaml- Optional:
destinationrule.yaml,peerauthentication.yaml
Apply a directory in one shot if you prefer: kubectl apply -f examples/base-with-istio/ --recursive (only the base-with-istio tree — not the Argo CD Application manifest).
Sanity check without applying: kubectl apply --dry-run=client -f <file>.
Verify
Section titled “Verify”kubectl get pods,svc -n demokubectl get gateway -n istio-systemkubectl get virtualservice -n demoistioctl analyzeUse the ingress gateway external address (often from kubectl get svc -n istio-system -l istio=ingressgateway) to curl port 80. See Istio troubleshooting.
If Pods fail, see Troubleshooting and Debugging.
Path B (Argo CD)
Section titled “Path B (Argo CD)”Keep the same YAML under examples/base-with-istio/ in Git. Point spec.source.path at that folder only so Argo CD does not sync an Application that references itself (avoid infinite recursion). Store demo-application.yaml outside that path — for example examples/argocd/demo-application.yaml.
Istio CRDs must already exist on the cluster (from the Istio install); Argo CD only applies your Gateway / VirtualService — not istiod.
Each manifest should set metadata.namespace where needed (demo, istio-system, etc.). For multi-namespace apps, prefer explicit namespaces in YAML; Argo CD applies each object to the namespace in the manifest.
RBAC: The default Argo CD installation can sync to common namespaces; custom AppProjects may need extra roles.
examples/argocd/demo-application.yaml
Section titled “examples/argocd/demo-application.yaml”Replace repoURL and targetRevision with your repository. path is only the workload + Istio manifests folder.
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: demo-with-istio namespace: argocdspec: project: default source: repoURL: https://github.com/your-org/your-repo.git targetRevision: main path: examples/base-with-istio destination: server: https://kubernetes.default.svc namespace: demo syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=trueOrder of operations (Argo CD)
Section titled “Order of operations (Argo CD)”- Commit
examples/base-with-istio/(app + Istio YAML only). - Apply the Application once:
kubectl apply -f examples/argocd/demo-application.yaml(or manage it from a bootstrap repo). - Watch sync: Argo CD UI, or
argocd app get demo-with-istio,argocd app sync demo-with-istio.
Installing Argo CD itself is documented on GitOps. If sync stays OutOfSync or health is Unknown, check namespace existence, CRDs, and project permissions. Ensure the demo namespace can be created (CreateNamespace) or pre-create it.
How this fits next to Helm is covered in Helm vs operators vs GitOps.
You Might Also Add
Section titled “You Might Also Add”- cert-manager and real TLS hostnames for production ingress.
- NetworkPolicies, PodDisruptionBudgets, HPA, ResourceQuota / LimitRange.
- Sealed Secrets or External Secrets for credentials.
- ServiceMonitor (if you use Prometheus).
- Kustomize overlays (
base/+overlays/prod) for environment-specific patches.