Learning Cron Job - Cron in Containers & Kubernetes CronJob
Episode 15 of 23

Learning Cron Job - Cron in Containers & Kubernetes CronJob

Running crond inside a container is an anti-pattern born from traditional server habits. This episode explains why that approach is problematic, how to replace it with a simple entrypoint loop or a Kubernetes CronJob batch/v1, and the anatomy of schedule, concurrencyPolicy, and TTL to avoid jobs piling up.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

In episode 14 we made the scripts safe. Now we move context: inside containers and Kubernetes.

When containerizing an application, many teams bring all their old habits — including running crond inside the image. It works, but it creates problems that don't exist on physical machines: an "immortal" container contradicts the container philosophy, and cron inside a container dies the moment the container is restarted. This episode explains why, and what to use instead.

The Problem with Crond Inside a Container

Anti-Pattern: Multiple Processes in One Container

A container should ideally run one main process (PID 1). When you run crond + an app in the same image, you need a process supervisor, complex signal configuration, and a schedule that dies with the container:

  • Container restarted → schedule lost (state isn't persistent).
  • Many container instances → multiple crond processes running the same job, i.e. duplicate jobs.
  • Signals (SIGTERM) aren't propagated to running jobs → processes hang.

Replace It with One Clear Approach

The modern rule of thumb: cron is not a container's responsibility; it's the orchestrator's. Two official replacements:

  1. Entrypoint loop — for long-running standalone containers.
  2. Kubernetes CronJob — for workloads in a cluster.

Entrypoint Loop: Simple Cron in a Container

For an image that runs one repeating job, a loop with sleep in the entrypoint can replace cron:

entrypoint.sh: loop jadwal
#!/bin/sh
set -e
 
while true; do
    /app/run-task.sh
    sleep 3600   # ekivalen jadwal setiap jam
done

With this pattern:

  • PID 1 is the loop — signal handling is correct.
  • No extra daemon.
  • Simple schedule state, easy to understand.

The downside: no precise minute offset without adding sleep logic. For complex needs, go straight to a K8s CronJob.

Note

If you really want cron inside a container (e.g. an image with many schedules), consider supercronic or a similar tool that uses a regular crontab — but run one process per container, and make sure init/signal handling is correct. Still, for most cases, a K8s CronJob is far better.

Kubernetes CronJob: batch/v1

Kubernetes provides CronJob (stable batch/v1 API) that schedules pods periodically — its schedule syntax is exactly crontab.

Basic Manifest

backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup
spec:
  schedule: "30 2 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: myorg/backup-tool:1.4
              args: ["backup", "run"]
  • schedule — the five-field crontab schedule (cluster/kube-controller-manager timezone).
  • concurrencyPolicy — what happens when two schedules meet: Allow, Forbid, or Replace. Forbid is the flock equivalent in the K8s world (episode 9).
  • restartPolicy — a pod in a job must be OnFailure or Never.

Preventing Job Pile-Up: History Limits and TTL

Each CronJob execution creates a Job and a Pod. Without cleanup, they pile up forever:

Batas history dan TTL
spec:
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      ttlSecondsAfterFinished: 3600
  • successfulJobsHistoryLimit / failedJobsHistoryLimit — how many Jobs to keep.
  • ttlSecondsAfterFinished — Jobs are deleted automatically a while after they finish.

Comparison with Cron on a Host

AspectHost cronK8s CronJob
DeploymentPer-hostAcross the cluster
Fault toleranceDepends on the hostPods restarted, schedule persistent
IsolationUser/rootNamespace/SA/RBAC
ConcurrencyManual flockconcurrencyPolicy
CleanupManualHistory limits + TTL
SecretsEnv file/VaultK8s Secrets + Vault

Warning

Don't run multiple crond processes across several container instances executing the same job — that's a recipe for duplication and data corruption. In K8s, concurrencyPolicy: Forbid + history limits is the modern equivalent of the flock + retention we built in episode 9.

First Practical Steps

KubernetesTerapkan dan uji CronJob
kubectl apply -f backup-cronjob.yaml
kubectl get cronjob backup
kubectl get jobs -l job-name=backup
kubectl logs job/backup-xxxxxxxx --tail=20

To test without waiting for the schedule, we'll cover manual triggering and advanced features (startingDeadlineSeconds, suspend, parallelism) in episode 19.

Closing

Key takeaways:

  • crond inside a container is an anti-pattern: multi-process, lost state, duplicate jobs.
  • Replace it with an entrypoint loop for standalone containers.
  • The K8s CronJob batch/v1 is the modern replacement for cluster workloads.
  • concurrencyPolicy: Forbid replaces flock; history limits + TTL prevent pile-up.
  • Use one scheduling mechanism — don't mix host crond and K8s for the same job.

In episode 16 we'll cover timezone and DST handlingCRON_TZ=Asia/Jakarta in crontab, the impact of Daylight Saving Time on daily jobs, and best practices for using UTC across infrastructure!