Sitemap

ArgoCD Install & Upgrade Guide

by bless2k@gmail.com (AI-assisted by Claude), 2026/05

ArgoCD는 Kubernetes(k8s) 환경에서 GitOps 기반의 지속적 배포(CD)를 구현하고 자동화하기에 매우 강력하고 유용한 툴이다. 다만, 버그가 많은 편이므로 지속적인 관리와 업데이트가 필요하다.

Press enter or click to view image in full size
GitOps + ArgoCD

A practical, end-to-end guide covering installation, backup, upgrade, and rollback of ArgoCD using manifest-based deployment.

Scope: Non-HA install via manifests/install.yaml. Tested for ArgoCD v3.x on AKS, EKS, GKE, and on-prem Kubernetes 1.27+. For HA installs, swap manifests/install.yaml for manifests/ha/install.yaml throughout — all other steps are identical.

0. Prerequisites

  • Kubernetes cluster, version 1.27 or newer (verify against the ArgoCD support matrix)
  • kubectl configured with cluster-admin context for the target cluster
  • Outbound network access to raw.githubusercontent.com and quay.io

Set the target version once and reuse it throughout:

export ARGOCD_VERSION=v3.4.1

1. Installation

1.1 Create the Namespace

kubectl create namespace argocd

The upstream manifests assume the namespace name argocd. Changing it requires editing every ClusterRoleBinding and Service reference — more work than it saves. Stick with argocd unless you have a strong reason not to.

1.2 Apply the Install Manifests

The unified install.yaml bundles CRDs, RBAC, ConfigMaps, Secrets, Services, Deployments, and the application-controller StatefulSet. There is no separate CRDs step.

kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml

Why --server-side --force-conflicts:

  • The ApplicationSet CRD exceeds the 262144-byte client-side apply annotation limit. Plain kubectl apply will fail with metadata.annotations: Too long.
  • --force-conflicts lets the apply take ownership of fields that would otherwise conflict with existing managers. On a fresh install this is harmless; on upgrades it has consequences (see §4.1).

Always pin to a version tag (v3.4.1), never stable. Pinning gives you a reproducible install you can match exactly during upgrade and rollback.

1.3 Wait for All Components

kubectl rollout status -n argocd deploy/argocd-server
...
kubectl get pods -n argocd

All pods should be Running and Ready 1/1.

1.4 Retrieve the Initial Admin Password

ArgoCD generates a random admin password on first install and stores it in a secret. Read it once, then plan to change it (§1.7).

kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d ; echo

The username is admin.

1.5 Access the Argo CD Server

The argocd-server Service is ClusterIP by default. Pick one of the access patterns below.

Option A — Port-forward (quickest, for testing):

kubectl port-forward -n argocd svc/argocd-server 8080:443
# UI: https://localhost:8080

The certificate is self-signed; the browser will warn on first visit.

Option B — LoadBalancer (cloud clusters):

kubectl patch svc argocd-server -n argocd \
-p '{"spec": {"type": "LoadBalancer"}}'
kubectl get svc argocd-server -n argocd \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}{"\n"}'

On AKS this provisions an Azure Load Balancer; on EKS, an ELB; on GKE, a GCLB. Restrict access via NSG/SG rules or a private LB if exposing only to corporate networks.

Option C — Ingress with TLS (recommended for production):

Configure your ingress controller (NGINX, Traefik, AGIC on AKS, ALB on EKS) to terminate TLS and forward to argocd-server. ArgoCD ships with HTTP/2 and gRPC; for NGINX you typically need nginx.ingress.kubernetes.io/backend-protocol: HTTPS and to disable TLS termination at the ArgoCD server with --insecure in argocd-cmd-params-cm. See the upstream ingress configurations doc for controller-specific examples.

1.6 Change the Admin Password

Log in to the UI as admin with the password from §1.4, then User Info → Update Password.

Get HyunWoo Lee, Yet Another Software Engineer’s stories in your inbox

Join Medium for free to get updates from this writer.

After the password is changed, delete the bootstrap secret so it cannot be re-read:

kubectl -n argocd delete secret argocd-initial-admin-secret

1.7 Smoke Test

Confirm sync works end-to-end with a declarative Application:

cat <<'EOF' | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated: {}
syncOptions:
- CreateNamespace=true
EOF
kubectl -n argocd get application guestbook -w

The Application should reach SYNC STATUS: Synced and HEALTH STATUS: Healthy. Delete it when done:

kubectl -n argocd delete application guestbook
kubectl -n default delete deploy,svc -l app=guestbook-ui

If the app reaches Synced / Healthy, your install is complete.

2. Pre-Upgrade Checklist

  • Record current version — you’ll need it for rollback:
kubectl -n argocd get deploy argocd-server \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
kubectl -n argocd get deploy,sts,svc,cm -o yaml > argocd-workloads-pre-upgrade.yaml
  • Take the backup in §3.

3. Pre-Upgrade Backup

Capture full ArgoCD state to a single file using argocd admin export. The argocd-application-controller-0 pod is the cleanest target — it is a StatefulSet, so its name is predictable, and its ServiceAccount has the right permissions.

3.1 Export Configuration

# (Simple)
kubectl exec -n argocd deploy/argocd-server -- argocd admin export > argocd-backup-$(date +%F).yaml

# (Optional)
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
kubectl exec -n argocd argocd-application-controller-0 -- \
argocd admin export -n argocd > "argocd-backup-${TIMESTAMP}.yaml"

3.2 Verify the Export

