Skip to content

markkimsal/laravel-docker-platform

Repository files navigation

laravel-docker-platform

Docker container for Laravel and PHP projects. Graceful shutdown. No-python supervisor. No-SSH.

Customizing

You can add your own one-time, start up scripts to /platform/start-container.d/

If any script in start-container.d exists with a return code other than 0, the container will fail to start.

You can change any PHP ini by bind mounting a file to /usr/local/etc/php-fpm.d/ or using your container orchestrator's config file functionality.

Available run-time environment variables

  • LARAVEL_WORK_DIR (defaults to /app)
  • LARAVEL_HORIZON_WORK_DIR (defaults to /app)
  • LARAVEL_QUEUE_WORK_DIR (defaults to /app)
  • LARAVEL_MIGRATE_COMMAND (defaults to "/usr/local/bin/php artisan migrate --force")
  • LARAVEL_SCHEDULER_COMMAND (defaults to "/usr/local/bin/php artisan schedule:run")
  • ENABLE_MIGRATIONS
  • ENABLE_CONFIG_CACHE
  • ENABLE_CHANGE_OWNER
  • ENABLE_ARTISAN_SCHEDULER (creates /etc/cron.d/laravel-artisan-schedule-run)

Config cache

One of the start-up scripts will cache the config with artisan. You can enable this step by setting the environment variable ENABLE_CONFIG_CACHE to any value.

Running queue:work as a standalone container

The script /platform/start-queue can be used to start artisan queue:work

set LARAVEL_QUEUE_WORK_COMMAND with your desired_commands

---
version: '3.7'

services:
  laravel:
    image:  your-docker-repo/your-app-name:latest-prod
  queue:
    image:  markkimsal/php-platform:8.0-nginx-fpm
    entrypoint: /platform/start-queue

    environment:
      LARAVEL_QUEUE_WORK_DIR: /app
      LARAVEL_QUEUE_WORK_COMMAND: /usr/local/bin/php artisan queue:work default --tries=1 --sleep=3

Running horizon as a standalone container

The script /platform/start-horizon can be used to call only artisan:horizon and no other processes. You can deploy the same built app container with 2 different command values to get 2 different container behaviors.

---
version: '3.7'
networks:
  traefik_ingress:
    external: true

services:
  laravel:
    image:  your-docker-repo/your-app-name:latest-prod
    networks:
      - 'traefik_ingress'

    deploy:
      endpoint_mode: dnsrr
      mode: replicated
      replicas: 3
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=traefik_ingress"
        - "traefik.http.routers.laraveltest.rule=Host(`app.localhost.test`)"
        - "traefik.http.routers.laraveltest.entrypoints=websecure"
        - "traefik.http.routers.laraveltest.tls=true"
        - "traefik.http.routers.laraveltest.tls.certresolver=myresolver"
        - "traefik.http.routers.laraveltest.service=laraveltest"
        - "traefik.http.services.laraveltest.loadbalancer.server.port=8080"

  horizon:
    image:  your-docker-repo/your-app-name:latest-prod
    entrypoint: /platform/start-horizon

    deploy:
      endpoint_mode: dnsrr
      mode: replicated
      replicas: 2
    environment:
      LARAVEL_HORIZON_WORK_DIR: /app

Running horizon as supervised service

If you want to run horizon under the supervisor runit you can add an environment variable START_HORIZON_AS_SERVICE=yes to your docker container by any means.

Running artisan schedule:run as a standalone container

The entrypoint /platform/start-cron can be used to run cron, with ENABLE_ARTISAN_SCHEDULER environment variable you can run artisan schedule:run every minute.

---
version: '3.7'

services:
  scheduler:
    image:  your-docker-repo/your-app-name:latest-prod
    entrypoint: /platform/start-queue

    environment:
      ENABLE_ARTISAN_SCHEDULER: 1

WWWUID and WWWGID

You can change the id of the www-data user to match any owner of files on your production system.

On your local development system, you can set WWWUID and WWWGID to 1000 so there are no ownership problems between your code and the running www-data user. (Note, this is only an issue on Gnu/Linux)

Set WWWUID and WWWGID like this:

---
version: '3.7'

services:
  queue:
    image:  markkimsal/php-platform:8.0-nginx-fpm
    command: /platform/start-queue

    environment:
      LARAVEL_QUEUE_WORK_DIR: /app
      LARAVEL_QUEUE_WORK_COMMAND: /usr/local/bin/php artisan queue:work default --tries=1 --sleep=3
      WWWUID: '${WWWUID:-33}'
      WWWGID: '${WWWGID:-33}'

PHP Extensions

I found that each extension barely takes up any more disk space or memory. If you don't want them, attach blank files to the container ini files when you deploy.

PHP Version - flavor 7.3-nginx-fpm 7.3-tools 7.4-nginx-fpm 7.4-tools 8.0-nginx-fpm 8.0-tools 8.1-nginx-fpm 8.1-tools
BCMATH X X X X X X X X
Intl X X X X X X X X
gd X X X X X X X X
mysql X X X X X X X X
pgsql X X X X X X X X
sqlite3 X X X X X X X X
readline X X X X X X X X
memcached (igbinary) X X X X X X X X
redis X X X X X X X X
xdebug X X X X X X X X
zip X X X X X X X X
SOAP X X X X X X X X
SSH2 X X X X X X X X
pcntl X X X X X X X X
sysvsem X X X X X X X X
sysvshm X X X X X X X X
sysvmsg X X X X X X X X
lib sodium X X X X X X X X
FFI X X X X X X
nginx X X X X X X X X
yarn X X X
nodejs-15.x X X X
composer2 X X X X
composer1.10 X X X X
deployer - 6 X X X X
deployer - 7 X
altax X X X
git X X X X
jq X X X X
unzip X X X X
openssh X X X X

Tools images

Having an image that can also run tests and build your image as part of your CI pipeline is useful. I could not find an image of nodejs that also had git, and at least one of my node dependencies required git to install so... here we are with nodejs 15 baked into this image.

Sometimes composer complains when you don't have a host OS unzip program, so that's been added to the tools variant.

Also, for deploying with PHP deployer, I found that having ssh, unzip, git are usefull or required.

I used to use altax for remote deployment so that's also on there.

I can't find an image on google cloud build that has jq, so I installed jq as well.

Supervisor runit

Runit is a small, c process supervisor. It tries to restart failed processes until you tell it to stop. The problem with runit is that you cannot send a signal that will gracefully stop all managed services. You must stop all services "by-hand", which is weird. You'd think that a simple SIGSTOP could initiate a graceful shutdown, but there's no way to do this. Runit (or runsv or runsvdir) will respond to TERM signals mostly by dieing immediately and letting children float to PID 1 and get reaped.

There is a small bash file that - in combination with dumb-init - will act as pid 1, take docker stop SIGQUIT or SIGTERM and gracefully try to shutdown the running services. This is essentially what the /sbin/my_init program from phusion/baseimage does.

Contributing and building your own images

Update the all Dockerfiles

php ./make-versions.php

Build a single image images with

bash ./build-image.sh 8.0

If you are on a Mac M1 (or other ARM environments)

export DOCKER_DEFAULT_PLATFORM=linux/arm64
bash ./build-image.sh 8.0

I think for RaspberryPi it might be

export DOCKER_DEFAULT_PLATFORM=linux/arm/v7
bash ./build-image.sh 8.0

About

docker container for laravel and php projects

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published