Dockerfile Generator for Laravel: dockerfile-laravel!

We want Fly.io to be the best place to deploy Laravel applications in, including all other external apps needed to make a Laravel application whole.

This is why the Laravel team at Fly.io has been making efforts to create repositories and packages that help in making Laravel applications ready for live action. Take for example the fly-apps/laravel-docker repository which provides a robust base Docker image for dockerizing Laravel applications. Then of course the fly-apps/fly-laravel package: for deploying your apps live. Just run it’s launch and deploy commands to go live with your Laravel app and necessary external applications (db, cache, storage ) in the Fly.io platform!

Enter dockerfile-laravel!

This time around, we created a new cli-package, dockerfile-laravel, geared towards dockerizing Laravel apps on top of the base docker image created from laravel-docker. With just one command:

dockerfile-laravel generate

dependencies required by your Laravel app will be scanned for and detected, and a Dockerfile tailored to your project generated.

It’s main goal is to detect different Laravel versions, packages, and dependency combinations, and create Dockerfiles tailored to successfully running these combinations. It’s at it’s earliest stages, and currently provides support for both Laravel 10 and 11 versions. And, it’s going to continue supporting newer versions in the future.

It’s also going to be used by flyctl in a near release to generate the Dockerfile used in deploying Laravel apps to Fly.io. That’s right! You can run it to generate Docker related files for your local Laravel container setup, and live server setup!

Ultimately:
It’s installable from packagist, and it’s repository is available publicly, with a special section on how to locally run this package. And yes, that’s where you, the Laravel Fly.io-ers community comes in!

Do test the package out, and submit any features/PR’s/issues you can.

Overtime more features can be requested, and reviewed, added, and maintained by the Laravel Fly team. Until such time dockerfile-laravel can run to successfully dockerize any Laravel application, and, ultimately, provide a package the Laravel community can turn to generating Dockerfiles for their Laravel applications, no matter how diverse their app set up be.

It’s exciting isn’t it? I am excited for this, and I hope you at the Laravel Fly community are too!

5 Likes

any plans on using frankenphp?

1 Like

Hi @ricardosawir!

Of course! There’s a flag you can pass on the generate command to create a Dockerfile that uses Octane - FrankenPHP. Simply run the command including the flag likeso:

vendor/bin/dockerfile-laravel generate --octane=frankenphp

And that should include Frankenphp in the Dockerfile configuration.

how about single static file deployment? Is it possible? Since from what I read frankenphp offers this, however have little idea how this could be implemented within docker

Hi again @ricardosawir ! It took a bit of time, but! I took a look at the topic you’ve mentioned above, “single static file deployment”, and can see that FrankenPHP really does offer this!

The idea is that we prep our app for binary transformation, then we run FrankenPHP’s ./build-static.sh script to create a Single, portable binary that contains the following:

  1. PHP interpreter
  2. Caddy Web server
  3. Our Laravel app

Then, once the buld-static.sh script has generated that binary, all that’s left is to run the generated binary!

The steps above are do-able via a Dockerfile! dockerfile-laravel already generates a Dockerfile for us prepped for “binary transformation” So all we have to do is install dockerfile-laravel, then run its generate command to generate the dockerfile: vendor/bin/dockerfile-laravel.

Afterwards, we’ll have to revise the Dockerfile it generated to include and run Frankenphp’s build-static.sh script to build the single file binary of our app. So, in our Dockerfile, after the line RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/..., include a new build stage with frankenphp’s static-builder:

# OUR NEW and last build stage!
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder as builder

# Copy our app found in the previous "base" stage
WORKDIR /go/src/app/dist/app
COPY --from=base /var/www/html .

# Build the static binary, be sure to select only the PHP extensions you want
# ALSO! please set the FRANKENPHP_VERSION to the latest one
WORKDIR /go/src/app/
RUN EMBED=dist/app/ \
    FRANKENPHP_VERSION=1.1.2 \
    ./build-static.sh

# EXPOSE port
EXPOSE 80

ENTRYPOINT ["dist/frankenphp-linux-x86_64", "php-server"]

Notice 2 things here!

  1. We copy our prepped app from the base stage /var/www/html to our Frankenphp build stage “builder”.
  2. I’ve added an ENTRYPOINT entry. That’s basically running the generated binary “frankenphp-linux-x86_64” with the php-server command

With that, you can build the Dockerfile into an image with

docker build -t my-laravel-binary-app .

And run it locally with

docker run -p 80:80 my-laravel-binary-app

You can finally visit your application at 127.0.0.1:80!

Test it out if it works, and let me know if there’s anything I missed! I’ll try to add this as a beta feature in dockerfile-laravel, so watch out for that too!

NOTE!
In an earlier version of my answer, I incorrectly used the following syntax when building the binary:

RUN ./dist/frankenphp-linux-x86_64 version \
    export FRANKENPHP_VERSION=1.1.2 \
    EMBED=dist/app/ \
    ./build-static.sh

This however does not embed the app at all. So, how did I verify that new syntax embedded the app while the old one didn’t? These were the telltale signs:

  1. Building the Dockerfile into an image using the correct syntax takes time to build the image, since it’s embedding the whole app I added for EMBED
  2. Running the generated binary in the container using the correct syntax actually now logs( unlike the container that used the old incorrect syntax I mentioned above( under NOTE ), which did not log that path at all ):
embedded PHP app 📦     {"path": "/tmp/frankenphp_a204c16a3cc7597030296b259d908317  app.tar"}
  1. On the container containing the correct syntax, running the generated binary does not require the presence of the Laravel app directory found in /go/src/app/dist/app. The directory can be removed and the binary will still serve the application, unlike in a container containing the incorrect syntax, which would throw a 404 page not found if the directory was removed.

Also, feast your eyes! This PR contains support for embedding a Laravel app into a FrankenPHP standalone binary! Works when deployed on Fly.io as well!

NOTES:

  1. The important Dockerfile code snippet needed to embed an app using FrankenPHP is here.

  2. Then of course a flag is required to be passed to signal that we want a FrankenPHP binary to be generated.

Thank you for suggesting this feature @ricardosawir! It will be included it in the new release of dockerfile-laravel.

nice!

1 Like

Question though, how does it do if we use the “local” driver storage? Will it produce a new folder or anything?

Hi again @ricardosawir! Another great suggestion–you’re on a roll!

I’m actually trying to figure local storage for Frankenphp generated binary apps. But, you’re on to something, I think a folder outside of the binary is perfect for standalone php binaries. This way we can add a Volume to the external folder and persist data in it.

I’m even thinking of mounting a Volume to the folder that contains the php binary instead. But I haven’t got around to see the gotchas of that approach or exactly how Frankenphp binary apps handle files. If you have other suggestions on how to implement this, I’m all eyes–otherwise storage folder outside of the binary it is!

In the meantime, if you don’t want to use local storage for your Laravel Fly app, you can deploy a Tigris Object Fly App instead and connect it with your Laravel Fly App. It’s S3 compatible so I think you’d just have to replace your AWS environment variables with values from your Tigris app.