Integrate ArgoCD with External Secrets Operator for secure Kubernetes secret management

Advanced 45 min Jun 12, 2026
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up External Secrets Operator to sync secrets from HashiCorp Vault and AWS Secrets Manager into your ArgoCD GitOps workflow, enabling secure automated secret management across multiple environments without storing sensitive data in Git repositories.

Prerequisites

  • Kubernetes cluster with kubectl access
  • ArgoCD installed
  • HashiCorp Vault or AWS Secrets Manager access
  • Helm 3.x installed

What this solves

Managing secrets in GitOps workflows presents a security challenge: you need secrets in Kubernetes but can't store them in Git repositories. The External Secrets Operator (ESO) bridges this gap by automatically synchronizing secrets from external systems like HashiCorp Vault and AWS Secrets Manager into your cluster. This tutorial shows you how to integrate ESO with ArgoCD for secure, automated secret management across multiple environments.

Prerequisites

You'll need a working Kubernetes cluster with ArgoCD installed and either HashiCorp Vault or AWS Secrets Manager access. If you haven't set up ArgoCD yet, check our ArgoCD installation guide.

Step-by-step installation

Install External Secrets Operator

Deploy the External Secrets Operator using Helm, which provides the most flexible installation method.

helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets -n external-secrets-system --create-namespace

Verify operator installation

Check that all ESO components are running correctly before proceeding.

kubectl get pods -n external-secrets-system
kubectl get crd | grep external-secrets

Create HashiCorp Vault SecretStore

Configure a SecretStore that connects to your HashiCorp Vault instance. This example uses Kubernetes authentication.

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: argocd
spec:
  provider:
    vault:
      server: "https://vault.example.com:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets"
          serviceAccountRef:
            name: "external-secrets-sa"

Create AWS Secrets Manager SecretStore

Alternative SecretStore configuration for AWS Secrets Manager using IAM roles.

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-backend
  namespace: argocd
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa

Set up service account and RBAC

Create the service account and necessary permissions for External Secrets Operator.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-secrets-sa
  namespace: argocd
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/ExternalSecretsRole
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: external-secrets-role
  namespace: argocd
rules:
  • apiGroups: [""]
resources: ["secrets"] verbs: ["create", "update", "get", "list", "watch"]

Apply the configurations

Deploy all the External Secrets Operator configurations to your cluster.

kubectl apply -f rbac.yaml
kubectl apply -f vault-secret-store.yaml

OR if using AWS

kubectl apply -f aws-secret-store.yaml

Step-by-step secret synchronization

Create an ExternalSecret resource

Define which secrets to sync from your external provider into Kubernetes.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: argocd-repo-creds
  namespace: argocd
spec:
  refreshInterval: 30s
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: argocd-repo-server-tls-certs-secret
    creationPolicy: Owner
  data:
  - secretKey: username
    remoteRef:
      key: argocd/repo-credentials
      property: username
  - secretKey: password
    remoteRef:
      key: argocd/repo-credentials
      property: password

Configure ArgoCD repository credentials

Create an ExternalSecret that manages ArgoCD's private repository credentials.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: private-repo-creds
  namespace: argocd
spec:
  refreshInterval: 60s
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: private-repo
    creationPolicy: Owner
    template:
      type: Opaque
      metadata:
        labels:
          argocd.argoproj.io/secret-type: repository
      data:
        type: git
        url: https://github.com/example/private-repo
        username: "{{ .username }}"
        password: "{{ .password }}"
  data:
  - secretKey: username
    remoteRef:
      key: github/credentials
      property: username
  - secretKey: password
    remoteRef:
      key: github/credentials
      property: token

Set up application secrets

Create ExternalSecrets for application-specific secrets that ArgoCD will deploy.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: database-credentials
  namespace: production
spec:
  refreshInterval: 300s
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: postgres-credentials
    creationPolicy: Owner
  data:
  - secretKey: POSTGRES_USER
    remoteRef:
      key: database/production
      property: username
  - secretKey: POSTGRES_PASSWORD
    remoteRef:
      key: database/production
      property: password
  - secretKey: POSTGRES_DB
    remoteRef:
      key: database/production
      property: database

Apply secret configurations

Deploy the ExternalSecret resources to start automatic synchronization.

kubectl apply -f external-secret.yaml
kubectl apply -f argocd-repo-secret.yaml
kubectl apply -f app-secrets.yaml

ArgoCD GitOps integration

Create ArgoCD Application with External Secrets

