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.

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.
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:
The modern rule of thumb: cron is not a container's responsibility; it's the orchestrator's. Two official replacements:
For an image that runs one repeating job, a loop with sleep in the entrypoint can replace cron:
#!/bin/sh
set -e
while true; do
/app/run-task.sh
sleep 3600 # ekivalen jadwal setiap jam
doneWith this pattern:
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 provides CronJob (stable batch/v1 API) that schedules pods periodically — its schedule syntax is exactly crontab.
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.Each CronJob execution creates a Job and a Pod. Without cleanup, they pile up forever:
spec:
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
ttlSecondsAfterFinished: 3600successfulJobsHistoryLimit / failedJobsHistoryLimit — how many Jobs to keep.ttlSecondsAfterFinished — Jobs are deleted automatically a while after they finish.| Aspect | Host cron | K8s CronJob |
|---|---|---|
| Deployment | Per-host | Across the cluster |
| Fault tolerance | Depends on the host | Pods restarted, schedule persistent |
| Isolation | User/root | Namespace/SA/RBAC |
| Concurrency | Manual flock | concurrencyPolicy |
| Cleanup | Manual | History limits + TTL |
| Secrets | Env file/Vault | K8s 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.
kubectl apply -f backup-cronjob.yaml
kubectl get cronjob backup
kubectl get jobs -l job-name=backup
kubectl logs job/backup-xxxxxxxx --tail=20To test without waiting for the schedule, we'll cover manual triggering and advanced features (startingDeadlineSeconds, suspend, parallelism) in episode 19.
Key takeaways:
crond inside a container is an anti-pattern: multi-process, lost state, duplicate jobs.batch/v1 is the modern replacement for cluster workloads.concurrencyPolicy: Forbid replaces flock; history limits + TTL prevent pile-up.In episode 16 we'll cover timezone and DST handling — CRON_TZ=Asia/Jakarta in crontab, the impact of Daylight Saving Time on daily jobs, and best practices for using UTC across infrastructure!