Considering you made this Dockerfile, I’ll tag you @fideloper-fly
Have you ever tried optimizing (the build speed) the Dockerfile? A small change in a PHP file causes a rebuild that takes about 1:34 minutes (on my local machine). It would be great if we could deploy faster
Oops. Sorry, I missed that. Perhaps I can redeem myself with a better suggestion.
It looks like composer supports a cache, which normally would be empty on each build, but by adding one line to the beginning of a RUN statement, a directory can be saved between builds. See Optimizing your build · Fly Docs for a Rails example.
Here it looks like the line would be something like:
RUN --mount=type=cache,id=composer-cache,sharing=locked,target=/root/.composer \
I’m a bit out of my expertise (it has been many years since I’ve programmed in PHP), but optimizing Dockerfiles is a interest of mine.
Are you testing this against a specific (perhaps large?) project? I’m curious how that timing stacks up vs smaller projects.
A fresh install of Laravel is really quick for me, but I’ve seen autoloader take a bunch more time on older (well, larger) projects - which makes sense when there are more classes/libaries to autoload.
But I’m curious if there are other contexts/ways to reduce that size
In terms of your change - what if you didn’t use the --no-autoloader flag when you ran composer install ? autoload-dumping would then be cached even if you just had a small code change. (Am I missing something there?)
# If no changes in composer, then these lines are cached
COPY composer.json composer.lock /var/www/html/
RUN composer install --no-dev --optimize-autoloader
# If there's a change to the code, we continue on here
COPY . /var/www/html
# Remove dump autoload here
RUN mkdir -p storage/logs \
&& ...
I don’t think changed code in autoloaded files would cause that to break, but I could be wrong!
Yea this is pretty good. It takes it down by about 50% when only php files change.
This could still be improved maybe because npm run build still runs on every build. I didn’t manage to fix that. It’s a bit more complicated than I thought. I thought I could just COPY the resources/js and resources/css and the package files, but Vite seems to need more files than just those.
# 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+
ARG PHP_VERSION=8.1
ARG NODE_VERSION=14
FROM fideloper/fly-laravel:${PHP_VERSION} as base
# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION
LABEL fly_launch_runtime="laravel"
COPY composer.json composer.lock /var/www/html/
RUN composer install --no-dev --optimize-autoloader --no-scripts
# copy application code, skipping files based on .dockerignore
COPY . /var/www/html
RUN php artisan package:discover \
&& mkdir -p storage/logs \
&& php artisan package:discover \
&& php artisan optimize:clear \
&& chown -R www-data:www-data /var/www/html \
&& sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php \
&& echo "MAILTO=\"\"\n* * * * * www-data /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
&& cp .fly/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/supervisor/conf.d/fpm.conf; \
if grep -Fq "spiral/roadrunner" /var/www/html/composer.json; then \
mv /etc/supervisor/octane-rr.conf /etc/supervisor/conf.d/octane-rr.conf; \
if [ -f ./vendor/bin/rr ]; then ./vendor/bin/rr get-binary; fi; \
rm -f .rr.yaml; \
else \
mv .fly/octane-swoole /etc/services.d/octane; \
mv /etc/supervisor/octane-swoole.conf /etc/supervisor/conf.d/octane-swoole.conf; \
fi; \
rm /etc/nginx/sites-enabled/default; \
ln -sf /var/www/html/.fly/default-octane /etc/nginx/sites-enabled/default; \
fi
# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:${NODE_VERSION} as node_modules_go_brrr
RUN mkdir /app
RUN mkdir -p /app
WORKDIR /app
COPY package.json package-lock.json /app/
RUN npm install
COPY . .
RUN npm run build
# From our base container created above, we
# create our final image, adding in static
# assets that we generated above
FROM base
# Packages like Laravel Nova may have added assets to the public directory
# or maybe some custom assets were added manually! Either way, we merge
# in the assets we generated above rather than overwrite them
COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm
COPY --from=node_modules_go_brrr /app/public/build/assets/widget-loader.*.js /var/www/html/public/js/widget-loader.js
RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ \
&& rm -rf /var/www/html/public-npm \
&& chown -R www-data:www-data /var/www/html/public
EXPOSE 8080
ENTRYPOINT ["/entrypoint"]
I (and perhaps @rubys ) and test it out on our end too to see what we can do. It would be nice if Vite didn’t need to get run on every build but I’m not totally sure on the feasibility of that.
It sort of depends on if your convention is to commit built static assets or not