Back to guides

Guides · May 6, 2026

Docker cron jobs: the four patterns and how to pick the right one

Running cron inside a container works in dev and breaks in prod more often than teams expect. Four patterns, one decision question, and working examples for Compose and external HTTP cron.
crontap.com / guides
Cron inside a container, sidecar cron, Kubernetes CronJob, or external HTTP cron. A decision framework for Docker Compose, Railway, and production.

Running cron inside a Docker container is one of those patterns that works in dev and breaks in prod. Timezone drift, lost crontab on rebuild, zombie processes, and images that grow every layer.

There are four ways to schedule jobs against Docker. This post is the decision framework, with examples for Docker Compose, Railway, and Kubernetes.

The deciding question: should the container image stay single-purpose (run the app only), or is it acceptable to bundle a scheduler into the same image?

Pattern 1: cron inside the app container

Install cron in the Dockerfile, copy a crontab file, run cron -f alongside your app via a shell entrypoint.

Why it is brittle:

  • Crontab state lives in the container filesystem; rebuilds wipe it unless you bake it into the image.
  • Two processes in one container (app + cron) fight process supervision.
  • Logs go to syslog inside the container, not your centralized logging by default.
  • Health checks report the web server, not whether cron fired.

Pick this only for: local dev, demos, or legacy lift-and-shift where nobody will touch it for six months.

Pattern 2: Cron sidecar container

In Docker Compose, run a second service that only runs cron and shares volumes or calls the app over the network.

services:
  app:
    build: .
    ports:
      - "8000:8000"
 
  cron:
    image: alpine
    volumes:
      - ./crontab:/etc/crontabs/root
    command: crond -f -l 2

The sidecar runs curl http://app:8000/internal/job on schedule.

Pick this when: you need in-cluster scheduling without an external SaaS and you accept operating two containers per stack.

Pattern 3: Kubernetes CronJob

If you already run on Kubernetes, use a native CronJob resource. Kubernetes spawns a Job pod on schedule.

Pick this when: k8s is the platform and ops owns cluster YAML. Skip when: you are on Compose, Railway, or Fly without k8s.

Pattern 4: External HTTP cron (Crontap)

Keep the app container single-purpose. Expose POST /internal/run-job with auth. Crontap fires it from outside the cluster.

Why this is the cleanest separation

  • The image does not need cron installed.
  • Deploys do not touch schedule state.
  • Retries and failure alerts live in Crontap, not in container logs.
  • Works the same on Railway, Fly, Render, and your own VPS.

Docker Compose + Crontap walkthrough

docker-compose.yml:

services:
  api:
    build: .
    environment:
      CRON_SECRET: ${CRON_SECRET}
    ports:
      - "8000:8000"

Your app verifies Authorization: Bearer ${CRON_SECRET} on /internal/nightly.

In Crontap:

  • URL: https://your-tunnel-or-domain:8000/internal/nightly (or production URL)
  • Method: POST
  • Cron: 0 2 * * *
  • Timezone: your business timezone

Railway + Crontap

Railway does not ship a first-class cron for arbitrary HTTP services. Point Crontap at your Railway public URL. See cron jobs for Railway.

Crontap fires HTTP cron from outside the container. Your container stays single-purpose. Your bill tracks actual work, not an always-on clock process.

Fix this in 60 seconds with Crontap. Free forever tier. Three schedules. No credit card. Schedule your first job →

Docker HEALTHCHECK vs Crontap heartbeat

Docker's built-in HEALTHCHECK asks whether the container responds. It does not know your batch job ran at 2am. For calendar jobs, use either:

FAQ

Can I use docker compose run from host cron?

Yes: host crontab runs docker compose run --rm api python manage.py nightly. That couples scheduling to the host VM and breaks on multi-node deploys. External HTTP cron is usually simpler.

Does Crontap work with private containers?

Crontap needs a reachable HTTPS URL. Use a public URL with auth, or a tunnel for dev. Internal-only services need a small public ingress or VPN bridge.

What about Docker Swarm cron?

Swarm mode has no native cron. Pattern 4 or k8s-style orchestration still applies.

Related on Crontap

Fix this in 60 seconds with Crontap. Free forever tier. Three schedules. No credit card. Schedule your first job →

From the blog

Read the blog

Guides, patterns and product updates.

Tutorials on scheduling API calls, webhooks and automations, plus deep dives into cron syntax, timezones and reliability.

Alternatives

Vercel Cron every minute: beating the Hobby hourly limit

Vercel Cron caps Hobby at hourly cadence and 5 jobs, and ties every change to a redeploy. Here is the external cron pattern teams use to ship per-minute schedules, per-IANA timezones, and one dashboard across projects without paying $20/mo per user for Pro.

Alternatives

Cloud Run cron without Cloud Scheduler

Cloud Scheduler costs $0.10 per job per month after the first 3 and asks for OIDC plus IAM bindings on every target. Here is the IAM-free pattern Cloud Run teams use to fire their .run.app URLs on a clock with one bearer token and one dashboard across every GCP project.