8 min to read
Integrating Kustomize with ArgoCD for GitOps
Automating deployments across multiple environments with ApplicationSet

Overview
In my previous post, I covered how to efficiently manage Kubernetes resources using Kustomize. In this article, I’ll demonstrate how to integrate Kustomize with ArgoCD to establish an automated GitOps-based deployment environment.
ArgoCD is a declarative continuous delivery tool that automatically synchronizes the state of your Kubernetes cluster with manifest definitions stored in Git repositories. By combining it with Kustomize, you can easily manage environment-specific YAML overlays while automating deployments through GitOps practices.
In this tutorial, we’ll cover how to use the ApplicationSet resource to automatically create and deploy multiple applications across dev, qa, and production environments, providing a comprehensive understanding of the core concepts behind GitOps strategies for maintaining environment-specific resources.
Kustomize + ArgoCD ApplicationSet
For this practical exercise, we’ll be using the argocd-applicationset-nginx-deployment
example from the following GitHub repository:
https://github.com/somaz94/kustomize-study
Directory Structure
The project has the following structure:
- app/nginx/base: Contains the base resources for the Nginx application
- app/nginx/overlays: Contains directories with environment-specific resource configurations (dev, qa, prod)
- appsets: Contains ArgoCD ApplicationSet definitions
- github-secret: Contains definitions for secrets needed to access the GitHub repository
.
├── README.md
├── app
│ └── nginx
│ ├── base
│ │ ├── kustomization.yaml
│ │ ├── nginx-deployment.yaml
│ │ ├── nginx-ingress.yaml
│ │ └── nginx-service.yaml
│ └── overlays
│ ├── dev
│ │ ├── ingress.yaml
│ │ ├── kustomization.yaml
│ │ └── replica-count.yaml
│ ├── prod
│ │ ├── ingress.yaml
│ │ ├── kustomization.yaml
│ │ └── replica-count.yaml
│ └── qa
│ ├── ingress.yaml
│ ├── kustomization.yaml
│ └── replica-count.yaml
├── appsets
│ └── nginx-applicationset.yaml
└── github-secret
└── github-secret.yaml
Deployment Workflow
The deployment workflow consists of three main steps:
- Creating GitHub Repository Secret and Project in ArgoCD
- Writing and uploading templates
- Deploying the ApplicationSet to ArgoCD
1. Creating GitHub Repository Secret and Project in ArgoCD
First, we need to create a secret for accessing the GitHub repository:
# github-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: github-secret
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
type: git
url: git@github.com:somaz94/kustomize-study.git
sshPrivateKey: |
# sshPrivateKey Example
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
...
0zbXo5J4Yb6oMAAAAUZ2VuaXVzNTcxMUBnbWFpbC5jb20BAgMEBQYH
-----END OPENSSH PRIVATE KEY-----
Deploy the secret:
kubectl apply -f github-secret.yaml -n argocd
After deploying the secret, verify that the GitHub repository is registered in ArgoCD and create a new project.
3. Deploying the ApplicationSet to ArgoCD
Now, let’s deploy the ApplicationSet to ArgoCD:
# nginx-applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: nginx-appicationset
spec:
generators:
- matrix:
generators:
- list:
elements:
- cluster: master-node.somaz.link
url: https:// # ClusterIP or URI or Domain
overlay: dev # This new field specifies the overlay to use
values:
environment: dev
project: nginx
- cluster: master-node.somaz.link
url: https://
overlay: qa
values:
environment: qa
project: nginx
- cluster: master-node.somaz.link
url: https://
overlay: prod
values:
environment: prod
project: nginx
- git:
repoURL: git@github.com:somaz94/kustomize-study.git
revision: HEAD
directories:
- path: argocd-applicationset-nginx-deployment/app/nginx/overlays/ # Use the new overlay field
template:
metadata:
name: '-'
spec:
project: ''
source:
repoURL: git@github.com:somaz94/kustomize-study.git
targetRevision: HEAD
path: ''
kustomize:
namePrefix: '-'
destination:
server: ''
namespace: '-'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true
Deploy the ApplicationSet:
kubectl apply -f nginx-applicationset.yaml -n argocd
Verifying the Deployment
After deploying the ApplicationSet, you can verify that the resources have been created in each environment:
k get po,svc,ingress -n dev-nginx
NAME READY STATUS RESTARTS AGE
pod/dev-nginx-deployment-8d545c96d-2w7sw 1/1 Running 0 88s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dev-nginx-service ClusterIP 10.233.21.0 <none> 80/TCP 88s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/dev-nginx-ingress <none> dev-nginx.fgn.nerdystar.io 10.10.100.22 80 88s
k get po,svc,ingress -n qa-nginx
NAME READY STATUS RESTARTS AGE
pod/qa-nginx-deployment-8d545c96d-4bd8t 1/1 Running 0 93s
pod/qa-nginx-deployment-8d545c96d-mxchm 1/1 Running 0 93s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/qa-nginx-service ClusterIP 10.233.62.87 <none> 80/TCP 94s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/qa-nginx-ingress <none> qa-nginx.fgn.nerdystar.io 10.10.100.22 80 93s
k get po,svc,ingress -n prod-nginx
NAME READY STATUS RESTARTS AGE
pod/prod-nginx-deployment-8d545c96d-45z4t 1/1 Running 0 96s
pod/prod-nginx-deployment-8d545c96d-jspm5 1/1 Running 0 96s
pod/prod-nginx-deployment-8d545c96d-qf97w 1/1 Running 0 96s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/prod-nginx-service ClusterIP 10.233.17.172 <none> 80/TCP 97s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/prod-nginx-ingress <none> prod-nginx.fgn.nerdystar.io 10.10.100.22 80 96s
Advanced Use Cases
Wrapping Helm Charts with Kustomize
In real-world scenarios, it’s often difficult to use Helm Charts as-is. One effective approach is to wrap Helm Charts with Kustomize to add environment-specific overlays.
For example, you can include Helm-generated resources in the base directory and override the values.yaml in overlays/dev:
# base/kustomization.yaml
helmCharts:
- name: nginx
repo: https://charts.bitnami.com/bitnami
version: 13.2.18
releaseName: nginx
namespace: default
valuesFile: values.yaml
The environment-specific values.yaml files are then managed separately in the overlay directories.
CI/CD Integration Example
The core of GitOps in a Kustomize + ArgoCD structure is the “Git push → automatic deployment” flow.
Here’s an example using GitHub Actions to trigger ArgoCD CLI app sync when a commit occurs on the main branch:
name: ArgoCD Sync Trigger
on:
push:
branches:
- main
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Sync ArgoCD App
run: |
argocd login $ARGOCD_SERVER --username admin --password $ARGOCD_PASSWORD --insecure
argocd app sync nginx-dev
env:
ARGOCD_SERVER: argocd.example.com
ARGOCD_PASSWORD: $
With this setup, developers can experience the complete GitOps flow where deployment is automated simply by committing to Git.
Fine-Tuning Sync Policies
The syncPolicy section offers various options beyond the automated prune and selfHeal settings. These options provide more granular control over the synchronization strategy:
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true # Automatically create namespaces
- ApplyOutOfSyncOnly=true # Apply only resources that have changed
- RespectIgnoreDifferences=true # Respect ignoreDifferences settings
- Validate=false # Skip resource validation
- PrunePropagationPolicy=foreground # Configure deletion strategy
By properly combining these options, you can design a flexible GitOps pipeline that aligns with your team’s deployment strategy.
Operational Optimization Tips
ArgoCD Ignore Differences Setting
Some resources might always be marked as OutOfSync by ArgoCD due to fields that change automatically during operation, such as metadata.annotations in Pod templates.
In such cases, you can use the ignoreDifferences option to ignore these changes:
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/template/metadata/annotations
- group: ""
kind: Service
jsonPointers:
- /spec/clusterIP
This prevents unnecessary Sync indicators or unintended resource updates.
Conclusion: The First Step in GitOps Automation
By integrating ArgoCD with Kustomize, you can implement a GitOps strategy that goes beyond manual deployments, where “environment-specific resources declared in Git → automatic synchronization” becomes the norm.
The ApplicationSet resource is particularly useful for automatically creating numerous applications according to patterns, making it highly advantageous for large microservice architectures or multi-environment operations.
Additionally, Kustomize’s base/overlay structure reduces manifest duplication and improves maintainability. By configuring your CI pipeline to have ArgoCD automatically reflect the results built through Kustomize, you can evolve into a true GitOps workflow.
While this tutorial focused on nginx deployment, in actual project environments, this serves as a core automation foundation that enables efficient operation of dozens to hundreds of resources.
References
- GitHub - Kustomize + ArgoCD Practice Code
- ArgoCD Official Documentation
- ArgoCD ApplicationSet Official Documentation
- Kustomize Official Documentation
- ArgoCD SyncPolicy Options Guide
- ArgoCD Ignore Differences Setting Examples
- Bitnami Helm Charts (nginx example)
- What is GitOps (Weaveworks Introduction)
- CI/CD with GitHub Actions + ArgoCD Example
Comments