Recently, I needed to get cron working inside a Docker container running Debian Slim. It’s not difficult once you figure it out, but it did take a bit of research and learning to get everything to work.
First off, Debian Slim is real slim. There’s no cron
nor is there a syslog
when you want to debug things. Add apt-get install
cron
and rsyslog
in your Dockerfile before you start anything else. With syslog
installed, you can tail /var/log/syslog
while you’re debugging your cron files, which is incredibly helpful.
In my case, I wanted to run a few Python scripts on a schedule. Cron natively uses sh
. I’m sure there’s a way to get it to work that way, but I didn’t want to play around with that. I added my scripts directory in Docker to PATH
in my cronfile and set bash
as my SHELL
. My container runs in Docker Swarm, so it was also important that the output for my script landed in the same STDOUT that eventually ends up in docker.log
. /proc/1/rd/1
is the trick for getting output in the right place.
First, I created a little shell script — build_projects_env.sh
— for my container to run to get things ready for cron. This script enables Bash’s job control, makes sure environment variables are in a place cron can get to them at, and starts up the cron service itself.
#!/bin/bash
# turn on bash's job control
set -m
# extract environment variables for cron
printenv | sed 's/^\(.*\)$/export \1/g' > /root/project_env.sh
# Start the helper processes
service rsyslog start
service cron start
Next, I created my cron file. The below example is where my cron file ended up, once I got everything working. It sets up PATH
to point to my scripts, changes the shell to bash
, make sure cron can see environment variables, and gets my script ready to run each hour piping its output to STDOUT and errors to STDERR.
PATH=/your-working-directory/cron-scripts:/usr/local/bin:rest-of-path-goes-here
SHELL=/bin/bash
BASH_ENV=/root/project_env.sh
0 * * * * scheduled_script.py > /proc/1/fd/1 2>&1
Having a valid cron file is just half the battle in getting cron to run successfully in Docker. In your Docker file you’ll need to make sure your script is executable and copy your cron file to the right place. Make sure your new cron file is executable with a little chmod
and chown
and then apply your changes.
# Give execution rights on the cron job
RUN mkdir crontab
# Copy or create your cron file named crontab into the root directory crontab
RUN chown -R root crontab && chmod -R 0644 crontab
# Apply cron job
RUN crontab crontab/crontab
By default the cron and syslog service will not start on their own. Make sure you start those two service before you run any of your own commands. I do that in my little shell script, but you can do it elsewhere.
CMD ["sh", "-c", "./scripts/build_project_env.sh ; other_commands_here"]
The above is really all you need to get things going. But… your Docker container is going to default to UTC. That’s fine and all, but when I’m setting up specific schedules in cron, I like to think in local time. The below command is one of many ways to change your time zone. Is it the best? I don’t know. It worked for me.
RUN apt-get install tzdata -y
RUN ln -sf /usr/share/zoneinfo/US/Central /etc/localtime
RUN echo "US/Central" | tee /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata