Run a cron job with Docker

| 46 Comments | 2 minutes read

ContainersAt ekito, we adopted Docker as lightweight virtualization solution to deploy our internal servers, as well as deliver “ready to run” applications to our customers.

Bert has already published an excellent article on Docker, one year ago. Since then, we continued to experiment on this topic and I would like to present a very simple (but not trivial) use case:
run a cron job in a docker container.

The source code is available on github.

Install docker

Depending on your platform, you can find the complete documentation on the docker website.

The cron job

We will create a cron job that will display “Hello world”, every minute, in the console

Let’s create a new file called “crontab” to describe our job.

* * * * * root echo "Hello world" >> /var/log/cron.log 2>&1
# An empty line is required at the end of this file for a valid cron file.

This job will echo “Hello world”, every minutes, and redirect the output in the file /var/log/cron.log

Important remarks:

  • a blank line is required at the end of this file!
  • the job is run by the “root” user as it is the only user that will exist in the container

The docker image

The following DockerFile describes all the steps to build your image

FROM ubuntu:latest
MAINTAINER docker@ekito.fr
 
# Add crontab file in the cron directory
ADD crontab /etc/cron.d/hello-cron
 
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
 
# Create the log file to be able to run tail
RUN touch /var/log/cron.log
 
# Run the command on container startup
CMD cron && tail -f /var/log/cron.log

Then you can build the image with

sudo docker build --rm -t ekito/cron-example .

And run it:

sudo docker run -t -i ekito/cron-example

Be patient, wait for 2 minutes and your commandline should display:

Hello world
Hello world

Now you can create you own script to backup you data, check your system status, or whatever you want !

Julien Boulay Author: Julien Boulay

Eclectic developer & architect
Activist for usability, performance and interoperability of systems.

My favorite quote : "The best feature is the one we don't need to develop !"

My hashtags : #windchill #java #talend #nodejs #angularjs #oss #docker

Like it?  Share  it!

Share Button

