How to run a cron job inside a docker container?

509,255

Solution 1

Unfortunately, none of the above answers worked for me, although all answers lead to the solution and eventually to my solution, here is the snippet if it helps someone. Thanks

This can be solved with the bash file, due to the layered architecture of the Docker, cron service doesn't get initiated with RUN/CMD/ENTRYPOINT commands.

Simply add a bash file which will initiate the cron and other services (if required)

DockerFile

FROM gradle:6.5.1-jdk11 AS build
# apt
RUN apt-get update
RUN apt-get -y install cron
# Setup cron to run every minute to print (you can add/update your cron here)
RUN touch /var/log/cron-1.log
RUN (crontab -l ; echo "* * * * * echo testing cron.... >> /var/log/cron-1.log 2>&1") | crontab
# entrypoint.sh
RUN chmod +x entrypoint.sh
CMD ["bash","entrypoint.sh"]

entrypoint.sh

#!/bin/sh
service cron start & tail -f /var/log/cron-2.log

If any other service is also required to run along with cron then add that service with & in the same command, for example: /opt/wildfly/bin/standalone.sh & service cron start & tail -f /var/log/cron-2.log

Once you will get into the docker container there you can see that testing cron.... will be getting printed every minute in file: /var/log/cron-1.log

Solution 2

I decided to use busybox, as it is one of the smallest images.

crond is executed in foreground (-f), logging is send to stderr (-d), I didn't choose to change the loglevel. crontab file is copied to the default path: /var/spool/cron/crontabs

FROM busybox:1.33.1

# Usage: crond [-fbS] [-l N] [-d N] [-L LOGFILE] [-c DIR]
#
#   -f  Foreground
#   -b  Background (default)
#   -S  Log to syslog (default)
#   -l N    Set log level. Most verbose 0, default 8
#   -d N    Set log level, log to stderr
#   -L FILE Log to FILE
#   -c DIR  Cron dir. Default:/var/spool/cron/crontabs

COPY crontab /var/spool/cron/crontabs/root

CMD [ "crond", "-f", "-d" ]

Solution 3

This question have a lot of answers, but some are complicated and another has some drawbacks. I try to explain the problems and try to deliver a solution.

cron-entrypoint.sh:

#!/bin/bash

# copy machine environment variables to cron environment
printenv | cat - /etc/crontab > temp && mv temp /etc/crontab

## validate cron file
crontab /etc/crontab

# cron service with SIGTERM support
service cron start
trap "service cron stop; exit" SIGINT SIGTERM

# just dump your logs to std output
tail -f  \
    /app/storage/logs/laravel.log \
    /var/log/cron.log \
    & wait $!

Problems solved

  • environment variables are not available on cron environment (like env vars or kubernetes secrets)
  • stop when crontab file is not valid
  • stop gracefully cron jobs when machine receive an SIGTERM signal

For context, I use previous script on Kubernetes with Laravel app.

Solution 4

If you'd like to run other services than crontab you might want to call this script to activate yourscript in addition to your entry point:

addCronTabEntry.sh

(crontab -l 2>/dev/null; echo "*/1 * * * * <yourscript>") | crontab -
service cron start

see https://github.com/WolfgangFahl/pymediawikidocker

Share:
509,255
C Heyer
Author by

C Heyer

Updated on July 08, 2022

Comments

  • C Heyer
    C Heyer almost 2 years

    I am trying to run a cronjob inside a docker container that invokes a shell script.

    Yesterday I have been searching all over the web and stack overflow, but I could not really find a solution that works.
    How can I do this?

    EDIT:

    I've created a (commented) github repository with a working docker cron container that invokes a shell script at given interval.

  • Peter
    Peter almost 3 years
    Shouldn't it be doing tail -f /var/log/cron-1.log instead of /var/log/cron-2.log, since cron-1.log is where the STDOUT/STDERR is being directed? (Unless I'm missing something)
  • Gaurav Tyagi
    Gaurav Tyagi almost 3 years
    Yes, correct, that was a typo, /var/log/cron-1.log should be at every place
  • Johnson_145
    Johnson_145 almost 2 years
    If I run docker stop with this setup, nothing happens, i.e. service cron stop doesn't get executed. If I run the latter manually from within the container, the cron process stops immediately instead of waiting for the cronjobs. cronjobs will still complete their run, so that may be fine. When they are done, the container does not stop either though. What am I missing?
  • Johnson_145
    Johnson_145 almost 2 years
    Got it working now. I think the trap handler wasn't triggered, because I defined my entryscript as CMD "/bin/sh" ENTRYPOINT /entrypoint.sh instead of ENTRYPOINT ["/entrypoint.sh"]. That way, it got wrapped in another shell which didn't pass the signals through. I had to do some further steps to actually wait for running cronjobs to finish. Elaborating on your answer over here.