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