DRY Kubernetes: Deploying Job and CronJob with YAML Anchors and List
Managing both a one-time Job and a recurring CronJob in Kubernetes often leads to duplicated specs. In most cases, they share the same container image, environment variables, volumes, and execution logic — so why repeat yourself?
This article shows how to eliminate duplication by using:
- YAML anchors (
&and<<:) for reusable specs - Kubernetes
Listkind to group related resources into a single manifest
Why This Pattern?
You may want both:
- A
Jobyou can trigger manually (e.g., ad hoc sync or debug run) - A
CronJobthat runs on a schedule (e.g., nightly mirror)
But you don’t want to maintain the full spec.template twice.
The YAML Pattern
📄 base/job-cron.yaml
apiVersion: v1
kind: List
items:
- apiVersion: batch/v1
kind: Job
metadata:
name: custom-job
spec: &jobSpec
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: runner
image: alpine
command: ["sh", "-c", "echo Running job"]
- apiVersion: batch/v1
kind: CronJob
metadata:
name: custom-cron
spec:
schedule: "0 0 * * *"
jobTemplate:
spec:
<<: *jobSpec
How It Works
&jobSpecdefines an anchor at theJob.speclevel.<<: *jobSpecuses the anchor to inject that structure into theCronJob’sjobTemplate.spec.kind: Listbundles the two resources into a single file you cankubectl apply -f.
Using Kustomize to control Cronjob and Job
📄 base/kustomization.yaml
resources:
- job-cron.yaml
Example of deletion of Job to keep CronJob 📄 overlays/prod/kustomization.yaml
resources:
- ../../base
patches:
- target:
kind: Job
name: custom-job
patch: |-
apiVersion: batch/v1
kind: Job
metadata:
name: custom-job
$patch: delete
You folder structure will look like this.
📁 custom-job-kustomize/
├── 📁 base/
│ ├── job-cron.yaml
│ └── kustomization.yaml
├── 📁 overlays/
│ └── 📁 prod/
│ └── kustomization.yaml
Why Use This?
- Avoids drift between
JobandCronJobdefinitions - Improves maintainability and readability
- Works well in GitOps and Kustomize bases
Gotchas
- YAML anchors don’t work across separate files (unless you use a YAML preprocessor).
- The syntax is pure YAML — not a Kubernetes-specific feature — so it’s resolved before reaching the API server.
- Editors and linters may complain unless configured properly.