Sometime you need to run a timed function on your dockerized php-apache system. Googling around you find you have to build a dedicated container in which setup the crontab or "better" put your tasks in the host crontab. I don't like any of these solutions: I want my cronned jobs called from the apache stack.

After a couple of hour of work and the reading of the f***ing manual, I found a possible solution, may be not the best but it works.

Tree structure

Well, to setup a test stack, prepare the folder structure with the www and log directories, the Dockerfile, the docker-compose.yml and other things as shown below:

./
├── .env
├── Dockerfile
├── cron
├── docker-compose.yml
├── log/
│   ├── apache2/
│   └── cron/
└── www/
    └── index.php

The main files are:

  • Dockerfile: define your image inheriting from php:apache (latest)
  • cron: put your tasks with the usual crontab syntax
  • docker-compose.yml: define the system as a whole
  • index.php: a simple test file
  • .env: the customization environment variables

Let's have a fast look at each document

.env

Some basic settings, e.g the host port for final apache service:

HTTP_PORT=8080

cron

The crontab test file:

#     ,------ MINUTE
#    / ,----- HOUR
#   / / ,---- DAY OF MONTH
#  / / / ,--- MONTH
# / / / / ,-- DAY OF WEEK
#/ / / / /
* * * * *  echo "`date`: hello, world" >> /var/log/cron/cron.log
#

Pay attention to the destination of log of the test command: the folder /var/log/cron will be mounted from the local host folder ./log/cron.

Dockerfile

The php:apache image enriched with the working cron process:

FROM php:apache

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

RUN cp /usr/share/zoneinfo/Europe/Rome /etc/localtime && \
    echo "Europe/Rome" > /etc/timezone

#RUN apt-get -y remove tzdata
RUN rm -rf /var/cache/apk/*

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

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

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

# Create the log file to be able to run tail
RUN mkdir -p /var/log/cron

# Add a command to base-image entrypont scritp
RUN sed -i 's/^exec /service cron start\n\nexec /' /usr/local/bin/apache2-foreground

The new docker image inherits from php:apache latest official image, adds the correct timezone, setups the cron job and, finally -the hardest poin-t: modifies the entrypoint script of the inherited image (apache2-foreground) adding the service start cron just before the launch of the apache process. Remember that each container is hung up to ta single running process. In php:apache that should be apache itself (and not the cron). So the idea is to start cron as a service and before the container single running process.

docker-compose.yml

The instructions for the docker-compose command:

version: '3.1'
services:
  web:
    build: .
    image: phpcron
    container_name: crontest
    restart: unless-stopped
    volumes:
      - ./www:/var/www/html
      - ./log/apache2:/var/log/apache2
      - ./log/cron:/var/log/cron
    ports:
      - $HTTP_PORT:80

The stack is limited the unique service web: build from the Dockerfile, defines the mount volumes and some other detail.

index.php

Finally put something like this in the index page:

<h1><?= date('Y-m-d H:i:s'); ?></h1>
<?php
phpinfo();

The running test

Now you can test the system with the command:

docker-compose up

and then look inside the log file cron.log that should appear in the ./log/cron/ folder for the every minute 'hello, world' line.

Also test your localhost:8080 to get the Php Info page correctly shown.

;)