46 Comments

  1. Pingback: Run cron in centOS as user process, not daemon | DL-UAT

  2. Pingback: Run cron in centOS as user process, not daemon | You need? We have! Everything

  3. To use this in production, you probably want cron to run in the forground like this:

    CMD [“cron”, “-f”]

  4. Um… this doesn’t work. I waited for 5 minutes and nothing is displayed. How can I debug this?

  5. Nice article! Thx

  6. Nice article but the use case is quite dummy. Would be much better to have an example of cron job executed for a different container than cron container.

    • Julien Boulay

      Hi jangorecki,

      Thanks for your comment. You’re right, it is a very simple use case. But usually, it is recommended to execute only one process by container. We could imagine run a cron in one container, that will access a shared volume…
      The idea was to keep the article as simple as possible to illustrate the usage and debugging of a cron.

  7. Hi,

    I try to get it working on a centos 7 httpd container. The daemon crond doesn’t start. Is someone have successfully startup cron in a container that already run another daemon (like httpd) ?

    I tried with RUN/CMD crond before the startup CMD for httpd, but it’s not working. I suppose that it is not possible to start multiple daemon in the same container ? if I just try to start the crond daemon without httpd, it works.

  8. Thanks for this, however, you should be aware that cron can’t access any environment variables you pass into the container… making life pretty difficult if you’re running commands for an app that may require stuff.

  9. Nice article!. Thanks. Specially the commented line help me a lot
    # An empty line is required at the end of this file for a valid cron file.

  10. Pingback: 使用Docker Compose管理Docker容器 | 勇气

  11. do you know as run the cron in another’s containers simultaneously? For example, if i have three containers, configure crontab for all. I’m trying, but not work, the cron, only run in one container. I using the next method:

    edit:
    crontab -e

    and put: */2 * * * * export PATH=”/root/driver:$PATH” && cd /root/file && /usr/bin/ruby /root/file/file.rb

    Thanks.

  12. Hi Julien!

    It’s worth noting, that:

    1. It would be better to mount the ‘crontab’ file straight from your project directory, to keep it up to date on every container startup.

    2. You should rather mount a separate volume for logs from the host machine, to keep your logs outside the container OR create fifo file for logs.

    • Julien Boulay

      Hi Dimitri,

      Thank you very much for your remarks. You are right, it would be much better to mount external file systems to control crontab and logs files.
      I will test it and update my post soon with external directories.

  13. I created a similar image but all bases on env vars , no config files to mount.

    let me know what you think

    https://www.vip-consult.solutions/post/better-docker-cron#content

  14. Thank you for this article Julien. Cannot get it to work with a container using python:2.7 image. I see the cron -f process running, and my cron job is in the /etc/cron.d/hello-cron, but I’m getting no cron jobs auto firing after 5 minutes of waiting.

    Any help would be appreciated as I’ve been messing with this for too many hours.

  15. Pingback: Gestione multidominio con Docker - Ajenti-V - Letsencrypt » m4-Web

  16. For those having trouble, I solved issues (below). I assume you’re using “FROM ubuntu:latest” for the current LTS. I suspect some things changed in the base image since the time this was written.

    (Use whatever you want of course, but I strongly suggest deferring experimentation with non-Ubuntu base… get the basics working first)

    Problem: missing ‘crontab’ file
    “`Step 6/7 : RUN /usr/bin/crontab /etc/cron.d/hello-cron
    —> Running in 19b11a035f00
    /bin/sh: 1: /usr/bin/crontab: not found“`

    Solution: add:
    “`RUN apt-get update && apt-get install -y cron“`

    Problem:
    “`# /bin/sh: 1: root: not found“`

    Solution: remove ‘root’ from column 6 of the cron file. The command will be implicitly run as root and column 6 should contain the command.

    Problem: nothing output
    Also add:
    RUN /usr/bin/crontab /etc/cron.d/hello-cron
    (leave in the CMD cron && tail -f /var/log/cron.log)

    Other suggestions:
    If you’re having trouble differentiating images and instances because you are new, edit the cron file to say “Hello world2” etc., because nothing’s worse than making changes and not seeing them work as expected because the wrong thing is executed.. 🙂

    • Hello,

      I seems to be one of those having troubles and need help since after many and many tries I am unable to have the cronjob running correctly..

      ============
      Dockerfile
      ============

      FROM ubuntu:latest

      # Install cron
      RUN apt-get update && apt-get install -y cron

      # Create the log file to be able to run tail
      RUN touch /var/log/cron.log

      # Add crontab file in the cron directory
      ADD crontab /etc/cron.d/hello-cron

      # Give execution rights on the cron job
      RUN chmod 0644 /etc/cron.d/hello-cron

      RUN /usr/bin/crontab /etc/cron.d/hello-cron

      # Setup cron job
      #RUN (crontab -l ; echo “* * * * * echo “Hello world” >> /var/log/cron.log”) | crontab

      # Run the command on container startup
      #CMD [“cron”, “-f”]
      CMD cron && tail -f /var/log/cron.log

      =============
      crontab file
      =============
      * * * * * root echo Hello worldxxxxxx >> /var/log/cron.log 2>&1
      # empty line

      I log in the container and drun the following commands to check things:

      ps -ef |grep cron
      root 1 0 0 13:37 ? 00:00:00 /bin/sh -c cron && tail -f /var/log/cron.log
      root 6 1 0 13:37 ? 00:00:00 cron
      root 7 1 0 13:37 ? 00:00:00 tail -f /var/log/cron.log
      root 17 8 0 13:37 ? 00:00:00 grep –color=auto cron

      bash -c “crontab -l”
      * * * * * root echo Hello worldxxxxxx >> /var/log/cron.log 2>&1

      bash -c “pgrep cron”
      6

      I wait to see if “Hello worldxxxxxx” start to display but it doesn’t; if I do
      cat /var/log/cron.log nothing output because file is 0 bytes

      Thanks in advance for any possible suggestion

      • Julien Boulay

        Hi Emi,

        Did you try to clone the github repository and run the given example ?
        I just did it to verify that `ubuntu:latest` didn’t change the behaviour of the container and it works for me.

        • Salut Julien,

          thank you for your kind reply

          I have tried one more time and can confirm that cloning from the git repo is working fine as you stated!

          I am going to review what mistake I’ve done trying to implement it with other containers in docker-compose and with a replica of your Dockerfile

          Perhaps I was checking in the wrong place to see the Hello World print out..

          Thank you again

          • To anyone having issues, there is also this for files in /etc/cron.d:

            must conform to the same naming convention as used by
            run-parts(8): they must consist solely of upper- and lower-case letters, digits, underscores, and hyphens. This means that they cannot contain any dots.

            So make sure you name your file correctly.

  17. Pingback: Docker and Time based Jobs – Phil Schatzmann Consulting

  18. Pingback: SummerEye part11 -cron設定- | fukuの犬小屋

  19. Pingback: Docker setup – part 5: cleanup – Datadriven-investment.com

  20. Great article, I was searching for the reverse actually, a crontab entry on the host OS to execute the containers entrypoint which might be a script or binary.

    Any reason to not take this approach you can think of?

    • Julien Boulay

      Hi Cam,

      Thank you for your comment. You’re right, we can think about running a cron job from host OS.
      Then, you’re cron script could just run the container each time it is needed.
      Please feel free to post your example here.

      • In the end I went for containers being executed via systemd using a “oneshot” approach. Along with a systemd timer for periodic execution. Works like a charm.

        Systemd unit files simply specify the container from a registry, so when they run, they pull the image needed.

        Also, the unit file allows us to specify dependencies. So if a db we require should be running on the same host, we can ensure our process does not run until the db is started.

        Systemd timers can do all sorts of cool optional things like:
        – run when the server starts
        – run if the server was down when the job was scheduled to run
        – back off for ‘n’ period of time so that there is no wave of heavy load
        – control if you want / don’t want multiple instances running at once
        – allow for finer time controls (seconds not just minutes)
        – allow for a standard amount of time between executions (run ‘n’ period of time after last run completed)

        Keeping a command line application in a container, as the container entry point means it can be fired on demand, run from a systemd timer, or some other container scheduler which I believe is available in the various PaaS solutions such as open shift.

        Also, systemd services and timers can be run with a –user option if you don’t have access to the central unit files (usually under /etc/systemd/system) the you are still fine

        I can’t see myself working with cron ever again 🙂
        It was a great tool that helped shelter and feed my family and I, but I believe it has seen it’s day.

        Regarding log output: if your containers just output to stdout/err systemd will redirect them to syslog. Which was perfect for us.

        A small tip, in the systemd unit file, specify your appname like so:

        SyslogIdentifier=my-containerised-app

        Then you can grep for messages with:

        tail -f /var/log/message | grep my-containerised-app

        I thing this approach is very ’12 Factor’

  21. Hi!
    I’m trying to execute a job for a different (not-root) user.
    I created the user via “useradd” command and edited the crontab script, so instead of root it uses the username of the new user I created.
    Lines of the file with “root” user are being executed perfectly, but it doesn’t execute the lines with “crontask” user.
    Please, help!

  22. chmod 0644 != execute permissions for any user. otherwise this worked for me.

What do  You  think? Write a comment!

Leave a Reply

Required fields are marked *.


CommentLuv badge

This site uses Akismet to reduce spam. Learn how your comment data is processed.