dockercrondockerfile

Docker Container ubuntu image cron service not running


Have the following Dockerfile

FROM ubuntu:20.04
FROM postgres:17.0

##### packages needed that dont come with the image of ubuntu:20.04
RUN apt-get update \
&& apt-get install -y bash \
&& apt-get install -y cron \
&& apt-get install nano
#&& apt-get install -y iputils-ping
#RUN sudo apt install postgresql

COPY backup.sh /usr/local/bin/
COPY setup.sh /usr/local/bin/ 
WORKDIR /usr/local/bin/
RUN chmod +x backup.sh
RUN chmod +x setup.sh
RUN ./setup.sh

# Add the cron job to the crontab
RUN crontab -l | { cat; echo "* * * * * /usr/local/bin/backup.sh 10 'localhost' '5432' 'postgres' 'xxxxx' 'dvdrental' >> /mnt/backups/backup.log 2>&1"; } | crontab -

CMD ["/bin/bash","cron"]

The command to build the container

sudo docker build --no-cache -t hcorte/backup_container .

The command to run the container

sudo docker run --name ubuntu -v /mnt/backups/:/mnt/backups/ --add-host host.docker.internal:host-gateway -itd hcorte/backup_container bash

when attach to container and run the command service cron status returns

cron is not running ... failed!

but doesn't the CMD ["/bin/bash","cron"] indicate to start cron service? what change is need in Dockerfile so the cron service is already start at the begining?


Solution

  • TL;DR: change the last line of the Dockerfile to CMD ["cron", "-f"], and don't put bash at the end of the docker run command.


    A container only runs a single process. You can't start background services in a Dockerfile, and by and large commands like service just don't work.

    In your docker run command, you have

    docker run ... -itd hcorte/backup_container \
      bash
    

    Anything after the image name is taken as the command to run, and replaces the Dockerfile CMD. Since the container only runs one process, the shell runs instead of the cron daemon you're trying to run, and that's why when you get a debugging shell inside the container, you can't find the cron daemon.

    In the Dockerfile CMD line, you shouldn't usually need to mention bash or another shell. The syntax with square brackets (a JSON array) runs exactly the command specified, with no additional quoting or escaping; or you can just write out a command, and it will be implicitly passed through /bin/sh -c (or the current SHELL).

    In your case, you want the main container process to be the cron daemon; nothing else will run in the container. This is just a simple command and doesn't need any sort of variable expansion, so the exec form (JSON-array syntax) works fine. For Debian cron the -f option causes it to run in the foreground (there is at least one other cron implementation with a different option).

    If you do docker exec into the container, service still won't show the cron daemon running. If you've installed ps, though, it will be there with a process ID of 1.


    There's a couple more things you should consider cleaning up in the Dockerfile.

    An image has only one parent image. Having two FROM lines creates a multi-stage build; it doesn't cause the ubuntu and postgres images to be combined. I'd delete the first FROM line.

    Beyond the cron daemon, the tools you install with APT aren't necessary. Debian-based images already include GNU bash (and you don't use any of its features); you won't normally need to edit files inside containers with nano, or send raw ICMP packets with ping.

    If files are checked into source control with the executable bit set, you won't need to RUN chmod +x them. You can COPY multiple files into one directory in one shot. Since /usr/local/bin is in the default $PATH, you don't need to change WORKDIR or use a path to run the scripts.

    Taking those changes together, you'd get a final Dockerfile

    FROM postgres:17.0
    
    RUN apt-get update \
     && DEBIAN_FRONTEND=noninteractive apt-get install -y cron
    
    COPY backup.sh setup.sh /usr/local/bin/
    RUN setup.sh
    
    RUN crontab -l | { cat; echo "* * * * * /usr/local/bin/backup.sh 10 'localhost' '5432' 'postgres' 'xxxxx' 'dvdrental' >> /mnt/backups/backup.log 2>&1"; } | crontab -
    
    CMD ["cron", "-l"]