ArgoCD Silently Skips Helm Hooks — Your Post-Install Jobs Never Run

Apr 22, 2026

I deployed MinIO via ArgoCD using the official Helm chart. The chart includes a post-install Job that creates default buckets. ArgoCD synced MinIO successfully — all pods Running, Application green. The bucket never existed.

Thanos storegateway kept crashing with a 503 readiness probe failure because it couldn’t initialize its bucket store. There was no obvious error pointing back to MinIO. The bucket was just missing.


Why the Job Never Ran

ArgoCD does not execute Helm hooks.

When ArgoCD renders a Helm chart, it strips any resource annotated with helm.sh/hook. Post-install Jobs, pre-delete hooks, test hooks — all of them are ignored. ArgoCD only manages the steady-state resources: Deployments, Services, ConfigMaps, PVCs. Anything the chart author intended to run imperatively during Helm’s install lifecycle gets silently dropped.

This is documented behavior, but it’s easy to miss if you’re coming from helm install. With helm install, hooks run automatically as part of the release. With ArgoCD, hooks don’t exist.

The MinIO chart’s bucket creation Job has exactly this annotation:

annotations:
  helm.sh/hook: post-install
  helm.sh/hook-weight: "5"
  helm.sh/hook-delete-policy: hook-succeeded

ArgoCD sees this annotation and discards the resource. The Job never appears in the cluster. Bucket never gets created.

What Doesn’t Work

Removing the hook annotation and letting ArgoCD manage the Job as a regular resource doesn’t work — the Job fires immediately on first sync before MinIO is Ready, then succeeds or fails and never retries. It’s also not idempotent across cluster rebuilds.

Adding a sync-wave annotation to delay the Job doesn’t help either. The hook annotation causes ArgoCD to ignore the resource entirely — sync-wave annotations on a hook-annotated resource have no effect because the resource is never queued.

What Works: Imperative Initialization in Bootstrap

The right pattern is to accept that ArgoCD owns declarative state and move imperative initialization outside ArgoCD — into whatever runs after ArgoCD has synced.

In activate.yaml (the Ansible playbook that pushes manifests and activates the GitOps loop), I added two tasks at the end:

- name: Wait for MinIO pod to be ready
  ansible.builtin.command:
    cmd: >
      kubectl --kubeconfig={{ kubeconfig }}
      wait pod -n monitoring -l app=minio
      --for=condition=Ready --timeout=600s
  retries: 10
  delay: 30
  register: minio_wait
  until: minio_wait.rc == 0

- name: Create MinIO thanos bucket (idempotent)
  ansible.builtin.shell:
    cmd: |
      POD=$(kubectl --kubeconfig={{ kubeconfig }} get pod -n monitoring \
            -l app=minio -o jsonpath='{.items[0].metadata.name}')
      kubectl --kubeconfig={{ kubeconfig }} exec -n monitoring "$POD" -- \
        sh -c 'mc alias set local http://localhost:9000 \
               "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" >/dev/null 2>&1 && \
               (mc ls local/thanos >/dev/null 2>&1 || mc mb local/thanos)'
  register: bucket_result
  changed_when: "'Bucket created' in bucket_result.stdout"

This runs after ArgoCD activates, waits for MinIO to be Ready, and creates the bucket using MinIO’s own CLI (mc) already embedded in the MinIO pod. The mc ls local/thanos || mc mb local/thanos pattern makes it idempotent — make up can run repeatedly without failing.

Note: it reads credentials from the pod’s own environment variables ($MINIO_ROOT_USER, $MINIO_ROOT_PASSWORD) rather than hardcoding them. The credentials are already in the pod from the Kubernetes Secret managed by ESO.

The Broader Pattern

Any Helm chart that uses helm.sh/hook for initialization will silently skip that work under ArgoCD:

The pattern that works in all cases: initialize imperatively in bootstrap (Ansible, a shell script, whatever runs once after the cluster comes up), keep the ArgoCD Application managing only the persistent resources.

If the initialization is complex enough to warrant a Kubernetes Job, run it via kubectl create job from your bootstrap tooling — not as a Helm hook.

Takeaway

ArgoCD strips helm.sh/hook resources. If a chart depends on post-install Jobs to be functional, those Jobs will never run. Watch for charts that create buckets, schemas, or config at install time — any of those that use hooks will silently fail. Move initialization to your bootstrap layer and make it idempotent.