How to run a cron job inside a docker container?
You can copy your crontab into an image, in order for the container launched from said image to run the job.
See "Run a cron job with Docker" from Julien Boulay in his Ekito/docker-cron
:
Let’s create a new file called "
hello-cron
" to describe our job.
# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)
* * * * * 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.
If you are wondering what is 2>&1, Ayman Hourieh explains.
The following Dockerfile describes all the steps to build your image
FROM ubuntu:latest
MAINTAINER docker@ekito.fr
RUN apt-get update && apt-get -y install cron
# Copy hello-cron file to the cron.d directory
COPY hello-cron /etc/cron.d/hello-cron
# Give execution rights on the cron job
RUN chmod 0644 /etc/cron.d/hello-cron
# Apply cron job
RUN crontab /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
(see Gaafar's comment and How do I make apt-get
install less noisy?:apt-get -y install -qq --force-yes cron
can work too)
As noted by Nathan Lloyd in the comments:
Quick note about a gotcha:
If you're adding a script file and telling cron to run it, remember toRUN chmod 0744 /the_script
Cron fails silently if you forget.
OR, make sure your job itself redirect directly to stdout/stderr instead of a log file, as described in hugoShaka's answer:
* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2
Replace the last Dockerfile line with
CMD ["cron", "-f"]
See also (about cron -f
, which is to say cron "foreground") "docker ubuntu cron -f
is not working"
Build and run it:
sudo docker build --rm -t ekito/cron-example .
sudo docker run -t -i ekito/cron-example
Be patient, wait for 2 minutes and your commandline should display:
Hello world
Hello world
Eric adds in the comments:
Do note that
tail
may not display the correct file if it is created during image build.
If that is the case, you need to create or touch the file during container runtime in order for tail to pick up the correct file.
See "Output of tail -f
at the end of a docker CMD
is not showing".
See more in "Running Cron in Docker" (Apr. 2021) from Jason Kulatunga, as he commented below
See Jason's image AnalogJ/docker-cron
based on:
Dockerfile installing
cronie
/crond
, depending on distribution.an entrypoint initializing
/etc/environment
and then callingcron -f -l 2
how to perform cron jobs every 5 minutes inside docker
Add it on a separate line.
When you use && with cron, it's expecting multiple cron jobs to add for the same cron frequency.
eg.
0 * * * * a && b
Hence why it says "*/5 not found" because that's the b
above - it thinks it's a cron script to run.
Add your */5 * * * * script on a separate line in its own command.
How to run cron job in docker container?
Crontab
requires additional field: user, who runs the command:
* * * * * root python3 /code/populatePDBbackground.py >> /var/log/cron.log
# Empty line
The Dockerfile
is:
FROM python:3
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get install -y cron postgresql-client
RUN touch /var/log/cron.log
RUN mkdir /code
WORKDIR /code
ADD . /code/
COPY crontab /etc/cron.d/cjob
RUN chmod 0644 /etc/cron.d/cjob
ENV PYTHONUNBUFFERED 1
CMD cron -f
Test python script populatePDBbackground.py
is:
from datetime import datetime
print('Script has been started at {}'.format(datetime.now()))
And finally we get:
$ docker run -d b3fa191e8822
b8e768b4159637673f3dc4d1d91557b374670f4a46c921e0b02ea7028f40e105
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b8e768b41596 b3fa191e8822 "/bin/sh -c 'cron -f'" 4 seconds ago Up 3 seconds cocky_beaver
$ docker exec -ti b8e768b41596 bash
root@b8e768b41596:/code# tail -f /var/log/cron.log
Script has been started at 2019-03-13 00:06:01.095013
Script has been started at 2019-03-13 00:07:01.253030
Script has been started at 2019-03-13 00:08:01.273926
How to schedule a job in the background and start another process inside a docker container?
I found a solution using docker-compose
based on the following article.
It basically overrides the entrypoint in another service as follows in the docker-compose.yml
file:
version: "3"
services:
app:
image: demo-image:latest
volumes:
- data:/app-data
cron:
image: demo-image:latest
command: [ "cron -f" ]
tty: true
volumes:
- data:/app-data
volumes:
data:
My example Dockerfile
:
# syntax=docker/dockerfile:experimental
FROM python:3.9
RUN apt-get update
RUN apt-get -y install cron
COPY my-crontab /etc/cron.d/my-crontab
RUN chmod 0744 /etc/cron.d/my-crontab
RUN crontab -l | { cat; cat /etc/cron.d/my-crontab } | crontab -
RUN touch /var/log/cron.log
WORKDIR /code
COPY . /code
ENTRYPOINT ["/bin/bash", "/docker-entrypoint.sh"]
My example cronjob
file with an important hint that took me hours of bugtracking:
* * * * * echo "Hello world" >> /var/log/cron.log 2>&1
# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)
I found this solution much cleaner because it only uses one process per container.
Cronjob in docker container not running
As Saeed said in this comment
First of all, your cronjob command is wrong. You should have
2>&1
instead of2&>1
. Second. runls -lh /app/cron.sh
to see if your file is copied. Also be surecron.sh
is in the directory where yourDockerfile
is.
2&>1
was the mistake that I had made.
How to run a cron job inside a docker container
Here is how I run one of my cron containers.
Dockerfile:
FROM alpine:3.3
ADD crontab.txt /crontab.txt
ADD script.sh /script.sh
COPY entry.sh /entry.sh
RUN chmod 755 /script.sh /entry.sh
RUN /usr/bin/crontab /crontab.txt
CMD ["/entry.sh"]
crontab.txt
*/30 * * * * /script.sh >> /var/log/script.log
entry.sh
#!/bin/sh
# start cron
/usr/sbin/crond -f -l 8
script.sh
#!/bin/sh
# code goes here.
echo "This is a script, run by cron!"
Build like so
docker build -t mycron .
Run like so
docker run -d mycron
Add your own scripts and edit the crontab.txt and just build the image and run. Since it is based on alpine, the image is super small.
Cron task inside container from host
Note: when debugging such problems with cron, you should look for errors in your local system mails or redirect those to your real mail by adding MAILTO=yourmail@yourdomain.com
on top of your crontab file.
There are 2 problems with your crontab command
TLDR; the fixed cron expression
* * * * * docker exec sample_container bash -c 'touch /selected/directory/temp$(date +\%H-\%M)'
% has a special meaning in crontab
From man -s 5 crontab
Percent-signs (
%
) in the command, unless escaped with backslash (\
),
will be changed into newline characters, and all data after the
first%
will be sent to the command as standard input.
So you will need to escape those %
signs in your date format string
Cron does not allocate a tty
Cron does not allocate a tty whereas your are trying to use one when executing your command (i.e. the -t
option to docker exec
). The command will therefore fail with the error the input device is not a TTY
You do not need to go interactive (-i
) nor to allocate a tty for this command to do its job anyway, so you have to drop those options to launch it from cron.
Related Topics
How to Use Xargs to Copy Files That Have Spaces and Quotes in Their Names
Optimize PDF Files (With Ghostscript or Other)
Why Exit Code 141 with Grep -Q
Where Does Eclipse Look for Eclipse.Ini Under Linux
How to Use Arrow Keys Alone to Expand Tree Node in Package Explorer in Eclipse on Linux
Create Zip File and Ignore Directory Structure
Does Gcc Have Any Options to Add Version Info in Elf Binary File
Nuget on Linux: Error Getting Response Stream
Can't Clone a Github Repo on Linux via Https
How to Program Linux .Dts Device Tree Files
User-Data Scripts Is Not Running on My Custom Ami, But Working in Standard Amazon Linux
How to Upgrade Aws Cli to the Latest Version
How to Sort a File, Based on Its Numerical Values for a Field