Configure ArgoCD to manage ExternalSecret resources as part of your GitOps workflow.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: external-secrets-config
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/example/k8s-config
    targetRevision: HEAD
    path: external-secrets/
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Configure sync waves for proper ordering

Use ArgoCD sync waves to ensure ExternalSecrets are created before applications that depend on them.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-config
  namespace: production
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
spec:
  refreshInterval: 300s
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: app-config-secret
    creationPolicy: Owner
  data:
  - secretKey: api-key
    remoteRef:
      key: application/config
      property: api-key

Deploy application with dependency

Create an application deployment that uses the synchronized secrets.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: production
  annotations:
    argocd.argoproj.io/sync-wave: "0"
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: nginx:1.21
        env:
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: app-config-secret
              key: api-key
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: postgres-credentials
              key: POSTGRES_USER

Multi-environment configuration

Create ClusterSecretStore for shared access

Use ClusterSecretStore when multiple namespaces need access to the same secret backend.

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-cluster-backend
spec:
  provider:
    vault:
      server: "https://vault.example.com:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets-cluster"
          serviceAccountRef:
            name: "external-secrets-sa"
            namespace: "external-secrets-system"

Configure environment-specific secrets

Create ExternalSecrets that pull different values based on environment using path templating.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: env-database-creds
  namespace: staging
spec:
  refreshInterval: 300s
  secretStoreRef:
    name: vault-cluster-backend
    kind: ClusterSecretStore
  target:
    name: database-credentials
    creationPolicy: Owner
  data:
  - secretKey: username
    remoteRef:
      key: database/staging/postgres
      property: username
  - secretKey: password
    remoteRef:
      key: database/staging/postgres
      property: password

Apply multi-environment configuration

Deploy the cluster-wide and environment-specific configurations.

kubectl apply -f cluster-secret-store.yaml
kubectl apply -f env-specific-secret.yaml

Verify your setup

Test that External Secrets Operator is properly synchronizing secrets and ArgoCD can access them.

# Check ESO operator status
kubectl get pods -n external-secrets-system

Verify SecretStore connection

kubectl describe secretstore vault-backend -n argocd

Check ExternalSecret synchronization

kubectl get externalsecrets -n argocd kubectl describe externalsecret argocd-repo-creds -n argocd

Verify secrets were created

kubectl get secrets -n argocd | grep argocd-repo kubectl get secrets -n production | grep postgres-credentials

Check ArgoCD can access private repositories

kubectl get applications -n argocd kubectl describe application external-secrets-config -n argocd
Security note: Never use kubectl get secret -o yaml to view secret contents in production logs or CI/CD output, as this exposes sensitive data in plaintext.

Advanced configuration

Configure secret rotation and refresh

Set up automatic secret rotation with shorter refresh intervals for critical secrets.

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: rotating-api-key
  namespace: production
spec:
  refreshInterval: 30s
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: api-credentials
    creationPolicy: Owner
    deletionPolicy: Retain
  data:
  - secretKey: api-key
    remoteRef:
      key: rotating/api-credentials
      property: current-key

Set up monitoring and alerts

Configure monitoring for External Secrets Operator to track synchronization health.

apiVersion: v1
kind: ServiceMonitor
metadata:
  name: external-secrets-metrics
  namespace: external-secrets-system
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: external-secrets
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Common issues

SymptomCauseFix
ExternalSecret stuck in "SecretSyncError"Invalid credentials or vault pathCheck SecretStore configuration and vault permissions
ArgoCD can't access private repositoryRepository secret not properly labeledAdd argocd.argoproj.io/secret-type: repository label
Secrets not refreshingService account lacks permissionsVerify RBAC configuration and vault policies
Application pods failing with secret mount errorsSecret not synchronized before pod creationUse ArgoCD sync waves to order resource creation
ClusterSecretStore connection failingService account in wrong namespaceEnsure service account exists in external-secrets-system namespace

Security best practices

Follow these security guidelines when integrating External Secrets Operator with ArgoCD for production use.

Important: Always use least-privilege access when configuring vault policies and Kubernetes RBAC. Grant only the minimum permissions required for each component to function.
  • Configure vault policies to restrict access to specific secret paths per environment
  • Use different service accounts for different environments and applications
  • Set appropriate refresh intervals based on secret sensitivity
  • Monitor External Secrets Operator logs for failed authentication attempts
  • Regularly rotate service account tokens and vault authentication credentials

Next steps

Running this in production?

Want this handled for you? Running this at scale adds a second layer of work: capacity planning, failover drills, cost control, and on-call. Our managed platform covers monitoring, backups and 24/7 response by default.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.