512

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?

user229044
  • 232,980
  • 40
  • 330
  • 338
C Heyer
  • 5,123
  • 3
  • 10
  • 8

31 Answers31

608

You can copy your crontab into an image, in order for the container launched from said image to run the job.


Important: as noted in docker-cron issue 3: use LF, not CRLF for your cron file.


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

But: if cron dies, the container keeps running.

(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 to
RUN 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"]

But: it doesn't work if you want to run tasks as a non-root.

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 command-line 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 calling

    cron -f -l 2
    
x-yuri
  • 16,722
  • 15
  • 114
  • 161
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    I had first to install cron, as it is not included. But by adding this to the Dockerfile it works. Thanks! `RUN apt-get update && apt-get install cron` – C Heyer May 26 '16 at 12:15
  • @CHeyer OK, I have updated the answer to include the missing `RUN` command. – VonC May 26 '16 at 13:10
  • 4
    you should probably add `-y` to installing cron to avoid docker build exiting – gafi Oct 16 '16 at 07:41
  • @Gaafar Strange, I don't see a -y option in http://man7.org/linux/man-pages/man8/cron.8.html – VonC Oct 16 '16 at 08:15
  • i mean `apt-get install -y cron` to avoid the install promting for `y` or `n` – gafi Oct 16 '16 at 10:25
  • 1
    @Gaafar Right! I have included your comment in the answer for more visibility and added another option. – VonC Oct 16 '16 at 10:30
  • Has anyone tried this on a Vagrant VM with Docker Compose as provisioner? I cant get it to run, no matter what.. – lephleg Oct 24 '16 at 21:30
  • @LePhleg It is better to make that an actual question on its own instead of a comment. – VonC Oct 24 '16 at 21:41
  • Many thanks @VonC. How would you edit this to create create crontab from a file? I've been doing `RUN crontab -u root cron_files.txt` and I've tried `ADD crontab cron_files.txt /etc/cron.d/feeds` but when I try to do `chmod` it returns `chmod: cannot access '/etc/cron.d/feeds': No such file or directory` – LearningSlowly Nov 21 '16 at 18:06
  • I tried this on Ubuntu 14.04 and it didn't work. The output is empty. – Pangur Feb 06 '17 at 13:34
  • 1
    @Pangur nothing in `docker ps` or `docker logs`? If so, please ask a new question. – VonC Feb 06 '17 at 14:32
  • @VonC docker ps showed running container, but one has nothing to do with it. I've found out the problem: my kernel was old enough and it caused known bug related to not only cron https://github.com/phusion/baseimage-docker/issues/144 . After I've updated my kernel to 4.4 everything gone well. Other workaround was running under Ubuntu Precise but this one I don't need. – Pangur Feb 07 '17 at 11:06
  • On certain hosts, I needed to touch the crontab files to pull them into the top layer. CMD touch /etc/crontab /etc/cron.*/* /var/log/cron.log && cron && tail -f /var/log/cron.log – Ken Ashcraft May 25 '17 at 22:31
  • 1
    The `root` must be added into cronjob record. – Ryan Chou Feb 02 '18 at 12:48
  • 13
    Does this solution still work? When I follow the guidelines given, when I log into the container as root and type `crontab -l`, I get **No crontab installed for root**, also, my screen remains blank. However, when I check '/etc/cron.d/', I see the crontab fiel is there (and even more surprisingly), when I check `/var/log/cron.log`, I see that the script is running (the file content is being appended with `Hello World`). I'm pulling this image in my Dockerfile: `FROM phusion/baseimage:0.10.0`. Any ideas about the discrepancy in behaviour? – Homunculus Reticulli Feb 02 '18 at 16:30
  • @HomunculusReticulli It did run at the time. Can you ask a new question (with a link back to this one)? – VonC Feb 02 '18 at 16:31
  • 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 : https://stackoverflow.com/a/43807880/1032870 – gileri Feb 23 '18 at 10:46
  • @Eric Thank you. I have included your comment in the answer for more visibility. – VonC Feb 23 '18 at 12:20
  • Also you need to add RUN /usr/bin/crontab /etc/cron.d/hello-cron in your Dockerfile to make it work – Rustam Ganeyev Jul 07 '18 at 15:51
  • @RustamGaneyev Can you edit the answer in order to add that step? – VonC Jul 07 '18 at 15:51
  • @RustamGaneyev I just did: thank you for your contribution :) – VonC Jul 07 '18 at 15:59
  • 26
    As of 2018, this approach no longer works; has anyone been able to get their cronjob to work with Ubuntu as the base image? I'm not interested in the Alpine image which comes with cron running out of the box – pelican Jul 26 '18 at 19:57
  • 1
    What about running cron in a container while *not* being root? – Shmarkus Nov 18 '18 at 20:16
  • 1
    @Shmarkus You would need another setup, like https://github.com/uber/makisu, https://cloud.google.com/blog/products/gcp/open-sourcing-gvisor-a-sandboxed-container-runtime or https://medium.com/lucjuggery/running-a-container-with-a-non-root-user-e35830d1f42a. – VonC Nov 18 '18 at 20:37
  • Anyone knows if this is possible using AWS Fargate ? – Sebastien H. Jul 11 '19 at 15:07
  • 5
    Quick note about a gotcha: if you're adding a script file and telling cron to run it, remember to `RUN chmod 0744 /the_script`. Cron fails silently if you forget. – Nathan Lloyd Mar 18 '20 at 00:42
  • @NathanLloyd Thank you. I have included your comment in the answer for more visibility. – VonC Mar 18 '20 at 05:34
  • 1
    Given the complexity of this answer, I think it makes sense to **consider** the work around of running the cron job on a local server and pushing updates to the docker registry with the local cron job. – geneorama Jul 07 '20 at 23:16
  • 2
    I have read many guides on getting cron working in a Docker container and 4 hours later this one helped me because you mentioned the blank link in the cron file :) Thank you! – Ste Jan 11 '21 at 21:50
  • 1
    Nothing on this page works. It has been running for several minutes and the output is empty. – Arrow_Raider Mar 31 '21 at 21:13
  • @Arrow_Raider Strange. Could you ask a separate question with details about your particular setup? – VonC Mar 31 '21 at 22:05
  • 1
    I removed root and then it worked. This stuff is extremely frustrating for me. – Arrow_Raider Apr 01 '21 at 23:21
  • 7
    I wrote a blog post that implements this advice (and other issues I found running cron in docker) into working docker images for multiple distros (ubuntu, alpine, centos): https://blog.thesparktree.com/cron-in-docker – Jason Kulatunga Apr 27 '21 at 05:37
  • @JasonKulatunga Thank you, very interesting. I have included your comment in the answer for more visibility. – VonC Apr 27 '21 at 07:36
  • 1
    If I use CMD the container is only doing this job. I actually want t run a cron in an existing container already execution something else. Any hint how this can be achieved, so cron is only in the back? – Tobias Jun 09 '21 at 09:38
  • @Tobias good question. Can you make it a separate question, with a link back to this one, for context? – VonC Jun 09 '21 at 09:45
  • Specifying cron job directly in build file worked for me `RUN (crontab -l ; echo "* * * * * echo "Hello world" >> /var/log/cron.log") | crontab`. Maybe because of difference in line endings on Windows – Nat Sep 06 '21 at 23:21
  • What are the last two args of `cron -f -l 2` supposed to do ? Is there a way to force cron command to log to stdout instead of syslog (that is usually disable in docker images?) – digEmAll Jan 14 '22 at 13:57
  • @digEmAll on the first part of your comment: https://github.com/AnalogJ/docker-cron/blob/e75add23dbd6de8508b771fd7a3265b324dd3876/alpine.dockerfile#L9: as a CMD, using shell form `[ "", "", ... ]`. – VonC Jan 14 '22 at 14:09
  • @digEmAll On the second part, it depends on the OS and how the cron service is configured: https://serverfault.com/a/562996/783 – VonC Jan 14 '22 at 14:18
  • @VonC: I know that is the CMD, but I was wondering what `-l 2` do, when passed to cron command. According to "man cron" (on ubuntu) `-l` enables LSB compliance but doesn't accept a value "2"... be aware that I could be missing something really naive, I'm not a linux expert. About the cron service cfg, the problem is that in the base ubuntu image we don't have `rsyslog.d`, so I don't know where the logs are sent at all... it doesn't matter though, I don't want to discuss this in comments, thanks anyway ;) – digEmAll Jan 14 '22 at 15:02
  • @digEmAll OK. Sorry for having misinterpreted your initial question. – VonC Jan 14 '22 at 15:04
  • 1
    @digEmAll Note the `crond` mentioned here is the BusyBox one, for the Alpine image. Not the regular Linux one. See https://unix.stackexchange.com/questions/412805/crond-log-level-meaning. `-l` is for "log level". – VonC Jan 14 '22 at 15:09
  • Your comment could be changed to `# must be ended with a new line "LF" (Unix) and not "CRLF" (Windows)` which took me hours to figure out ;-) – Cord Kaldemeyer Apr 27 '22 at 14:41
  • @CordKaldemeyer Good point. I have edited the answer accordingly. – VonC Apr 27 '22 at 21:56
  • Actually, I found the behavior of `crontab` quite weird because it normally checks for validity but in this case does not complain about a missing new line or the format when using `CRLF` which does not run the cronjob at all. Thanks anyway, for editing as it is one thing one just have to keep in mind! – Cord Kaldemeyer Apr 28 '22 at 05:51
  • Instead of adding gotcha I would edit the answer as `RUN chmod 0644 /etc/cron.d/hello-cron` are wrong permissions and the line is confusing – 4xy Oct 10 '22 at 19:44
  • @4xy Please, do edit the answer, it will clarify it, and help others. – VonC Oct 10 '22 at 20:16
  • Your answer is good enough if we're constrained by traditional `cron` implementations. But what if we aren't? Check out [my answer](https://stackoverflow.com/a/75353647/52499). That also handles @Shmarkus concerns. I'd add a link to my answer to the beginning of yours, but I'm not sure you'll agree. – x-yuri Feb 05 '23 at 16:21
253

The accepted answer may be dangerous in a production environment.

In docker you should only execute one process per container because if you don't, the process that forked and went background is not monitored and may stop without you knowing it.

When you use CMD cron && tail -f /var/log/cron.log the cron process basically fork in order to execute cron in background, the main process exits and let you execute tailf in foreground. The background cron process could stop or fail you won't notice, your container will still run silently and your orchestration tool will not restart it.

You can avoid such a thing by redirecting directly the cron's commands output into your docker stdout and stderr which are located respectively in /proc/1/fd/1 and /proc/1/fd/2.

Using basic shell redirects you may want to do something like this :

* * * * * root echo hello > /proc/1/fd/1 2>/proc/1/fd/2

And your CMD will be : CMD ["cron", "-f"]

But: this doesn't work if you want to run tasks as a non-root.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
hugoShaka
  • 4,977
  • 3
  • 17
  • 29
  • 41
    Nice: `cron -f` is for "cron foreground". I have included your answer in mine above, for more visibility. +1 – VonC Sep 14 '17 at 14:49
  • Let's say my program doesn't output anything. Can I still use this method and be sure my process isn't going to stop in the background? – Arcsector May 18 '18 at 19:34
  • 3
    @Arcsector this method avoid putting a process in the background, thats why it does not fails silently. Having a background process in a docker container is not simple. If you want to have a running background process you might want to use an init process to monitor the multiple processes you run in the container. Another way is to start the process into another container next to the main one called 'sidecar'. The best way is often to avoid multiple processes in the container. – hugoShaka May 22 '18 at 14:14
  • 9
    This is a good solution and works well for us aside from one issue. When the container receives a SIGTERM signal it doesn't seem to wait for the scheduled process to finish and gracefully shutdown, instead it is killing the process which can cause issues. – James Hulse Jan 21 '20 at 11:03
  • This solution worked for me on Debian/Alpine/CentOS containers. This is the most "portable" solution. Thanks for this @hugoShaka – Pierre Sep 17 '21 at 08:32
  • This works and is the MOST portable solution; It also handles issues of Log Rotation and other things as it is echoing output! BEAUTIFUL – FlyingV Sep 23 '21 at 23:21
  • This should be the excepted answer as it really keeps cron running in the foreground and does not use “tail” as the main process. – Wouter Mar 29 '22 at 17:25
  • When cron runs as root but the cronjobs as a regular user, they aren't allowed to write to /proc/1/fd/. Has anybody found a solution? I'm trying to use two fifo files and tailing them but this has its own issues. – Alexander Hartmaier Jun 09 '22 at 18:49
  • Hi. Is there a way to update this so that the output also goes to a log file? – TheDataPanda Jul 26 '22 at 10:55
  • @TheDataPanda Look into the command "tee". It writes io to a file and also std in/out. – Kyle Jul 26 '22 at 13:41
  • Don't you think that traditional `cron` implementations are not `docker`-friendly? Check out [my answer](https://stackoverflow.com/a/75353647/52499). – x-yuri Feb 05 '23 at 16:23
  • In an Azure Container Instance, use `> /proc/$(pidof cron)/fd/1 2>/proc/$(pidof cron)/fd/2`, as its logs show the logs of the cron process instead of the docker process. – Bas May 30 '23 at 14:14
214

For those who wants to use a simple and lightweight image:

FROM alpine:3.6

# copy crontabs for root user
COPY config/cronjobs /etc/crontabs/root

# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]

Where cronjobs is the file that contains your cronjobs, in this form:

* * * * * echo "hello stackoverflow" >> /test_file 2>&1
# remember to end this file with an empty new line

But apparently you won't see hello stackoverflow in docker logs.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Oscar Fanelli
  • 3,337
  • 2
  • 28
  • 40
  • 22
    Simple, light and standard image based. This should be the accepted answer. Also use the `> /proc/1/fd/1 2> /proc/1/fd/2` redirection to access cronjobs output directly from the docker logs. – HenriTel Apr 24 '18 at 08:24
  • 9
    For people not using alpine: The crond supporting the `-d 8` parameter is not the standard cron, it is the crond command from busybox. For example from ubuntu, you can run this as `busybox crond -f -d 8`. For older versions you have to use `-L /dev/stdout/`. – Trendfischer Apr 25 '18 at 13:44
  • For my need - emulating a production setting set of independently running cron jobs where I need to see what is happening - this is great. – Thorbjørn Ravn Andersen May 07 '18 at 11:03
  • 2
    I would give this +100 if I could. This is by far the best way to run cron jobs in a Docker environment. – Jitsusama May 16 '18 at 12:43
  • Will the cron job run normally if I just run it with kubectl run my_instance_name --image gcr.io/my_image Or do I have to do anything else? – Dominique Paul Dec 29 '18 at 14:49
  • 2
    can this be done entirely by docker-compose.yml with an `image:alpine`? – Groostav Jun 01 '19 at 08:57
  • 1
    If you don't want to build a new image every time you change the cron job (or if you need multiple), you can just run Alpine and use a volume to set the cron. I tested it out using the following: `docker run -v ${PWD}/cronjobs:/etc/crontabs/root alpine:3.6 crond -f -d 8`. @Groostav you can use a similar thing in Docker Compose. – duality_ Jul 12 '19 at 07:19
  • 2
    `CMD ["crond"` or `CMD ["cron"`? – codesmith Jun 17 '20 at 13:08
  • 1
    As said [here](https://gist.github.com/andyshinn/3ae01fa13cb64c9d36e7#gistcomment-3633541) you have to be very careful that `/etc/crontabs/root` is owned by `root` and `chmod` to `600` – Anthony O. Feb 25 '21 at 12:17
  • The line sequence should be `LF` for cronjobs file to work. – Masih Jahangiri Sep 16 '21 at 14:01
60

What @VonC has suggested is nice but I prefer doing all cron job configuration in one line. This would avoid cross platform issues like cronjob location and you don't need a separate cron file.

FROM ubuntu:latest

# Install cron
RUN apt-get -y install cron

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

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

# Run the command on container startup
CMD cron && tail -f /var/log/cron.log

After running your docker container, you can make sure if cron service is working by:

# To check if the job is scheduled
docker exec -ti <your-container-id> bash -c "crontab -l"
# To check if the cron service is running
docker exec -ti <your-container-id> bash -c "pgrep cron"

If you prefer to have ENTRYPOINT instead of CMD, then you can substitute the CMD above with

ENTRYPOINT cron start && tail -f /var/log/cron.log

But: if cron dies, the container keeps running.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Youness
  • 1,920
  • 22
  • 28
  • 7
    `RUN apt-get update && apt-get -y install cron` or else it will not be able to find package `cron` – alphabetasoup Aug 29 '17 at 23:08
  • 3
    Thanks Youness, you gave me the idea of doing the following, which worked in my case where each cron is specified in a different file: `RUN cat $APP_HOME/crons/* | crontab` Like a charm :) – marcostvz Nov 30 '17 at 15:51
  • adding `cron` to an entrypoint script seems like the best option: ENTRYPOINT ["entrypoint.sh"] – bozdoz May 08 '20 at 00:45
  • 1
    Using 2 commands in your `ENTRYPOINT` is dangerous. I believe the first one (`cron`) forks to the background, while 2nd one (`tail`) runs in the foreground. If `cron` stops, you'll never know it. If `tail` stops, then docker will notice. – Paul Sturm Mar 05 '21 at 23:43
  • That makes sense to some extent though you can add some monitoring/logging around it (with another entry point or some other monitoring mechanisms) to check the health status of the cron service – Youness Mar 25 '21 at 21:55
27

There is another way to do it, is to use Tasker, a task runner that has cron (a scheduler) support.

Why ? Sometimes to run a cron job, you have to mix, your base image (python, java, nodejs, ruby) with the crond. That means another image to maintain. Tasker avoid that by decoupling the crond and you container. You can just focus on the image that you want to execute your commands, and configure Tasker to use it.

Here an docker-compose.yml file, that will run some tasks for you

version: "2"

services:
    tasker:
        image: strm/tasker
        volumes:
            - "/var/run/docker.sock:/var/run/docker.sock"
        environment:
            configuration: |
                logging:
                    level:
                        ROOT: WARN
                        org.springframework.web: WARN
                        sh.strm: DEBUG
                schedule:
                    - every: minute
                      task: hello
                    - every: minute
                      task: helloFromPython
                    - every: minute
                      task: helloFromNode
                tasks:
                    docker:
                        - name: hello
                          image: debian:jessie
                          script:
                              - echo Hello world from Tasker
                        - name: helloFromPython
                          image: python:3-slim
                          script:
                              - python -c 'print("Hello world from python")'
                        - name: helloFromNode
                          image: node:8
                          script:
                              - node -e 'console.log("Hello from node")'

There are 3 tasks there, all of them will run every minute (every: minute), and each of them will execute the script code, inside the image defined in image section.

Just run docker-compose up, and see it working. Here is the Tasker repo with the full documentation:

http://github.com/opsxcq/tasker

OPSXCQ
  • 526
  • 5
  • 6
  • Dockerception (running docker containers from another container) is a bad practice and should be limited to continuous integration. A workaround would be to use `docker exec` on specified containers. – HenriTel Apr 23 '18 at 09:36
  • 1
    Tasker doesn't use docker in docker (Dind/Dockerception), note that is passed the docker socket as a mapping, all containers spawned are are in spawned in the daemon that tasker runs. And if you don't want to run tasker inside docker, you can just deploy it as any other application. – OPSXCQ Apr 27 '18 at 22:17
  • 2
    I don't get the advantages of using tasker. Seems really a overkill to me using java and sh*** just to run a cron job. – Karl Adler Oct 30 '18 at 14:59
  • Mixing cron and the base image that you need (python/node for example) create a extra dependency that need to be maintained and deployed, in this scenario all jobs share the same container, it means that you have to worry about cleaning up everything after every job runs. Jobs running on tasker are idempotent, so you have less things to worry about. – OPSXCQ Jan 10 '19 at 13:59
19

Though this aims to run jobs beside a running process in a container via Docker's exec interface, this may be of interest for you.

I've written a daemon that observes containers and schedules jobs, defined in their metadata, on them. Example:

version: '2'

services:
  wordpress:
    image: wordpress
  mysql:
    image: mariadb
    volumes:
      - ./database_dumps:/dumps
    labels:
      deck-chores.dump.command: sh -c "mysqldump --all-databases > /dumps/dump-$$(date -Idate)"
      deck-chores.dump.interval: daily

'Classic', cron-like configuration is also possible.

Here are the docs, here's the image repository.

funky-future
  • 3,716
  • 1
  • 30
  • 43
  • 1
    Thank you. This answer is most right for the Docker containers environment. No any changes in Docker images, only adding special container for executing tasks, it works like command ```docker exec ``` by schedule. – PRIHLOP Oct 06 '19 at 10:25
  • This is gold! A very elegant and easy-to-use solution, in the paradigm of containers. Getting rid of crutches. – vatavale Jun 08 '22 at 15:48
14

If you're using docker for windows, remember that you have to change your line-ending format from CRLF to LF (i.e. from dos to unix) if you intend on importing your crontab file from windows to your ubuntu container. If not, your cron-job won't work. Here's a working example:

FROM ubuntu:latest

RUN apt-get update && apt-get -y install cron
RUN apt-get update && apt-get install -y dos2unix

# Add crontab file (from your windows host) to the cron directory
ADD cron/hello-cron /etc/cron.d/hello-cron

# Change line ending format to LF
RUN dos2unix /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/hello-cron.log

# Run the command on container startup
CMD cron && tail -f /var/log/hello-cron.log

This actually took me hours to figure out, as debugging cron jobs in docker containers is a tedious task. Hope it helps anyone else out there that can't get their code to work!

But: if cron dies, the container keeps running.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Andreas Forslöw
  • 2,220
  • 23
  • 32
  • 1
    This helped solve my issue when trying to get output redirection to work. A command like `cat /proc/1/status > /proc/1/fd/1` would return an error from crond stating `crond: USER root pid 6 cmd root cat /proc/1/status > /proc/1/fd/1: nonexistent directory/proc/1/fd/1`. Changing the line endings to Unix enabled me to run the command successfully. Thanks, this took me more than a few hours to figure out! – Daryl Wright Oct 28 '20 at 13:06
13

VonC's answer is pretty thorough. In addition I'd like to add one thing that helped me. If you just want to run a cron job without tailing a file, you'd be tempted to just remove the && tail -f /var/log/cron.log from the cron command.

However this will cause the Docker container to exit shortly after running because when the cron command completes, Docker thinks the last command has exited and hence kills the container. This can be avoided by running cron in the foreground via cron -f.

halfer
  • 19,824
  • 17
  • 99
  • 186
vanugrah
  • 131
  • 1
  • 4
12

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

But, if cron dies, the container keeps running.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Gaurav Tyagi
  • 641
  • 7
  • 7
  • 2
    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) – Peter Jul 04 '21 at 05:05
  • Yes, correct, that was a typo, `/var/log/cron-1.log` should be at every place – Gaurav Tyagi Jul 05 '21 at 08:06
10

I created a Docker image based on the other answers, which can be used like

docker run -v "/path/to/cron:/etc/cron.d/crontab" gaafar/cron

where /path/to/cron: absolute path to crontab file, or you can use it as a base in a Dockerfile:

FROM gaafar/cron

# COPY crontab file in the cron directory
COPY crontab /etc/cron.d/crontab

# Add your commands here

For reference, the image is here.

vvvvv
  • 25,404
  • 19
  • 49
  • 81
gafi
  • 12,113
  • 2
  • 30
  • 32
6

Define the cronjob in a dedicated container which runs the command via docker exec to your service.

This is higher cohesion and the running script will have access to the environment variables you have defined for your service.

#docker-compose.yml
version: "3.3"
services:
    myservice:
      environment:
        MSG: i'm being cronjobbed, every minute!
      image: alpine
      container_name: myservice
      command: tail -f /dev/null

    cronjobber:
     image: docker:edge
     volumes:
      - /var/run/docker.sock:/var/run/docker.sock
     container_name: cronjobber
     command: >
          sh -c "
          echo '* * * * * docker exec myservice printenv | grep MSG' > /etc/crontabs/root
          && crond -f"
Jakob Eriksson
  • 18,597
  • 1
  • 25
  • 34
  • I was unable to get this to work using docker swarm. Getting `myservice unknown` errors. – Mark Grimes Jul 18 '18 at 17:13
  • 2
    There should be a warning about the high security impact mounting a docker socket has: https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container.html – casual Oct 22 '19 at 14:28
6

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" ]

But output of the tasks apparently can't be seen in docker logs.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Iljanne
  • 61
  • 1
  • 2
  • The `-d` parameter requires the log level as argument. You should change your `CMD` line to: `CMD [ "crond", "-f", "-d", "8" ]` – langfingaz Jan 14 '23 at 12:45
5

When you deploy your container on another host, just note that it won't start any processes automatically. You need to make sure that 'cron' service is running inside your container. In our case, I am using Supervisord with other services to start cron service.

[program:misc]
command=/etc/init.d/cron restart
user=root
autostart=true
autorestart=true
stderr_logfile=/var/log/misc-cron.err.log
stdout_logfile=/var/log/misc-cron.out.log
priority=998
Priya
  • 1,359
  • 6
  • 21
  • 41
Sagar Ghuge
  • 231
  • 3
  • 9
  • I get an error in supervisor.log that the cron service stopped multiple times and entered a FATAL state. However cron seems to be running in top and executing cronjobs normally. Thanks for this! – lephleg Jan 14 '17 at 23:13
  • Yes, same thing happened to me as well, but it works as normal, so don't need to bother. – Sagar Ghuge Feb 06 '17 at 10:42
5

From above examples I created this combination:

Alpine Image & Edit Using Crontab in Nano (I hate vi)

FROM alpine

RUN apk update
RUN apk add curl nano

ENV EDITOR=/usr/bin/nano 

# start crond with log level 8 in foreground, output to stderr
CMD ["crond", "-f", "-d", "8"]

# Shell Access
# docker exec -it <CONTAINERID> /bin/sh

# Example Cron Entry
# crontab -e
# * * * * * echo hello > /proc/1/fd/1 2>/proc/1/fd/2
# DATE/TIME WILL BE IN UTC
Dan Watts
  • 109
  • 1
  • 3
5

Setup a cron in parallel to a one-time job

Create a script file, say run.sh, with the job that is supposed to run periodically.

#!/bin/bash
timestamp=`date +%Y/%m/%d-%H:%M:%S`
echo "System path is $PATH at $timestamp"

Save and exit.

Use Entrypoint instead of CMD

f you have multiple jobs to kick in during docker containerization, use the entrypoint file to run them all.

Entrypoint file is a script file that comes into action when a docker run command is issued. So, all the steps that we want to run can be put in this script file.

For instance, we have 2 jobs to run:

Run once job: echo “Docker container has been started”

Run periodic job: run.sh

Create entrypoint.sh

#!/bin/bash

# Start the run once job.
echo "Docker container has been started"

# Setup a cron schedule
echo "* * * * * /run.sh >> /var/log/cron.log 2>&1
# This extra line makes it a valid cron" > scheduler.txt

crontab scheduler.txt
cron -f

Let’s understand the crontab that has been set up in the file

* * * * *: Cron schedule; the job must run every minute. You can update the schedule based on your requirement.

/run.sh: The path to the script file which is to be run periodically

/var/log/cron.log: The filename to save the output of the scheduled cron job.

2>&1: The error logs(if any) also will be redirected to the same output file used above.

Note: Do not forget to add an extra new line, as it makes it a valid cron. Scheduler.txt: the complete cron setup will be redirected to a file.

Using System/User specific environment variables in cron

My actual cron job was expecting most of the arguments as the environment variables passed to the docker run command. But, with bash, I was not able to use any of the environment variables that belongs to the system or the docker container.

Then, this came up as a walkaround to this problem:

  1. Add the following line in the entrypoint.sh
declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env
  1. Update the cron setup and specify-
SHELL=/bin/bash
BASH_ENV=/container.env

At last, your entrypoint.sh should look like

#!/bin/bash

# Start the run once job.
echo "Docker container has been started"

declare -p | grep -Ev 'BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID' > /container.env

# Setup a cron schedule
echo "SHELL=/bin/bash
BASH_ENV=/container.env
* * * * * /run.sh >> /var/log/cron.log 2>&1
# This extra line makes it a valid cron" > scheduler.txt

crontab scheduler.txt
cron -f

Last but not the least: Create a Dockerfile

FROM ubuntu:16.04
MAINTAINER Himanshu Gupta

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

# Add files
ADD run.sh /run.sh
ADD entrypoint.sh /entrypoint.sh
 
RUN chmod +x /run.sh /entrypoint.sh

ENTRYPOINT /entrypoint.sh

That’s it. Build and run the Docker image!

tripleee
  • 175,061
  • 34
  • 275
  • 318
himanshuIIITian
  • 5,985
  • 6
  • 50
  • 70
  • 1
    @himanshuIIITian I tried this, the issue is that the script of the "run once job" is never returning and also the corn -f is not returning so... this is not working for me, any ideas? thanks – Doron Levi May 19 '20 at 08:54
  • @DoronLevi - can you please share some logs to look into the issue? Or you can check the whole code from here - https://github.com/nehabhardwaj01/docker-cron – himanshuIIITian May 19 '20 at 10:28
  • 1
    You don't need ENTRYPOINT to run multiple commands. And generally the description/solution seems too complex. – x-yuri Feb 05 '23 at 15:32
5

Here's my docker-compose based solution:

  cron:
    image: alpine:3.10
    command: crond -f -d 8
    depends_on:
      - servicename
    volumes:
      - './conf/cron:/etc/crontabs/root:z'
    restart: unless-stopped

the lines with cron entries are on the ./conf/cron file.

Note: this won't run commands that aren't in the alpine image.

Also, output of the tasks apparently won't appear in docker logs.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
That Brazilian Guy
  • 3,328
  • 5
  • 31
  • 49
4

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.

pablorsk
  • 3,861
  • 1
  • 32
  • 37
  • 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 Jun 17 '22 at 14:19
  • 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](https://stackoverflow.com/a/72735422/1665966). – Johnson_145 Jun 23 '22 at 19:14
4

I occasionally tried to find a docker-friendly cron implementation. And this last time I tried, I've found a couple.

By docker-friendly I mean, "output of the tasks can be seen in docker logs w/o resorting to tricks."

The most promising I see at the moment is supercronic. It can be fed a crontab file, all while being docker-friendly. To make use of it:

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

A gist with a bit more info.

Another good one is yacron, but it uses YAML.

ofelia can be used, but they seem to focus on running tasks in separate containers. Which is probably not a downside, but I'm not sure why I'd want to do that.

And there's also a number of traditional cron implementations: dcron, fcron, cronie. But they come with "no easy way to see output of the tasks."

x-yuri
  • 16,722
  • 15
  • 114
  • 161
  • A good alternative to my [answer indeed](https://stackoverflow.com/a/37458519/6309). – VonC Feb 05 '23 at 16:55
2

this line was the one that helped me run my pre-scheduled task.

ADD mycron/root /etc/cron.d/root

RUN chmod 0644 /etc/cron.d/root

RUN crontab /etc/cron.d/root

RUN touch /var/log/cron.log

CMD ( cron -f -l 8 & ) && apache2-foreground # <-- run cron

--> My project run inside: FROM php:7.2-apache

But: if cron dies, the container keeps running.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Santiago Vasquez
  • 149
  • 1
  • 10
2

With multiple jobs and various dependencies like zsh and curl, this is a good approach while also combining the best practices from other answers. Bonus: This does NOT require you to set +x execution permissions on myScript.sh, which can be easy to miss in a new environment.

cron.dockerfile

FROM ubuntu:latest

# Install dependencies
RUN apt-get update && apt-get -y install \
  cron \
  zsh \
  curl;

# Setup multiple jobs with zsh and redirect outputs to docker logs
RUN (echo "\
* * * * * zsh -c 'echo "Hello World"' 1> /proc/1/fd/1 2>/proc/1/fd/2 \n\
* * * * * zsh /myScript.sh 1> /proc/1/fd/1 2>/proc/1/fd/2 \n\
") | crontab

# Run cron in forground, so docker knows the task is running
CMD ["cron", "-f"]

Integrate this with docker compose like so:

docker-compose.yml

services:
  cron:
    build:
      context: .
      dockerfile: ./cron.dockerfile
    volumes:
      - ./myScript.sh:/myScript.sh

Keep in mind that you need to docker compose build cron when you change contents of the cron.dockerfile, but changes to myScript.sh will be reflected right away as it's mounted in compose.

Cwista
  • 361
  • 1
  • 11
1

When running on some trimmed down images that restrict root access, I had to add my user to the sudoers and run as sudo cron

FROM node:8.6.0
RUN apt-get update && apt-get install -y cron sudo

COPY crontab /etc/cron.d/my-cron
RUN chmod 0644 /etc/cron.d/my-cron
RUN touch /var/log/cron.log

# Allow node user to start cron daemon with sudo
RUN echo 'node ALL=NOPASSWD: /usr/sbin/cron' >>/etc/sudoers

ENTRYPOINT sudo cron && tail -f /var/log/cron.log

Maybe that helps someone

But: if cron dies, the container keeps running.

x-yuri
  • 16,722
  • 15
  • 114
  • 161
Senica Gonzalez
  • 7,996
  • 16
  • 66
  • 108
  • I believe the node image uses the node user; so maybe you needed to add permissions for that user – bozdoz May 08 '20 at 00:44
1

So, my problem was the same. The fix was to change the command section in the docker-compose.yml.

From

command: crontab /etc/crontab && tail -f /etc/crontab

To

command: crontab /etc/crontab

command: tail -f /etc/crontab

The problem was the '&&' between the commands. After deleting this, it was all fine.

random2137
  • 139
  • 2
  • 14
  • The second command overrides the first one. That is, having 2 command keys equals to having only the last one (the last one wins). – x-yuri Feb 05 '23 at 15:40
1

Focusing on gracefully stopping the cronjobs when receiving SIGTERM or SIGQUIT signals (e.g. when running docker stop).

That's not too easy. By default, the cron process just got killed without paying attention to running cronjobs. I'm elaborating on pablorsk's answer:

Dockerfile:

FROM ubuntu:latest

RUN apt-get update \
    && apt-get -y install cron procps \
    && rm -rf /var/lib/apt/lists/*

# Copy cronjobs file to the cron.d directory
COPY cronjobs /etc/cron.d/cronjobs

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

# similarly prepare the default cronjob scripts
COPY run_cronjob.sh /root/run_cronjob.sh
RUN chmod +x /root/run_cronjob.sh
COPY run_cronjob_without_log.sh /root/run_cronjob_without_log.sh
RUN chmod +x /root/run_cronjob_without_log.sh

# Apply cron job
RUN crontab /etc/cron.d/cronjobs

# to gain access to environment variables, we need this additional entrypoint script
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# optionally, change received signal from SIGTERM TO SIGQUIT
#STOPSIGNAL SIGQUIT

# Run the command on container startup
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh:

#!/bin/bash
# make global environment variables available within crond, too
printenv | grep -v "no_proxy" >> /etc/environment

# SIGQUIT/SIGTERM-handler
term_handler() {
  echo 'stopping cron'
  service cron stop
  echo 'stopped'
  echo 'waiting'
  x=$(($(ps u -C run_cronjob.sh | wc -l)-1))
  xold=0
  while [ "$x" -gt 0 ]
  do
    if [ "$x" != "$xold" ]; then
      echo "Waiting for $x running cronjob(s):"
      ps u -C run_cronjob.sh
      xold=$x
      sleep 1
    fi
    x=$(($(ps u -C run_cronjob.sh | wc -l)-1))
  done
  echo 'done waiting'
  exit 143; # 128 + 15 -- SIGTERM
}

# cron service with SIGTERM and SIGQUIT support
service cron start
trap "term_handler" QUIT TERM

# endless loop
while true
do
  tail -f /dev/null & wait ${!}
done

cronjobs

* * * * * ./run_cronjob.sh cron1
*/2 * * * * ./run_cronjob.sh cron2
*/3 * * * * ./run_cronjob.sh cron3

Assuming you wrap all your cronjobs in a run_cronjob.sh script. That way, you can execute arbitrary code for which shutdown will wait gracefully.

run_cronjobs.sh (optional helper script to keep cronjob definitions clean)

#!/bin/bash

DIR_INCL="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR_INCL" ]]; then DIR_INCL="$PWD"; fi
cd "$DIR_INCL"

