Full Stack Laravel Bytes Tutorial Supervisor not working

I have tried the configuration on this and can the queue works but I’m now having issues with php-fpm. NFO spawnerr: can't find command 'php-fpm81'

I guess I’ll have to rollback on my previous configuration and just make supervisor work there.

Finally settled using

#!/usr/bin/env sh

if [ $# -gt 0 ];then
    # If we passed a command, run it as root
    exec "$@"
    # Otherwise start the web server

    ## Prepare Laravel caches
    /usr/bin/php /var/www/html/artisan config:cache --no-ansi -q
    /usr/bin/php /var/www/html/artisan route:cache --no-ansi -q
    /usr/bin/php /var/www/html/artisan view:cache --no-ansi -q
    chown -R webuser:webgroup /var/www/html

    nohup php artisan queue:work --daemon &

    exec /init

at entrypoint.sh

That should work (running the queue at run-time) however if you want supervisor to do it, it may simply be a case of also uncommenting this line, 11 (and commenting out line 12, naturally, to swap them over):

That would make supervisor run all of them.

I would assume (may be wrong) the Fly CLI is basing its supervisor on that one. If not of course that won’t work but if so it may be as simple as that.

Update: Ah, it has had other modifications too from that older hello world one. Small job for @fideloper-fly whenever you get a chance :slight_smile: : in investigating this, I just noticed that hello app on github has a remnant of ,laravel-notification, in that referenced line 11 above. That ,laravel-notification, needs removing. I’ll do a PR, if I remember. I assume supervisor would complain that does not exist. Or perhaps it doesn’t mind and happily carries on anyway.


There’s a few points of confusion to clear up. Flyctl is generating a different base container (as of like 2 days ago).

The new container (which it looks like you use!) does NOT use supervisord.

(i’m actually not sure how you ended up with both supervisord config AND the newer docker config!?)

The official way to run workers / cron is moving to the use of [processes] (docs: App Configuration (fly.toml) · Fly Docs )

You can run (IIRC) “cron -f” as one command and the queue worker command as another.

I’m updating the docs, etc, first thing next week but i think part of the confusion here is catching us mid-change in the setup here (the new setup has some benefits we’re aiming for).

I’m typing on my phone but i’ll try to come back with more details in a bit.

Docker setup weather “fly launch” command creates is taken from here: flyctl/scanner/templates/laravel at master · superfly/flyctl · GitHub

The new base containers are from here: GitHub - serversideup/docker-php: Production-ready Docker images for PHP. Optimized for Laravel, WordPress, and more!

Ah, that would explain it.

Ok I’ll leave this in your capable hands @fideloper-fly :slight_smile:

OK, here’s the “official” way to to run cron / queues in this new set.

Note: I started from a fresh application, and ran fly launch (using the latest flyctl) to ensure I got the newest setup. That creates files .dockerignore, files in docker, Dockerfile and fly.toml.

After that was up and running, I added the following based on the processes docs.

Here’s the whole fly.toml:

app = "red-sea-8756"
kill_signal = "SIGINT"
kill_timeout = 5
processes = []

    NODE_VERSION = "14"
    PHP_VERSION = "8.1"

  APP_ENV = "production"
  LOG_CHANNEL = "stderr"
  LOG_LEVEL = "info"
  LOG_STDERR_FORMATTER = "Monolog\\Formatter\\JsonFormatter"

  app = ""
  worker = "php artisan queue:work"
  cron = "cron -f"

  allowed_public_ports = []
  auto_rollback = true

  http_checks = []
  internal_port = 8080
  processes = ["app"]    # THIS IS HERE BY DEFAULT, JUST NOTE IT IS HERE
  protocol = "tcp"
  script_checks = []
    hard_limit = 25
    soft_limit = 20
    type = "connections"

    force_https = true
    handlers = ["http"]
    port = 80

    handlers = ["tls", "http"]
    port = 443

    grace_period = "1s"
    interval = "15s"
    restart_limit = 0
    timeout = "2s"

The addition of the processes groups there sets the CMD that’s passed to the container’s ENTRYPOINT script. That’s why the app group is an empty string. We don’t need to pass a custom command to it!

The other 2 (queue and cron) are separate processes that run what you’d expect - a queue worker instance, and the cron daemon (which is pre-configured to run artisan schedule:run).

What You Might Not Like

You end up with 3 vm’s (one per process defined):

Running a single container instead:

If you want everything running within one container, you can do that manually by editing /creating some files.

It involves adding directories and a run shell script in /etc/services.d.

For example, create directories:

  • /etc/services.d/cron
  • /etc/services.d/queue

And then within each, create a run executable script like this:

For queue:

#!/usr/bin/with-contenv bash

su webuser

/usr/bin/php /var/www/html artisan queue:work

For cron:

#!/usr/bin/with-contenv bash

cron -f

You can create these new files in the docker directory, and then edit your Dockerfile to move those files to the correct location in the resulting image after they are copied into the Docker image via COPY . /var/www/html (which already will be in the Dockerfile near line 19)

Let me know if you run into trouble.

1 Like

Running in a Single container instruction did not work for me.


# syntax = docker/dockerfile:experimental

# Default to PHP 8.1, but we attempt to match
# the PHP version from the user (wherever `flyctl launch` is run)
# Valid version values are PHP 7.4+
FROM serversideup/php:${PHP_VERSION}-fpm-nginx as base

LABEL fly_launch_runtime="laravel"

RUN apt-get update && apt-get install -y \
    git curl zip unzip rsync ca-certificates vim htop cron supervisor \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /var/www/html

# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

RUN mv docker/queue /etc/services.d/queue

RUN composer install --optimize-autoloader --no-dev \
    && mkdir -p storage/logs \
    && php artisan optimize:clear \
    && chown -R webuser:webgroup /var/www/html \
    && sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php \
    && echo "MAILTO=\"\"\n* * * * * webuser /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
    && rm -rf /etc/cont-init.d/* \
    && cp docker/nginx-websockets.conf /etc/nginx/conf.d/websockets.conf \
    && cp docker/nginx-default /etc/nginx/sites-available/default \
    && cp docker/entrypoint.sh /entrypoint \
    && chmod +x /entrypoint

# If we're using Octane...
RUN if grep -Fq "laravel/octane" /var/www/html/composer.json; then \
    rm -rf /etc/services.d/php-fpm; \
    if grep -Fq "spiral/roadrunner" /var/www/html/composer.json; then \
    mv docker/octane-rr /etc/services.d/octane; \
    if [ -f ./vendor/bin/rr ]; then ./vendor/bin/rr get-binary; fi; \
    rm -f .rr.yaml; \
    else \
    mv docker/octane-swoole /etc/services.d/octane; \
    fi \

# From our base container created above, we
# create our final image, adding in static
# assets that we generated above
FROM base

RUN chown -R webuser:webgroup /var/www/html/public


ENTRYPOINT ["/entrypoint"]


#!/usr/bin/with-contenv bash

su webuser

/usr/bin/php /var/www/html artisan queue:work

error swoole extensions


Sorry for the delay, I was traveling through the weekend and got back just a bit ago.

If you can let me know if you got a specific error, that’ll help a bunch. Right off the bat, however, I think I had an accidental space there for the queue command. It should be:

#!/usr/bin/with-contenv bash

su webuser

# Previously was "/var/www/html artisan"
/usr/bin/php /var/www/html/artisan queue:work

Note that you need to fill that command with whatever other flags you may need as well. For example, the Laravel docs shows an example:

# This defines the "sqs" driver, which may not be appropriate for you
php artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600

@bogordesain can you try installing php8.1-swoole as an additional package in your Dockerfile near the top? (within the FROM ... as base build step).

Near line 12, change it to include the swoole package. It will look something like this (where you should be able to use the $PHP_VERSION variable to make that choose the right PHP version dynamically).

RUN apt-get update && apt-get install -y \
    git curl zip unzip rsync ca-certificates vim htop cron php${PHP_VERSION}-swoole \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \

If you redeploy that and still get an error, let me know.

No worries nohup php artisan queue:work --daemon & in entrypoint.sh is working fine at the moment.

But I did try what you suggested but there were no errors shown I know it did not work.

This might help.

2022-08-15T20:12:14.825 runner[a53f4bf6] sin [info] Starting instance

2022-08-15T20:12:15.077 runner[a53f4bf6] sin [info] Configuring virtual machine

2022-08-15T20:12:15.095 runner[a53f4bf6] sin [info] Pulling container image

2022-08-15T20:12:20.525 runner[a53f4bf6] sin [info] Unpacking image

2022-08-15T20:12:22.340 runner[a53f4bf6] sin [info] Preparing kernel init

2022-08-15T20:12:22.594 runner[a53f4bf6] sin [info] Configuring firecracker

2022-08-15T20:12:22.840 runner[a53f4bf6] sin [info] Starting virtual machine

2022-08-15T20:12:23.023 app[a53f4bf6] sin [info] Starting init (commit: c86b3dc)...

2022-08-15T20:12:23.039 app[a53f4bf6] sin [info] Preparing to run: `/entrypoint` as root

2022-08-15T20:12:23.054 app[a53f4bf6] sin [info] 2022/08/15 20:12:23 listening on [fdaa:0:8268:a7b:232f:a53f:4bf6:2]:22 (DNS: [fdaa::3]:53)

2022-08-15T20:12:23.955 app[a53f4bf6] sin [info] INFO Processing jobs from the [default] queue.

2022-08-15T20:12:23.970 app[a53f4bf6] sin [info] [s6-init] making user provided files available at /var/run/s6/etc...exited 0.

2022-08-15T20:12:23.988 app[a53f4bf6] sin [info] [s6-init] ensuring user provided files have correct perms...exited 0.

2022-08-15T20:12:23.988 app[a53f4bf6] sin [info] [fix-attrs.d] applying ownership & permissions fixes...

2022-08-15T20:12:23.989 app[a53f4bf6] sin [info] [fix-attrs.d] done.

2022-08-15T20:12:23.989 app[a53f4bf6] sin [info] [cont-init.d] executing container initialization scripts...

2022-08-15T20:12:23.989 app[a53f4bf6] sin [info] [cont-init.d] done.

2022-08-15T20:12:23.990 app[a53f4bf6] sin [info] [services.d] starting services

2022-08-15T20:12:23.997 app[a53f4bf6] sin [info] [services.d] done.

2022-08-15T20:12:24.048 app[a53f4bf6] sin [info] Reaped child process with pid: 630, exit code: 0

2022-08-15T20:12:24.051 app[a53f4bf6] sin [info] Reaped child process with pid: 638, exit code: 0

2022-08-15T20:12:24.052 app[a53f4bf6] sin [info] Reaped child process with pid: 646, exit code: 0

2022-08-15T20:12:24.052 app[a53f4bf6] sin [info] Reaped child process with pid: 654, exit code: 0

2022-08-15T20:12:24.053 app[a53f4bf6] sin [info] Reaped child process with pid: 676, exit code: 0

2022-08-15T20:12:24.053 app[a53f4bf6] sin [info] Reaped child process with pid: 684, exit code: 0

2022-08-15T20:12:24.054 app[a53f4bf6] sin [info] Reaped child process with pid: 692, exit code: 0

2022-08-15T20:12:24.056 app[a53f4bf6] sin [info] Reaped child process with pid: 700, exit code: 0

2022-08-15T20:12:24.065 app[a53f4bf6] sin [info] [15-Aug-2022 20:12:24] NOTICE: fpm is running, pid 731

2022-08-15T20:12:24.065 app[a53f4bf6] sin [info] [15-Aug-2022 20:12:24] NOTICE: ready to handle connections

2022-08-15T20:12:24.065 app[a53f4bf6] sin [info] [15-Aug-2022 20:12:24] NOTICE: systemd monitor interval set to 10000ms

I even tried to echo something


#!/usr/bin/with-contenv bash

su webuser

# /usr/bin/php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600

Ah ok, based on that output, it’s exiting when there are no more jobs and then being restarted. It likely hits a max number of restarts before setting it as failed.

You may need --daemon for that shell script then. Alternatively. queue:listen may be appropriate instead. I’ll play with this soon to figure out the best way.

I think what you have in Entrypoint will fail when it hits the first error, but it’ll work up until then!

Thanks for experimenting!

yes is cool. but we need other php extension
Call to undefined function mb_strcut
need install other php extension

php${PHP_VERSION}-swoole php${PHP_VERSION}-xml php${PHP_VERSION}-mbstring

thanks, i’ll get those in!

Cron is failed /usr/bin/php ? or /usr/bin/php8.0 ? this case i use php8.0

CRON doesn’t use a working directory, so you’ll need something like /usr/bin/php /var/www/html/artisan sendmail:daily

I just followed the tutorial yesterday and flyctl logs complains that it can’t find the index.php file with a stock laravel deploy? (app: electrify613).

Some time if setup cache with redis show error

i was have solution for this problem

it’s must be configured before php artisan optimize:clear

ARG REDIS_INTERNAL_URL="redis://default:xxxxxxxx:6379"

so this final code start line 22

ARG REDIS_INTERNAL_URL="redis://default:xxxxxxxx:6379"
RUN composer install --optimize-autoloader --no-dev \
    && mkdir -p storage/logs \
    && php artisan optimize:clear \

are you using octane or just php-fpm?

1 Like


Sorry to hear that didn’t work for you. Testing this out quick seemed to work for me - Like @bogordesain pointed out, definitely let us know a few things:

  1. If you used vanilla Laravel vs octane
  2. What version of flyctl you have (fly version) - I just tested on version v0.0.378
  3. If you can show us your fly.toml and Dockerfile that can help