Dokku, PHP apps, and Crontab tasks

I decided to try Dokku for hosting PHP apps to simplify the process of upgrading code. I mostly followed guide http://www.broculos.net/2015/12/dokku-how-to-deploy-vanilla-php.html with a few exceptions (using the apache2 handler, adding persistent volumes for storing uploads, etc.)

In my case, I wanted to host an instance of Mautic. Mautic relies on a dozen or so cron tasks to be run in the background to do essential jobs - importing contacts, sending emails, checking bounces, etc. It turned out there is no Dokku plugin or a standard way of running these cron tasks.

Dokku documentation suggests using out-of-container crontab - https://github.com/dokku/dokku/blob/master/docs/deployment/one-off-proce... by making cron task spawn a new process, enter it, run its commands and exit process, scaling it down afterwards.

That sounds wasteful - scaling, entering running docker instance takes a few seconds for Dokku. I also have to manually copy crontab config on the Dokku host instead of keeping it as part of the deployed app repository.

I found a relatively easy solution to manage it. It involves an additional to 'web' process inside Procfile. I called it cron. Since Mautic needs to have its cron tasks to run quite often, it made sense to keep cron process running all the time. And that process would run all necessary cron tasks.

I started with defining crontab file for all tasks Mautic needs. I did some digging and debugging to find correct $PATH to allow bash to find bundled PHP executable:

$ cat deploy/crontab
SHELL=/bin/bash
PATH="/app/.heroku/php/bin:/app/.heroku/php/sbin:$PATH"
 
8,23,38,52 * * * *     php /app/app/console mautic:segments:update
       */5 * * * *     php /app/app/console mautic:import
5,20,35,50 * * * *     php /app/app/console mautic:campaigns:rebuild
2,17,32,47 * * * *     php /app/app/console mautic:campaigns:trigger
0,15,30,45 * * * *     php /app/app/console mautic:messages:send
0,15,30,45 * * * *     php /app/app/console mautic:emails:send
0,15,30,45 * * * *     php /app/app/console mautic:email:fetch
0,15,30,45 * * * *     php /app/app/console mautic:social:monitoring
0,15,30,45 * * * *     php /app/app/console mautic:webhooks:process
0,15,30,45 * * * *     php /app/app/console mautic:broadcasts:send
         * 1 * * *     php /app/app/console mautic:maintenance:cleanup --days-old=365
        0 4 15 * *     php /app/app/console mautic:iplookup:download
       */5 * * * *     php /app/app/console mautic:reports:scheduler
 
# download geoip db on start if it does not exist
@reboot                [[ "$(ls -A /app/app/cache/ip_data 2>/dev/null)" ]] || php /app/app/console mautic:iplookup:download

I also needed a script to be run by Dokku's cron process. That script had run infinitely to keep docker container from exiting, hence sleep infinity. Make sure to make it executable:

$ cat deploy/cron.sh
#!/bin/bash
 
crontab /app/deploy/crontab
sleep infinity

Once these pieces are in place, add cron process definition into Procfile:

$ cat Procfile
web: vendor/heroku/heroku-buildpack-php/bin/heroku-php-apache2
cron: ./deploy/cron.sh

And last but not least, tell Dokku to keep one instance of cron process running:

$ cat DOKKU_SCALE
web=1
cron=1

Commit all these changes into your application repository and push to Dokku host - assuming paths and file permissions are correct, you should get your cron tasks running.