Purpose

The purpose of this post is to help developers/sysadmins to run their programmed jobs very easily using docker. These are some examples that could be useful to have a Docker cron image/container:

  • Backup databases creating dumps weekly, every x days or even every x hours.
  • Run database/OS maintenance tasks periodically.
  • Retrieving data from a 3rd party making API calls periodically.
  • Keeping distributed content synced using Rsync.

Before you begin

In this tutorial, we’ll learn how to run tasks periodically in a Docker Cron Alpine Linux container. A Linux/Mac OS/Windows machine with Docker installed is required to follow this tutorial.

Periodic cron jobs with crond and Alpine

By default crond installation is part of the Busybox package that comes with Alpine Linux and uses the following default folders:

$ docker run -it alpine ls /etc/periodic
>
15min    daily    hourly   monthly  weekly

And the following crontab schedule:

/ # crontab -l
# do daily/weekly/monthly maintenance
# min	hour	day	month	weekday	command
*/15	*	*	*	*	run-parts /etc/periodic/15min
0	*	*	*	*	run-parts /etc/periodic/hourly
0	2	*	*	*	run-parts /etc/periodic/daily
0	3	*	*	6	run-parts /etc/periodic/weekly
0	5	1	*	*	run-parts /etc/periodic/monthly

Where we can choose when to run a task by placing scripts in the required folder.

Following previous definitions, we can use the following docker-compose example to run cron tasks every hour ( /etc/periodic/hourly ) and every 15 minutes ( /etc/periodic/15min ) by mounting them as a read-only volume:

version: '3'

services:
  cron:
    image: alpine:3.11
    command: crond -f -l 8
    volumes:
      - ./cron_tasks_folder/15min:/etc/periodic/15min/:ro
      - ./cron_tasks_folder/hourly:/etc/periodic/hourly/:ro

Scripts inside of cron_tasks_folder have to be without extension and with +x permissions. Local folder:

$ tree cron_tasks_folder
cron_tasks_folder
├── 15min
│   └── script_foo
└── hourly
    └── script_bar

script_foo will be executed every 15 and script_bar every hour.

Finally, we can run the Docker Alpine container and wait for the execution of the cron jobs:

docker-compose run -d

Adding more time periods

Following the current Alpine crontab strategy we can add a new folder that we’ll allow us to run a cron job every minute. Because we want the change to be permanent in the Docker Alpine container we are going to create a startup.sh script and that will triggered in the runtime:

#!/bin/sh

echo "Starting startup.sh.."
echo "*       *       *       *       *       run-parts /etc/periodic/1min" >> /etc/crontabs/root
crontab -l

As we promised before, we should call startup.sh in the docker-compose command: /usr/local/startup.sh && crond -f -l 8 and mount the cron job folder that will be triggered every minute:

version: '3'

services:
  cron:
    image: alpine:3.11
    command: /usr/local/startup.sh && crond -f -l 8
    volumes:
      - ./cron_tasks_folder/15min:/etc/periodic/15min/:ro
      - ./cron_tasks_folder/hourly:/etc/periodic/hourly/:ro
      - ./cron_tasks_folder/1min:/etc/periodic/1min/:ro
      - ./scripts/startup.sh:/usr/local/startup.sh:ro

The final step is to recreate the container:

$ docker-compose run -d --force-recreate

Please be aware that more periods can be added by adding crontab lines in startup.sh and mounting the scripts as a volume in the docker-compose cron service.

Bonus track: How to backup a PostgreSQL daily

To backup a PostgreSQL database we can use the following docker-compose:

version: '2'

services:
  db:
    image: postgres:9.6
    volumes:
      - /var/opt:/var/lib/postgresql
    environment:
      - PGDATA=/var/lib/postgresql/pg_data
  cron:
    image: postgres:9.6-alpine
    command: crond -f -l 8
    volumes:
      - ./cron_tasks_folder:/etc/periodic/daily/:ro
      - /host_dumps_folder/:/var/opt/
    depends_on:
      - db

Include the following file ./cron_tasks_folder/dump (Remember to not include the file extension .sh):

#!/bin/sh

pg_dump -h db -U POSTGRES_USER POSTGRES_DB >> /var/opt/$(date +"%Y%m%d%H%M%S").sql

And finally run Docker:

docker-compose build
docker-compose run -d

More information about how to backup a Postgres database using Docker here.