Sarıkaya Dev Logo

Deploy Laravel on Fly.io: Edge‑First, Auto‑Scaling, Zero‑Downtime

Mahmut Sarıkaya 4 min read 1 Views 0
Deploy Laravel on Fly.io: Edge‑First, Auto‑Scaling, Zero‑Downtime

Why edge deployment matters for Laravel apps

Imagine a user in Tokyo loading a Laravel dashboard hosted on a server in Virginia. The round‑trip latency can exceed 150 ms, turning a smooth experience into a sluggish one. Fly.io’s global network places your containers within 30 ms of major metros, turning that latency into a fraction of its former self. For e‑commerce, SaaS, or API‑driven services, every millisecond counts toward conversion and retention.

System requirements before you start

Fly.io runs any Docker‑compatible image, so the only hard requirement is a recent Docker Engine (≥ 20.10) and the Fly CLI (flyctl). Your Laravel project should be on PHP 8.1 or newer, and you’ll need a PostgreSQL or MySQL database that Fly can provision or that you connect to externally. A Git repository is assumed for version control.

Dockerizing a Laravel application

Start by creating a minimal Dockerfile that installs extensions Laravel relies on, such as pdo_mysql and gd. The example below builds the app, caches Composer dependencies, and sets the correct permissions for the storage folder.

FROM php:8.2-fpm AS base<br>RUN apt-get update && apt-get install -y \\ 
    git \\ 
    unzip \\ 
    libpng-dev \\ 
    libonig-dev \\ 
    libxml2-dev \\ 
    && docker-php-ext-install pdo_mysql gd mbstring exif pcntl bcmath<br>WORKDIR /var/www<br>COPY composer.json composer.lock ./<br>RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer <br>RUN composer install --no-dev --optimize-autoloader<br>COPY . .<br>RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache<br>EXPOSE 8080<br>CMD ["php", "-S", "0.0.0.0:8080", "-t", "public"]

Notice the use of --no-dev for production builds and the explicit EXPOSE of port 8080, which Fly.io expects by default.

Configuring Fly.io for Laravel

Fly.io uses a fly.toml file to describe the service. Place it in the project root and set the [[services]] block to enable HTTP, HTTPS, and automatic TLS. The [[services.http_checks]] entry adds a health‑check that hits /health, a route you should implement in Laravel to return a 200 status.

app = "laravel-fly-demo"<br>primary_region = "iad"<br><br>[build]<br>  image = "Dockerfile"<br><br>[[services]]<br>  internal_port = 8080<br>  protocol = "tcp"<br>  auto_stop_machines = true<br>  auto_start_machines = true<br>  min_machines_running = 1<br><br>  [[services.ports]]<br>    handlers = ["http"]<br>    port = 80<br>  [[services.ports]]<br>    handlers = ["tls", "http"]<br>    port = 443<br><br>  [[services.http_checks]]<br>    interval = "30s"<br>    grace_period = "5s"<br>    method = "GET"<br>    path = "/health"<br>    protocol = "http"

Setting min_machines_running = 1 guarantees at least one instance is always ready, while the auto‑scaling flags let Fly spin up additional VMs as traffic spikes.

Deploying with Flyctl

After installing flyctl, log in, create an app, and push the Docker image. The commands below illustrate a typical CI/CD flow:

flyctl auth login<br>flyctl launch --no-deploy --copy-config<br>docker build -t registry.fly.io/laravel-fly-demo:$(git rev-parse --short HEAD) .<br>docker push registry.fly.io/laravel-fly-demo:$(git rev-parse --short HEAD)<br>flyctl deploy --image registry.fly.io/laravel-fly-demo:$(git rev-parse --short HEAD)

The --no-deploy flag lets you adjust fly.toml before the first release. Fly automatically creates a PostgreSQL cluster if you run flyctl postgres create, and you can bind it to the app with flyctl secrets set DATABASE_URL=....

Auto‑scaling and edge‑first strategy

Fly’s “machines” model monitors CPU and request latency. By default, a 70 % CPU threshold triggers a new machine in the nearest region with spare capacity. You can fine‑tune this behavior with flyctl scale count or by adding a [[services.concurrency]] block to limit connections per machine. For a Laravel API serving 200 RPS, a single 1‑CPU VM typically handles the load; during a flash sale, Fly may launch three additional VMs across Europe, Asia, and North America, keeping latency under 50 ms globally.

Zero‑downtime deployments

Fly.io replaces the running image only after the new container passes health checks. To avoid session loss, configure Laravel to use the database or Redis for session storage rather than the default file driver. Adding the following to .env ensures session continuity across machines:

SESSION_DRIVER=redis<br>CACHE_DRIVER=redis<br>QUEUE_CONNECTION=redis

Because Fly routes traffic at the edge, the switch between versions happens at the load‑balancer level, so users never see a 502 error. If a release fails the health check, Fly automatically rolls back to the previous stable version.

Monitoring, logs, and rollback

Use flyctl logs to stream real‑time logs from all machines. Integrate with Fly’s metrics dashboard to watch CPU, memory, and request latency per region. A typical alert rule might be: “If average latency > 100 ms for 5 minutes in any region, trigger a Slack webhook.” Rolling back is as simple as flyctl releases list followed by flyctl deploy --release .

Conclusion

Deploying Laravel on Fly.io transforms a monolithic, single‑region setup into a globally distributed, auto‑scaling service with true zero‑downtime releases. By containerizing the app, defining a concise fly.toml, and leveraging Fly’s machine‑level scaling, you gain sub‑100 ms latency for users worldwide while keeping operational overhead low. The workflow outlined above can be scripted into any CI pipeline, making edge‑first Laravel deployments repeatable and reliable.

Sources

Fly.io Official Documentation, Laravel Official Documentation, DigitalOcean Community Tutorials

Author: Mahmut Sarıkaya — sarikayadev.com

Tags: #Laravel #Fly.io #edge deployment #Docker #Flyctl
Share:
M

Written by

Mahmut Sarıkaya

Software Developer

Comments

No comments yet. Be the first to share your thoughts!

Leave a Comment

3 + 2 =