# redirect all cronjob output to docker
./run_cronjob_without_log.sh "$@" > /proc/1/fd/1 2>/proc/1/fd/2

run_cronjob_without_log.sh

your_actual_cronjob_src()

Btw, when receiving a SIGKILL the container still shut downs immediately. That way you can use a command like docker-compose stop -t 60 cron-container to wait 60s for cronjobs to finish gracefully, but still terminate them for sure after the timeout.

Johnson_145
  • 1,994
  • 1
  • 17
  • 26
1

All the answers require root access inside the container because 'cron' itself requests for UID 0. To request root acces (e.g. via sudo) is against docker best practices. I used https://github.com/gjcarneiro/yacron to manage scheduled tasks.

0

Just adding to the list of answers that you can also use this image: https://hub.docker.com/repository/docker/cronit/simple-cron

And use it as a basis to start cron jobs, using it like this:

FROM cronit/simple-cron # Inherit from the base image
#Set up all your dependencies 
COPY jobs.cron ./ # Copy your local config
Alexis Clarembeau
  • 2,776
  • 14
  • 27
0

Evidently, it is possible to run cron as a process inside the container (under root user) alongside other processes , using ENTRYPOINT statement in Dockerfile with start.sh script what includes line process cron start. More info here

#!/bin/bash

