How to run a cron job inside a docker container

By: Shane Harter|Last Updated: Feb 05, 2024

Millions of developers use Docker for local development, hosted CI/CD pipelines, and production deployments. Docker can be used for just about everything, and no surprise, that includes cron jobs. To run a cron job inside a docker container, first determine whether you will continue to use the host crontab, or if you will have the jobs and crontab both run inside the container. This guide will show you both approaches.

Using Docker with your host crontab

When your cron jobs are part of a larger codebase they usually need access to the same dependencies, secrets and environment variables as the application itself. If this environment is already defined in your Dockerfiles and containers, it's clear that you need to run the cron jobs themselves within the containers too. In this first example, I will show you how to run your jobs inside a container with the crontab entries themselves added to the host crontab.

Imagine an application with this hypothetical directory structure:

acme-products-storefront
├── app
│   ├── app.js
│   ├── node_modules
│   ├── package.json
│   └── package-lock.json
├── docker-compose.yml
├── Dockerfile
├── scripts
│   └── send-coupon-mail.sh
└── README.md

The only thing special about your Dockerfile is a line to ensure that our scripts are executable:

# Pulling Ubuntu image
FROM ubuntu:latest

# Update packages
RUN apt-get update

# Setting up work directory
WORKDIR /src

# Copy the the app source into the container
COPY . .

# Ensure the scripts are executable
RUN chmod +x /src/scripts/*.sh

# <!-- Custom Application Setup Code Here -->

In this scenario, the marketing team has asked us to begin sending coupons every day to eligible users. After creating a script with the new selection criteria, you now need to create the cron job to run it daily. Because you're already using Docker in development and production, you can use the container built for your app to run your cron jobs, without ever needing to add a crontab to the container itself.

  1. If you have not already done this, you'll need to build the container from the Dockerfile. In this example, we are building a container named acme:app.
cd /acme-products-storefront
docker build -t acme:app .
  1. Using docker run, run the script from your host:
docker run --rm acme:app scripts/send-coupon-mail.sh

This will start the container you built in step one, and after it's running, it will run send-coupon-mail.sh inside of it, writing any script output directly to your terminal.

  1. Add the command as a cron job on your host by running crontab -e and adding a line:
0 8 * * * docker run --rm acme:app /acme-products-storefront/scripts/send-coupon-mail.sh
  1. You're all set - at 8:00 on the host, your job will run.

With this approach, we avoid the complexity of running cron itself within the container, while still reaping the benefits of the containerized environment to run our job in. That said, one red flag with this approach is that we are leaking application state and configuration out of the container and into the host environment itself.

Running cron inside your container

In the previous approach, we showed you how to run your send-coupon-mail.sh script within a container using docker run, using the same Dockerfile that you use to run your app itself. We will build upon that example here, with one key difference: Instead of scheduling the job in the host crontab, we will define a new container to run our cron jobs, and schedule our example job directly within the container itself,

  1. Start by adding a new cron directory to the root of our repository beside the Dockerfile, where we will add a Dockerfile and a Crontab that contains the Send Coupon Mail job. The directory structure now looks like this:
acme-products-storefront
├── app
│   ├── app.js
│   ├── node_modules
│   ├── package.json
│   └── package-lock.json
├── cron
│   ├── Crontab
│   └── Dockerfile
├── docker-compose.yml
├── Dockerfile
├── scripts
│   └── send-coupon-mail.sh
└── README.md

The Crontab entry looks a little different this time. Because this will be run within the container, the script is invoked directly without needing to use docker run, with a path that starts with the container's WORKDIR, /src.

0 8 * * * /src/scripts/send-coupon-mail.sh
  1. Copying the same structure as the previous Dockerfile, this container will be used specifically to run our cron jobs, so we will install cron and copy our new Crontab in place:
# Pulling Ubuntu image
FROM ubuntu:latest

# Update packages and Install cron
RUN apt-get update && apt-get install -y cron

# Setting up work directory
WORKDIR /src

# Copy the the app source into the container
COPY . .

# Ensure the scripts are executable
RUN chmod +x /src/scripts/*.sh

# Copy the crontab into place
COPY Crontab /etc/cron.d/

# <!-- Custom Application Setup Code Here -->

# Run cron, and tail the primary cron log
CMD cron && tail -f /var/log/cron.log

Note: In practice, you should base this dockerfile on an image created for your app container, and not duplicate the Dockerfile contents.

  1. Deploy and run the container in the same way you do any other. This could be anything from docker-compose to Fargate. In the simplest case you can run it from the commandline:
$ cd Crontab
$ docker build -t acme:cron .
$ docker run acme:cron

Monitoring containerized cron jobs

Running cron jobs inside a container introduces new risks and failure modes, and proper monitoring is essential. By installing CronitorCLI alongside your Crontab itself, you can ensure that jobs will be monitored by Cronitor and you will be alerted if they fail to run for any reason -- from bugs in your code to containers that have crashed.

Building on the previous example, this Dockerfile will install and provision CronitorCLI on the container:

# Pulling Ubuntu image
FROM ubuntu:latest

# Update packages and Install cron
RUN apt-get update && apt-get install -y cron

# Setting up work directory
WORKDIR /src

# Copy the the app source into the container
COPY . .

# Ensure the scripts are executable
RUN chmod +x /src/scripts/*.sh

# Copy the crontab into place
COPY Crontab /etc/cron.d/

# Install CronitorCLI
curl https://cronitor.io/install-linux?sudo=0 -H "API-KEY: <SDK KEY FROM https://cronitor.io/app/settings/api>"  | sh

# Find and monitor all cron jobs, specifying a hostname so jobs are matched consistently on Cronitor
cronitor discover --auto --hostname acme:cron

# <!-- Custom Application Setup Code Here -->

# Run cron, and tail the primary cron log
CMD cron && tail -f /var/log/cron.log

With these 2 lines added, any jobs you add to your Crontab will be automatically monitored by Cronitor and will show up on your Cronitor dashboard as soon as the updated container is built.