How to run a cron job inside a docker container
Asked Answered
W

5

65

I tried to run a cron job inside a docker container but nothing works for me.
My container has only cron.daily and cron.weekly files.
crontab,cron.d,cron.hourly are absent in my container.
crontab -e is also not working.
My container runs with /bin/bash.

Waken answered 3/5, 2016 at 22:44 Comment(1)
See my answer here.Anaptyxis
S
169

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.

Sharyl answered 4/5, 2016 at 1:18 Comment(12)
This is great. One problem I'm having is that after docker run ... I'm not able to stop the container running using ctrl-c? Even after I close the terminal, when I do docker ps I can see that the container is still up. Does anyone have the same problem?Warhol
@Warhol did you try doing "docker stop <container name/id>"?Sharyl
Yeah I had to use docker kill <container name/id>. In retrospect it is most likely not a docker issue and some side effect of the scripts I was running using the crontab. I made a little different configuration of my scripts now and think this is a non-issue.Warhol
Please add RUN apk add --update apk-cron && rm -rf /var/cache/apk/* to the answer for a full example. There are so many ways to add cron to alpine listed out there, and this one is the one that works with your example.Adina
Hi, why there are 2 ADD and 1 COPY?, base on what I read seem like 3 COPY or 3 ADD would work fine(in this case). am I right?Unrivaled
It's good to know that if you're just adding files to an image and changing entrypoint/command, it can all be done with configuration at container creation without building a new image.Margenemargent
It'd be nice if you didn't have to create a full container just for a simple cronjob. What would be a good way to add a cronjob to a pre-existing container instead of creating a new/separate container?Nicholle
crond -f -l 8, What does the 8 mean? According to man, the log level is with a capital L.Tim
@jcarlisweb different versions of CRON have different flags. Some use upper case L for log file and lower case l for log level. See man.gnu.org.ua/manpage/?8+crond as an example. Pick what works for your version of CRONSharyl
I don't see crond running in the container as default. I had to exec and run crond which actually triggered the cron job. Is there something I'm missing here ?Elnora
@Elnora The "Docker way" is to have the container run a single command (in general your app/command should be PID 0) as the "entrypoint", so if you're doing things The Right Way™ you shouldn't be running crond as a daemon, which is what it sounds like you're expecting. That's the traditional way cron is run, but again, it's doesn't fit the containerization paradigm. When you grow your system(s) you'll want something monitoring and managing your containers. Logging outside the container comes from your command's STDIN/STDOUT and exit codes are returned to the container controller/monitor, eg K8s.Headland
use his answer https://mcmap.net/q/296263/-how-to-run-a-cron-job-inside-a-docker-container because of this petermalmgren.com/signal-handling-dockerWulfila
P
15

crond works well with tiny on Alpine

RUN apk add --no-cache tini

ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/usr/sbin/crond", "-f"]

but should not be run as container main process (PID 1) because of zombie reaping problem and issues with signal handling. See this Docker PR and this blog post for details.

Pleasantry answered 22/8, 2017 at 10:21 Comment(1)
Why should not be run as PID1?Analogous
T
11

@ken-cochrane's solution is probably the best, however, there is also a way to do it without needing to create extra files.

To do it without extra files:

The way to go is to set the cron within your entrypoint.sh file.

Dockerfile


...

# Your Dockerfile above


COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh


echo "* * * * * echo 'I love running my crons'" >> /etc/crontabs/root
crond -l 2 -f > /dev/stdout 2> /dev/stderr &

# You can put the rest of your entrypoint.sh below this line

...

Talkathon answered 1/8, 2021 at 17:46 Comment(1)
If you are using alpine linux without bash, don´t forget to add the following to the beggining of the sh file: #!/bin/ashHemphill
A
2

You don't say much about what you did, but one way would be to make use of dcron and Alpine Linux:

docker-compose.yml:

services:
  dcron:
    build: .
    command: crond -fl info
    init: yes

Dockerfile:

FROM alpine:3.17
RUN set -x \
    && apk add --no-cache dcron shadow \
    && useradd -m app
COPY crontab /etc/crontabs/app

crontab:

* * * * * date >/dev/null 2>&1

(You can find a bit more information in my gist.)

Here I make it run tasks under non-root, since you shouldn't give more privileges than is needed (principle of least privilege), even inside a docker container.

The downside of this solution is that you can't see tasks' output in docker logs. Which is why I suggest you to give a try to a docker-friendly cron implementation, e.g. supercronic:

docker-compose.yml:

services:
  supercronic:
    build: .
    command: supercronic crontab

Dockerfile:

FROM alpine:3.17
RUN set -x \
    && apk add --no-cache supercronic shadow \
    && useradd -m app
USER app
COPY crontab .

crontab:

* * * * * date

You can also check out my gist about supercronic, and my other answer, which provides examples of using other cron implementations.

Anaptyxis answered 6/2, 2023 at 6:49 Comment(1)
I'd like to ask @ChrisF about my deleted answer, if this one is tailored enough to the question. But apparently I can't mention him here. If you're going to delete this answer let's discuss first what's a duplicate, and what's not. Or give me some way of communication so that I could improve my answer.Anaptyxis
U
0

Here is good explanation of cron problems inside docker container:

Docker file example:

FROM alpine

# Copy script which should be run
COPY ./myawesomescript /usr/local/bin/myawesomescript
# Run the cron every minute
RUN echo '*  *  *  *  *    /usr/local/bin/myawesomescript' > /etc/crontabs/root

CMD ['crond', '-l 2', '-f']
Unmixed answered 1/7, 2020 at 15:13 Comment(1)
This approach doesn't work, as far as I'm concerned. I've tried a few variants too, and it seems like in the final container, changes made to /etc/crontabs/root aren't there.Talkathon

© 2022 - 2024 — McMap. All rights reserved.