# copy environment variables for local use
env >> etc/environment

# start cron service
service cron start

# start other service
service other start
#...
0

If your image doesn't contain any daemon (so it's only the short-running script or process), you may also consider starting your cron from outside, by simply defining a LABEL with the cron information, plus the scheduler itself. This way, your default container state is "exited". If you have multiple scripts, this may result in a lower footprint on your system than having multiple parallel-running cron instances.

See: https://github.com/JaciBrunning/docker-cron-label

Example docker-compose.yaml:

version: '3.8'

# Example application of the cron image
services:
  cron:
    image: jaci/cron-label:latest
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "/etc/localtime:/etc/localtime:ro"

  hello:
    image: hello-world
    restart: "no"
    labels:
      - "cron.schedule=* * * * * "
Daniel Alder
  • 5,031
  • 2
  • 45
  • 55
0

I wanted to share a modification to the typical off of some of these other suggestions that I found more flexible. I wanted to enable changing the cron time with an environment variable and ended up adding an additional script that runs within my entrypoint.sh, but before the call to cron -f

*updatecron.sh*
#!/bin/sh
#remove old cron files
rm -rf /etc/cron.*/*
#create a new formatted cron definition
echo "$crondef [appname] >/proc/1/fd/1 2>/proc/1/fd/2" >> /etc/cron.d/restart-cron
echo \ >> /etc/cron.d/restart-cron
chmod 0644 /etc/cron.d/restart-cron
crontab /etc/cron.d/restart-cron

This removes any existing cron files, creates a new cronfile using an ENV variable of crondef, and then loads it.

0

Our's was a nodejs application to be run as cron job and it was also dependent on environment variables.

The below solution worked for us.

Docker file:

# syntax=docker/dockerfile:1
FROM node:12.18.1
ENV NODE_ENV=production

COPY ["startup.sh", "./"]

# Removed steps to build the node js application

#--------------- Setup cron ------------------
# Install Cron
RUN apt-get update
RUN apt-get -y install cron
 
# Run every day at 1AM
#/proc/1/fd/1 2>/proc/1/fd/2 is used to redirect cron logs to standard output and standard error
RUN (crontab -l ; echo "0 1 * * * /usr/local/bin/node /app/dist/index.js  > /proc/1/fd/1 2>/proc/1/fd/2") | crontab
    
#--------------- Start Cron ------------------
# Grant execution rights
RUN chmod 755 startup.sh
CMD ["./startup.sh"]

startup.sh:

!/bin/bash
echo "Copying env variables to /etc/environment so that it is available for cron jobs"
printenv >> /etc/environment
echo "Starting cron"
cron -f
Baga
  • 1,354
  • 13
  • 24
0

For those who wants to use a simple php-fpm image:

FROM php:7.4-fpm

RUN apt-get update \
&& apt-get install -y cron\ 
...

# Copy your cronjob file
COPY /cron/cronjob /etc/cron.d/crontab
COPY /cron/log /var/log

# owner can read and write into the crontab, group and others can read it
RUN chmod 0644 /etc/cron.d/crontab
# running our crontab using the binary from the package we installed
RUN /usr/bin/crontab /etc/cron.d/crontab

CMD cron && docker-php-entrypoint php-fpm

Not that this part is required if you want to use this Image in docker-compose.yml file : && docker-php-entrypoint php-fpm

if not, I had an error : 502 bad gateway issue (111: Connection refused) while connecting to upstream) by nginx image.

I hope this can help someone

Samuel Messigah
  • 111
  • 1
  • 3
0

This code has successfully worked for me. I placed the script.sh file inside the project folder, and both the script.sh and main.py Docker files are located at the same directory level. I made the following modifications to the Docker file in order to execute the Cron-job within the Docker container.

Script.sh file

#!/bin/bash
# script.sh

sleep 0
wget http://127.0.0.1:8081/v1.0/recommend/cronjob

Docker File

# Choose our version of Python
FROM python:3.11

# Set up a working directory
WORKDIR /opt/myapp
# Copy the code into the working directory
COPY . /opt/myapp
# Upgrade PIP
RUN pip install --upgrade pip
# Install requirements
RUN pip install -r requirements.txt

EXPOSE 8081

# Start both FastAPI and cron jobs
ADD script.sh /root/script.sh
RUN chmod 0644 /root/script.sh
RUN apt-get update
RUN apt-get -y install cron
RUN crontab -l | { cat; echo "30 2 * * * bash /root/script.sh"; } | crontab -

# Tell uvicorn to start spin up our code, which will be running inside the 
container now
CMD ["bash", "-c", "cron && uvicorn main:app --host 0.0.0.0 --port 8081"]
Buddhika Lakshan
  • 182
  • 2
  • 13