Crontab in Laravel

I have a Laravel application built on top of Docker linked to AWS ECS. The application is dockerized, and I wish to run crontab on that application

Hi

Are you using laravels internal scheduler?

If so, i just run “php artisan schedule:work &” before the main application/apache starts in my entrypoint script

1 Like

We are using crontab:

          • /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1

But as far as I have understood, crontab will not get effective unless it is executed by some supervisor or service (i.e. docker-compose). Is that right?

docker-compose is a way of managing your containers, instead of using “docker run”, you can use compose to make a recipe of your container, so you dont have to remember your docker run command.

The supervisor part is true though, what docker does, is that it starts 1 application when it starts, it could be apache, but it could also be something like supervisor, and then supervisor spawns cron and apache.

There is some examples of this here or i made an complete example here

1 Like

I have had some success using multipurpose images to run the Laravel Scheduler and Laravel Queues.

Means I build my image using a “start.sh” that will be my entrypoint.

In that “start.sh” I read a passed environment var like “CONTAINER_ROLE” and start what I want with that.

Example in a docker-compose.yml:
environment:
CONTAINER_ROLE: app // will start php-fpm
CONTAINER_ROLE: scheduler // will start php artisan scheduler:work
CONTAINER_ROLE: queue // will start php artisan queue:work

This way it is a breeze to scale the queue on demand.
You can scale it through the CLI or you can deploy more than one container through docker-compose.

I have never have success using the cron, because my containers cant run as root on kubernetes, so I have found out some solutions.

Here is how I build my container:

FROM php:8.2-fpm-alpine

# Arguments
ARG USER=deployer
ARG USER_GROUP=www-data

# Environment
ENV TZ=Europe/Berlin

# Add APK repositories
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
    && echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \

# Update / Upgrade / Install apk packages
    && apk update \
    && apk upgrade \
    && apk add \
    sudo \
    git \
    tzdata \
    7zip \
    unzip \
    nano \
    nodejs@main \
    npm@community \

# Set Timezone
    && cp /usr/share/zoneinfo/${TZ} /etc/localtime

# Add custom php.ini
ADD /Dockerfiles/php/php.ini /usr/local/etc/php/conf.d/php.ini

# Add custom www.conf
ADD /Dockerfiles/php/www.conf /usr/local/etc/php-fpm.d/www.conf

# Install "install-php-extensions"
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

# Install PHP extensions and latest Composer
RUN chmod +x /usr/local/bin/install-php-extensions \
    && install-php-extensions bcmath ctype curl dom fileinfo json mbstring openssl pcre pdo pdo_mysql pdo_pgsql tokenizer xml zip \
    && install-php-extensions @composer \

# Add non root user
    && adduser -D ${USER} \
    && mkdir -p /etc/sudoers.d \
    && echo "${USER} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${USER} \
    && addgroup ${USER} ${USER_GROUP} \
    && chmod 0440 /etc/sudoers.d/${USER} \

# Remove cache and tmp files
    && rm -rf /var/cache/apk/* /tmp/*

# Add start script / entrypoint
COPY --chown=${USER}:${USER} ./Dockerfiles/entrypoint/start.sh /usr/local/bin/start
RUN chmod u+x /usr/local/bin/start

# Change user
USER ${USER}

# Set Workdir
WORKDIR /var/www/html

# Entrypoint
ENTRYPOINT ["/usr/local/bin/start"]

Here is the start.sh:

#!/bin/sh

set -e

role=${CONTAINER_ROLE:-app}
work_on_queues=${WORK_ON_QUEUES:-default}

if [ "$role" = "app" ]; then

    php-fpm

elif [ "$role" = "queue" ]; then

    echo "Running the queue..."
    php /var/www/html/artisan queue:work --queue="$work_on_queues" --sleep=3 --tries=3 -v

elif [ "$role" = "scheduler" ]; then

    echo "Running the scheduler..."
    php /var/www/html/artisan schedule:work

else
    echo "Could not match the container role \"$role\""
    exit 1
fi

And here is a docker-compose.yml example:

version: '3'
services:
    nginx:
        image: nginxinc/nginx-unprivileged:alpine-slim
        restart: always
        tty: true
        environment:
            COLORTERM: true
        ports:
            - '${APP_PORT:-80}:8080'
        volumes:
            - ./docker/nginx/conf.d/:/etc/nginx/conf.d/
            - '.:/var/www/html'
        networks:
            - network
        depends_on:
            - app

    app:
        image: <your-build-image>
        restart: always
        tty: true
        environment:
            COLORTERM: true
            CONTAINER_ROLE: app
        volumes:
            - '.:/var/www/html'
        networks:
            - network
        depends_on:
            - mysql

    queue:
        image: <your-build-image>
        restart: always
        tty: true
        deploy:
            replicas: 2
        environment:
            COLORTERM: true
            CONTAINER_ROLE: queue
#            WORK_ON_QUEUES: "default,test"
        volumes:
            - '.:/var/www/html'
        networks:
            - network
        depends_on:
            - mysql
            - app

    scheduler:
        image: <your-build-image>
        restart: always
        tty: true
        environment:
            COLORTERM: true
            CONTAINER_ROLE: scheduler
        volumes:
            - '.:/var/www/html'
        networks:
            - network
        depends_on:
            - mysql
            - app

    mysql:
        image: mysql/mysql-server:8.0
        command: --local-infile=ON --innodb_file_per_table=ON --innodb_log_file_size=512M --innodb_strict_mode=OFF
        restart: always
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            COLORTERM: true
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: "%"
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
            TZ: Europe/Berlin
        volumes:
            - 'mysql:/var/lib/mysql'
        networks:
            - network
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
            retries: 3
            timeout: 5s

networks:
    network:
        driver: bridge

volumes:
    mysql:
        driver: local

I hope I could help, because it took a long time for me to get this to work…

1 Like