Install and expose ArgoCD
Our clusters ship without ArgoCD installed. Running your own is useful if you want to have full control over GitOps delivery into your cluster.
The guide is based on the upstream Argo CD Helm chart (argo/argo-cd).
Prerequisites
Helm needs to be provided with the correct repository:
-
Setup helm repo
helm repo add argo https://argoproj.github.io/argo-helm -
Make sure to update repo cache
helm repo update -
Make sure
kubectlis configured for the cluster you want to install into. -
To reach the ArgoCD UI over HTTPS you need something to route external traffic into the cluster and cert-manager to issue the certificate. This guide keeps the ArgoCD side of that independent of which controller you run, and leaves the controller-specific parts to the guide for the controller you picked. You can use our managed add-ons or install your own:
Install
-
Run Helm install, pinning an explicit chart version.
--create-namespacecreates theargocdnamespace for you:helm install argocd argo/argo-cd \ --namespace argocd \ --create-namespace \ --version 10.1.4A full list of available Helm values is on ArgoCD’s ArtifactHub page.
Note: If you already know you want the UI reachable via Ingress, you can supply a
values.yamlup front instead of runninghelm upgradeafterwards, see the Expose the ArgoCD UI section below.
Verify the installation
-
Check that all pods are running:
kubectl get pods -n argocd -
Retrieve the initial admin password:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -dUpstream recommends deleting this secret once you have changed the admin password.
-
Log in with the ArgoCD CLI over a port-forward:
kubectl port-forward svc/argocd-server -n argocd 8080:443 argocd login localhost:8080 --username admin --insecureNote: On a fresh install the ArgoCD server presents its own self-signed certificate, so the CLI needs
--insecure(or an interactive confirmation) to connect. Once you expose the server through the Ingress below, log in against that hostname with--grpc-webinstead, and drop--insecure.
Expose the ArgoCD UI
-
Interim access
Before you have decided on a hostname, reach the UI with a port-forward:
kubectl port-forward svc/argocd-server -n argocd 8080:443The UI is then available at https://localhost:8080.
Note: Both service ports forward to the same container port, so the scheme follows how the server is configured, not the port you forward. Use
https://localhost:8080on a default install, andhttp://localhost:8080once you have setserver.insecure: trueas described below. -
Decide where TLS terminates
The ArgoCD server multiplexes gRPC and HTTP on a single port and terminates TLS itself by default. You therefore have to choose one of two arrangements before writing any routing configuration:
- Terminate TLS at your ingress. Set
server.insecure: trueso the server serves plain HTTP behind it. This is the simpler option and what the examples below assume. - Keep TLS on the backend. Leave the server as it is and configure your ingress to pass TLS through untouched. How that is expressed depends entirely on which ingress you run.
configs: params: server.insecure: trueThe upstream ArgoCD ingress documentation covers both arrangements in detail.
- Terminate TLS at your ingress. Set
-
Route traffic to the server
The chart can create the routing resource for you in either model. Pick the one your controller implements: Gateway API if you run something like Envoy Gateway,
Ingressif you run an ingress controller such as ingress-nginx.Gateway API. Point an
HTTPRouteat an existingGateway:server: httproute: enabled: true parentRefs: - name: <your-gateway> namespace: <gateway-namespace> sectionName: https hostnames: - argocd.example.tldAdd a
GRPCRoutealongside it if you want the CLI to speak plain gRPC rather than tunnelling it over HTTP:server: grpcroute: enabled: true parentRefs: - name: <your-gateway> namespace: <gateway-namespace> sectionName: https hostnames: - argocd.example.tldNote: With Gateway API the certificate belongs to the
Gatewaylistener, not to the route, so there is no cert-manager annotation to set here. Configure TLS where theGatewayis defined.Ingress. If your controller uses
Ingressresources instead:server: ingress: enabled: true ingressClassName: <your-ingress-class> hostname: argocd.example.tld annotations: cert-manager.io/issuer: letsencrypt-prod tls: trueNote: Keep all annotations in one block. A second
server:section further down the samevalues.yamldoes not merge with the first one, so earlier annotations would be lost.Either way, apply the values with:
helm upgrade --install argocd argo/argo-cd --namespace argocd --version 10.1.4 --values values.yaml -
Certificates on the
IngresspathIf you routed with an
HTTPRoute, skip this. The certificate is attached to theGatewaylistener and is configured wherever thatGatewaylives.cert-manager.io/issuerrefers to anIssuer, which is namespaced. Theletsencrypt-prodIssuer from our Ingress guide has to exist in theargocdnamespace, so apply it withkubectl apply -f issuer.yaml -n argocd. To keep a single issuer for the whole cluster, create it as aClusterIssuerand use thecert-manager.io/cluster-issuerannotation instead.Note: Our managed cert-manager add-on installs the controller and its CRDs only. It does not create any
IssuerorClusterIssuerfor you, so one of the above is always required. Without it the certificate staysFalseand cert-manager reportsReferenced "Issuer" not found. -
Using the ArgoCD CLI against the exposed server
If you enabled a
GRPCRoute, the CLI can speak gRPC directly and needs no extra flag:argocd login argocd.example.tldOn the
Ingresspath there is no equivalent. AnIngresscarries one backend protocol and the route above serves the UI over HTTP, so the CLI has to tunnel gRPC over HTTP instead:argocd login argocd.example.tld --grpc-web--grpc-webworks through anything that terminates HTTP, so it is the safe choice if you are unsure. Serving plain gRPC through anIngressmeans a secondIngresson its own hostname with a controller-specific backend-protocol setting, which is one of the things Gateway API removes the need for. -
Restricting who can reach it
ArgoCD holds credentials for every repository it syncs and is a deployment path into your cluster, so restrict access before pointing a public hostname at it.
The control that does not depend on your ingress controller is the whitelist on the OpenStack load balancer in front of the cluster. It is part of your cluster configuration rather than something inside the cluster, so contact our support with the source ranges that should be allowed and we will apply it. If you run your own ingress controller, the equivalent is
loadBalancerSourceRangeson itsLoadBalancerservice.Note: Your ingress controller may also offer a source-range restriction of its own, such as
nginx.ingress.kubernetes.io/whitelist-source-range. It matches on the client address as the controller sees it, which is the real client address only when the controller is given it over PROXY protocol. Whether that holds depends on how the ingress in your cluster is set up, so check with our support before relying on the annotation. Treat it as defence in depth behind a load balancer restriction, never as your only control.
Connect a private Git repository
Add a repository with credentials via the CLI:
argocd repo add <repo-url> --username <user> --password <token>
For Azure DevOps, use a Personal Access Token scoped to Code: Read, or an SSH deploy key instead of username/password.
Alternatively, declare the repository as a Secret labelled for ArgoCD:
apiVersion: v1
kind: Secret
metadata:
name: private-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
stringData:
url: <repo-url>
username: <user>
password: <token>
Note: Avoid committing credentials in plaintext to git. See Managing secrets below for options to keep this manifest encrypted at rest in your repository.
Pull images from a private registry
Create an imagePullSecret in the namespace your application is deployed to:
kubectl create secret docker-registry regcred \
--namespace <app-namespace> \
--docker-server=<registry-server> \
--docker-username=<user> \
--docker-password=<password>
This works for any registry (Docker Hub, ACR, GCR, Quay, …). Reference the secret from your
workload’s ServiceAccount imagePullSecrets, or set it via your Helm chart’s values.
Managing secrets
Since ArgoCD deploys straight from git, secrets should not be committed in plaintext. Two common options, both customer-installed:
- Sealed Secrets encrypts secrets client-side
with
kubesealso only the controller in your cluster can decrypt them, safe to commit to git. - External Secrets syncs secrets from an external secret store
(e.g. Vault, cloud provider secret managers) into Kubernetes
Secretresources.
Deploy your first application
Create a file called application.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: <repo-url>
targetRevision: HEAD
path: <path-to-manifests>
destination:
server: https://kubernetes.default.svc
namespace: <app-namespace>
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Note: ArgoCD does not create the destination namespace on its own, so a sync into a namespace that does not exist fails.
CreateNamespace=truelets ArgoCD create it. Drop the option if you prefer to create the namespace yourself.
If you’re deploying a Helm chart instead of plain manifests, use a Helm source:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: <repo-url>
targetRevision: HEAD
path: <path-to-chart>
helm:
valueFiles:
- values.yaml
destination:
server: https://kubernetes.default.svc
namespace: <app-namespace>
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Then create the resource in the cluster by running:
kubectl apply -f application.yaml
Upgrade
helm upgrade --install argocd argo/argo-cd --namespace argocd --version <new-version>
Uninstall
-
Remove your
Applicationresources firstkubectl delete applications --all -n argocdNote: Applications that carry the
resources-finalizer.argocd.argoproj.iofinalizer need the ArgoCD controller running to be cleaned up. Delete them before uninstalling the release, or the namespace gets stuck inTerminatingwith nothing left to clear the finalizer. Without that finalizer the workloads ArgoCD deployed are left running, which is usually what you want when you are only replacing ArgoCD itself. -
Remove the release
helm uninstall argocd -n argocd -
Remove the namespace if necessary
kubectl delete namespace argocd -
Remove the CRDs if you are not reinstalling
kubectl delete crd applications.argoproj.io applicationsets.argoproj.io appprojects.argoproj.ioNote: The chart sets
crds.keep: true, sohelm uninstalldeliberately leaves the CRDs in place to protect your resources during an upgrade. They are cluster-scoped and outlive the namespace. Deleting a CRD deletes every resource of that type in the cluster, so only do this once you are sure ArgoCD is going away for good.