Kubernetes
Helm
imagePullSecrets
GCR
containerization

How to write a chart for imagePullSecret from gcr

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If a Helm chart needs to pull private images from GCR, the chart usually should not hardcode one secret name and hope every cluster matches it. A better design is to let the chart either reference an existing imagePullSecret or create one from provided registry credentials, then wire that secret into the pod spec.

Prefer an Existing Secret When Possible

In many production clusters, the image pull secret is created outside the application chart by platform tooling, GitOps, or external secret management. Your chart should support that directly.

A practical values.yaml shape is:

yaml
1image:
2  repository: gcr.io/example-project/my-app
3  tag: "1.2.3"
4  pullPolicy: IfNotPresent
5
6imagePullSecrets:
7  - name: gcr-registry

Then in the deployment template:

yaml
1spec:
2  containers:
3    - name: app
4      image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
5      imagePullPolicy: {{ .Values.image.pullPolicy }}
6  imagePullSecrets:
7    {{- toYaml .Values.imagePullSecrets | nindent 4 }}

This is the simplest and most reusable pattern.

Optional Secret Creation Inside the Chart

If you do want the chart to create the secret, make it opt-in and explicit. One common pattern is to accept a base64-encoded Docker config JSON or the raw JSON key material used to build one.

values.yaml:

yaml
1registrySecret:
2  create: false
3  name: gcr-registry
4  dockerconfigjson: ""

Secret template:

yaml
1{{- if .Values.registrySecret.create }}
2apiVersion: v1
3kind: Secret
4metadata:
5  name: {{ .Values.registrySecret.name }}
6type: kubernetes.io/dockerconfigjson
7data:
8  .dockerconfigjson: {{ .Values.registrySecret.dockerconfigjson | quote }}
9{{- end }}

Then the pod can reference either the created secret or a pre-existing one with the same name.

Reference the Secret from the Workload

The secret does nothing unless the pod spec references it.

yaml
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4  name: {{ include "mychart.fullname" . }}
5spec:
6  template:
7    spec:
8      imagePullSecrets:
9        - name: {{ .Values.registrySecret.name }}
10      containers:
11        - name: app
12          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

If your chart supports both created and existing secrets, make sure the name is the single source of truth so you do not duplicate configuration paths unnecessarily.

Keep Secret Handling Out of Plaintext Defaults

Do not commit real registry credentials into values.yaml. The chart should define the schema, not the secret value itself.

Good patterns are:

  • pass secret values at deploy time
  • use encrypted values files
  • use external secret operators
  • reference an existing secret managed elsewhere

For most teams, "chart references an existing secret" is the safest default.

GCR and Artifact Registry Note

Many clusters now use Artifact Registry rather than older GCR flows, but the Kubernetes-side behavior is the same: the pod needs credentials in a Docker registry secret format unless workload identity or another platform integration already solves it.

That means the Helm chart design still holds even if the backing registry endpoint changes.

Template Guardrails

It is worth adding a small condition so the deployment only renders imagePullSecrets when values are actually provided.

yaml
1spec:
2  {{- if .Values.imagePullSecrets }}
3  imagePullSecrets:
4    {{- toYaml .Values.imagePullSecrets | nindent 4 }}
5  {{- end }}
6  containers:
7    - name: app
8      image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

That keeps the manifest clean for clusters that use only public images.

Common Pitfalls

The biggest mistake is creating the secret but never referencing it in the pod spec. Kubernetes will not use it automatically.

Another issue is storing real registry credentials directly in the chart repository. That turns a deployment convenience into a credential-leak risk.

A third problem is hardcoding one secret name without making it configurable, which makes the chart painful to reuse across namespaces and clusters.

Summary

  • Let the chart reference an existing imagePullSecret by default.
  • Support chart-managed secret creation only as an explicit option.
  • Wire the secret into the pod imagePullSecrets field.
  • Keep registry credentials out of committed plaintext values files.
  • Design the chart so the secret name is configurable and reusable across environments.

Course illustration
Course illustration

All Rights Reserved.