Hi @cin4ed !
I’m with Fly.io and I’ll help you out. Here’s what’s happening and how to fix it:
The issue
When running fly launch
in a Laravel app, a couple of files are automatically set up. One of those files is the Dockerfile, which is how we’ll build a container that we can deploy on Fly.io’s servers.
The original Dockerfile actually does a multi-stage build, in three steps:
- Set up an environment where PHP and Laravel can run
- Create a new environment where the node modules are installed, using yarn, pnpm or npm.
- Copy over the node modules to the environment created in step 1.
The advantage of this is that the final container will have the correct node modules without needing to include node itself. This makes the final container smaller and keeps deployments snappy.
Since you have no package.json
file npm install
cannot run and will spit out errors, which is what you’re seeing).
The fix
The fix for this would be to eliminate steps 2 and 3 from the Dockerfile. If you comment out lines 28-74 (keep the expose 8080
line) it should work!
For reference, this is how a Dockerfile for this case could look:
# syntax = docker/dockerfile:experimental
ARG PHP_VERSION=8.2
ARG NODE_VERSION=18
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 application code, skipping files based on .dockerignore
COPY . /var/www/html
RUN composer install --optimize-autoloader --no-dev \
&& mkdir -p storage/logs \
&& php artisan optimize:clear \
&& chown -R www-data:www-data /var/www/html \
&& echo "MAILTO=\"\"\n* * * * * www-data /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
&& sed -i='' '/->withMiddleware(function (Middleware \$middleware) {/a\
\$middleware->trustProxies(at: "*");\
' bootstrap/app.php; \
if [ -d .fly ]; then cp .fly/entrypoint.sh /entrypoint; chmod +x /entrypoint; 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 . .
#COPY --from=base /var/www/html/vendor /app/vendor
#
## Use yarn or npm depending on what type of
## lock file we might find. Defaults to
## NPM if no lock file is found.
## Note: We run "production" for Mix and "build" for Vite
#RUN if [ -f "vite.config.js" ]; then \
# ASSET_CMD="build"; \
# else \
# ASSET_CMD="production"; \
# fi; \
# if [ -f "yarn.lock" ]; then \
# yarn install --frozen-lockfile; \
# yarn $ASSET_CMD; \
# elif [ -f "pnpm-lock.yaml" ]; then \
# corepack enable && corepack prepare pnpm@latest-8 --activate; \
# pnpm install --frozen-lockfile; \
# pnpm run $ASSET_CMD; \
# elif [ -f "package-lock.json" ]; then \
# npm ci --no-audit; \
# npm run $ASSET_CMD; \
# else \
# npm install; \
# npm run $ASSET_CMD; \
# fi;
#
## 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
#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
The lines that are commented out can also be removed if you want.
Let me know if this helps!