Deploying Laravel Project to fly.io
Laravel is not easy to deploy but not anymore.
Today we will learn how to deploy laravel to fly.io and you're gonna love how easy it is.
Create fly.io account
First you can register to fly.io account, you can visit registration page https://fly.io/app/sign-in.
After sign up you need to install flytcl
cli.
With mac:
brew install flyctl
For linux:
curl -L https://fly.io/install.sh | sh
For windows:
pwsh -Command "iwr https://fly.io/install.ps1 -useb | iex"
See more installation instruction here: https://fly.io/docs/hands-on/install-flyctl/.
Generate deployment
Now that flyctl
cli has been installed go to your project folder and generate deployment with this command:
flyctl launch
With this command it will generate fly.toml
like this:
# fly.toml app configuration file generated for dedikasiku on 2023-10-19T16:55:55+07:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = "dedikasiku"
primary_region = "sin"
console_command = "php /var/www/html/artisan tinker"
[build]
[build.args]
NODE_VERSION = "18"
PHP_VERSION = "8.1"
[env]
APP_ENV = "production"
LOG_CHANNEL = "stderr"
LOG_LEVEL = "info"
LOG_STDERR_FORMATTER = "Monolog\\Formatter\\JsonFormatter"
SESSION_DRIVER = "cookie"
SESSION_SECURE_COOKIE = "true"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ["app"]
And Dockerfile
like this.
# syntax = docker/dockerfile:experimental
# Default to PHP 8.2, 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.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 \
&& 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 /etc/nginx/sites-available/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 . .
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-7 --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
ENTRYPOINT ["/entrypoint"]
With this
Dockerfile
config make sure to enable npm for installing nodejs dependency, sometime pnpm or yarn will have a problem in build process.
After first deployment if you want to deploy new version or new changes you can run command:
Make sure you are in the current project folder when you run this command.
flyctl deploy
Enable Github Action
While running the deployment with flyctl
command line from terminal is easy enough we can make it better by setting up github action CI to make it run without running the command deploy manually. See official documentation here: https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/.
Create new file .github/workflows/fly.yml
:
name: Fly Deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Now let's create fly api token from https://fly.io/user/personal_access_tokens
Then don't forget to add env in your github action environment.
And that's how easy it is.