ls -lh "argocd-backup-${TIMESTAMP}.yaml"
grep -c '^kind:' "argocd-backup-${TIMESTAMP}.yaml"

You should see a non-zero file size and a count covering your ConfigMaps, Secrets, AppProjects, Applications, and ApplicationSets.

4. Upgrade Procedure

4.1 Apply the Target Version

export ARGOCD_VERSION=v3.4.1
kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/${ARGOCD_VERSION}/manifests/install.yaml

The --server-side --force-conflicts flags are mandatory. Without them you will hit:

CustomResourceDefinition.apiextensions.k8s.io "applicationsets.argoproj.io" is invalid:
metadata.annotations: Too long: may not be more than 262144 bytes

4.2 Re-Apply Customizations

If §2.3 captured custom changes, re-apply your patches/overlays now. --force-conflicts will have reverted any direct edits to fields the upstream manifests own.

5. Post-Upgrade Verification

5.1 Wait for Rollout

kubectl rollout status -n argocd deploy/argocd-server
kubectl rollout status -n argocd deploy/argocd-repo-server
kubectl rollout status -n argocd deploy/argocd-applicationset-controller
kubectl rollout status -n argocd deploy/argocd-dex-server
kubectl rollout status -n argocd deploy/argocd-notifications-controller
kubectl rollout status -n argocd deploy/argocd-redis
kubectl rollout status -n argocd statefulset/argocd-application-controller

All commands should return successfully rolled out.

5.2 Verify Server Version

Check the actual deployed image:

kubectl -n argocd get deploy argocd-server \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
# Expect: quay.io/argoproj/argocd:v3.4.1

5.3 Sanity Test

  • Open the Argo CD UI; confirm the Applications list loads and Sync Status is healthy.
  • Pick one low-risk Application and trigger a manual Refresh (not Sync).
  • Confirm any OutOfSync resources are explained by expected drift — not by tracking-annotation or RBAC regressions introduced by the upgrade.

6. Rollback Procedure

Rollback safety by version distance:

  • Patch downgrades (3.4.1 → 3.4.0): safe.
  • Minor downgrades (3.4 → 3.3): usually safe, but new CRD fields written under the newer version may fail validation under the older one.
  • Major downgrades (3.x → 2.x): not supported. Use restore-from-backup, not downgrade.

6.1 Re-Apply the Previous Version

This handles workloads, RBAC, Services, ConfigMaps, and CRDs.

PREVIOUS_VERSION=v3.2.0   # whatever you recorded in §2.1
kubectl apply -n argocd --server-side --force-conflicts \
-f https://raw.githubusercontent.com/argoproj/argo-cd/${PREVIOUS_VERSION}/manifests/install.yaml

Wait for rollout (use the commands from §5.1).

Existing Applications, AppProjects, and ApplicationSets are stored as CRDs — they survive the binary swap. If a CR contains fields introduced after ${PREVIOUS_VERSION}, those resources are preserved at the API layer but the older controller may log validation warnings. Edit the affected resources to remove the newer fields, or restore them from your backup (§6.2).

6.2 Restore from argocd admin export (If Needed)

Use this only if §6.1 leaves you with corrupted or incompatible CRs.

⚠️ This overwrites in-cluster ArgoCD state with the contents of the backup file. Existing Applications, Projects, and configuration created after the backup will be replaced. Review the backup contents before importing.

# Decrypt if you encrypted the backup
age -d -o argocd-backup-pre-upgrade.yaml argocd-backup-pre-upgrade.yaml.age
# Pipe the file into the application-controller pod
kubectl exec -i -n argocd argocd-application-controller-0 -- \
argocd admin import -n argocd - < argocd-backup-pre-upgrade.yaml

The -i flag keeps stdin open so the file can be piped in.

Useful flags for argocd admin import:

  • --dry-run — preview changes without applying.
  • --prune — delete resources that exist in the cluster but not in the backup. Use with caution; safer to omit on the first attempt.
  • --ignore-tracking — skip tracking annotation reconciliation.

6.3 Restart the Application Controller

After a restore, force a fresh reconciliation pass:

kubectl rollout restart statefulset/argocd-application-controller -n argocd
kubectl rollout status statefulset/argocd-application-controller -n argocd

7. Troubleshooting & Notes

  • Apply fails with Too long: may not be more than 262144 bytes: You forgot --server-side --force-conflicts. Re-run with the flags.
  • Custom modifications disappeared after upgrade: Expected with --force-conflicts. Re-apply your patches (§4.2). Long-term, move toward a Kustomize overlay or Helm install so customizations are tracked and re-applicable.
  • Server-side diff differences after upgrade: v3.4 uses server-side diffing by default. If sync results look different, audit your ignoreDifferences settings — server-side diff is more accurate and may surface drift the old client-side diff hid.
  • Sessions invalidated after upgrade or restore: If argocd-secret (server signing key) was rotated or restored from a different snapshot, all existing JWTs are invalidated. Users must re-login.
  • Stale pods in Error or Completed state: Common on cloud clusters due to spot-node preemption or node auto-repair. The Deployment/StatefulSet has already replaced them; clean up with kubectl delete pod <name> -n argocd once the replacement is Running.
  • CLI not available locally: All argocd admin commands work via kubectl exec into the application-controller pod — no local CLI install required for backup/restore operations.
  • Forgot the admin password and deleted argocd-initial-admin-secret: Patch the bcrypt hash in the argocd-secret directly — see the FAQ entry.

--

--