Docker ✕ cron : the struggle of web project
Run cronjob beside classical website with docker-compose
Published Nov 27, 2017 
Tags: Docker docker-compose php cron
This came accross multiple project :
How can I run a command periodically ? Obviously, on Linux you can use a
crontab.
But when docker arrives :
How can I run a crontab without explicitly redo a Dockerfile ? And there was too many options.
Current situation
-  
A website is built with ExpressJS on node so we have a process which run the web server.
 -  
A script named
my-awesome-commandin thebindirectory is executing some process. -  
We are in development environment using this
docker-composefileversion: '2' services: myapp: build: . volumes: - .:/app - /app/node_modules ports: - 3000:3000And Dockerfile for the app :
FROM node:8 ADD . app WORKDIR app RUN npm install EXPOSE 3000 ENTRYPOINT ["npm", "start"]Sample working repository : Running express web server
 
Give it a try
I want to run a job. So in the Dockerfile I will add some line to register this cron.
FROM node:8
ADD . app
WORKDIR app
RUN npm install
EXPOSE 3000
RUN apt-get update && apt-get install -y cron
RUN echo '* * * * * cd /app && .bin/my-awesome-command > /app/logs/crontab.log 2>&1' > /etc/crontab
RUN crontab /etc/crontab
ENTRYPOINT ["npm", "start"]
Sample working repository : Installing crontab in Dockerfile
Now try running docker-compose up -d --build, the webpage will be available at http://localhost:3000/, but my-awesome-command should write some things to crontab.log, where is this crappy file ?!
Take a look at the output of the docker build :
Step 7/9 : RUN echo '* * * * * cd /app && .bin/my-awesome-command > /app/logs/crontab.log 2>&1' > /etc/crontab
 ---> Running in eb9ec8fd5200
 ---> bdcebf265736
Removing intermediate container eb9ec8fd5200
Step 8/9 : RUN crontab /etc/crontab
 ---> Running in c20b3d7ea6df
 ---> a1926b68ecb2
Removing intermediate container c20b3d7ea6df
I’m not an expert of Docker but I’ve found that the fact that crontab /etc/crontab is executed inside an intermediate container totally fucked our crontab … How can I execute this job ?
The solution
The solution is to build another image in docker-compose with the same configuration as myapp except for one thing. This will not execute the web server this will execute the crontab !
We need to do 2 things :
Adding a service for docker-compose 
 Just copy the myapp service to another service mycron :
version: '2'
services:
  myapp:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - 3000:3000
  mycron:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
#    we don't need ports with this because it will execute the crontab
#    ports:
#      - 3000:3000
Now the real solution is overwriting the entrypoint of the Dockerfile. In myapp it’s npm start but in mycron this will be a script.
So, add entrypoint: sh /app/crontab.sh under mycron definition.
Set up a script
Let’s create the crontab.sh file.
What will be used for ?
- Create the log file
 - Registering the command to be executed by the crontab
 - Start the crontab
 - (optionnal) display logs
 
#!/usr/bin/env bash
# Ensure the log file exists
touch /app/logs/crontab.log
# Ensure permission on the command
chmod a+x /app/bin/my-awesome-command
# Added a cronjob in a new crontab
echo "* * * * * /usr/local/bin/node /app/bin/my-awesome-command >> /app/logs/crontab.log 2>&1" > /etc/crontab
# Registering the new crontab
crontab /etc/crontab
# Starting the cron
/usr/sbin/service cron start
# Displaying logs
tail -f /app/logs/crontab.log
Sample working repository : Using an entrypoint script
Now build then start, you should have this output :