Skip to content
dply/edge

Guides

Deploy a Laravel app

Take a Laravel app from a Git repository to a running container on dply, with a database, cache, queues, scheduler and migrations.

This guide deploys a Laravel app end to end: dply detects it from composer.json, builds a container image, runs it on the edge, and wires in the resources the app needs. It covers the database, Valkey (Redis), queues, the scheduler, migrations and logs. It assumes a standard Laravel 11 or 12 app in a GitHub, GitLab or Bitbucket repository.

Note

Laravel apps run as container apps, which are available on every plan. See Plans & pricing.

Before you start

  • Commit composer.lock. dply installs dependencies with composer install --no-dev from it.
  • If the app builds front-end assets with Vite, keep the build script in package.json. dply runs it in a separate Node stage and copies public/ into the image.
  • Pin PHP in composer.json if you care which minor you get (see PHP version).

Create the app

  1. Choose New app (or Deploy an app on an empty dashboard).
  2. Step 1 of 3 · Connect your source control: pick a connected GitHub, GitLab or Bitbucket account, or paste a repository under Or paste a repository.
  3. Step 2 of 3 · Select a repository: choose the repository and the branch or tag to deploy.
  4. Step 3 of 3 · Create your application: dply reads composer.json, finds laravel/framework, and sets Mode to Container in the Deploy summary. Give the app a name and choose Deploy.

If your organization has no plan yet, Deploy sends you to Stripe Checkout for the $0 Starter plan, then back to this page with your choices kept.

The first deploy builds the image, rolls it out, and checks that the app's URL answers with a status below 500. You land on the app's Overview while it builds; follow progress under Deploys.

What dply builds

When the repository has no Dockerfile, dply generates one (Dockerfile.dply) for you:

  • Base image: an Alpine PHP image with pdo_pgsql, pdo_mysql, redis, intl, zip, bcmath, pcntl and opcache. Any other ext-* your composer.json or locked packages require is installed on top.
  • Web server: nginx in front of PHP-FPM, listening on port 8080. If laravel/octane is installed, the app runs php artisan octane:start instead: RoadRunner when a spiral/roadrunner package is present, otherwise Swoole.
  • Assets: when package.json has a build (or production) script, a Node stage runs it and the built public/ directory is copied into the image.
  • Inertia SSR: when the build produces bootstrap/ssr, the image also starts php artisan inertia:start-ssr.

If your repository has its own Dockerfile at the root, dply uses it instead and routes traffic to the first EXPOSE port (8080 when there is none).

Important

With your own Dockerfile, the dply-specific boot steps (migrations on start, queue worker mode, SQLite restore) are not added. Use the generated image unless you need something it cannot do.

PHP version

dply picks the PHP version from composer.json:

  1. config.platform.php, if set.
  2. Otherwise the newest version your require.php constraint allows, up to PHP 8.4. An explicit ^8.5 or 8.5.* gets 8.5.

Available versions are 8.2, 8.3, 8.4 and 8.5.

Environment variables

On the first deploy dply adds the variables a Laravel app needs to boot, only where you have not set them:

Variable Value
APP_KEY A generated key
APP_ENV production
APP_URL, ASSET_URL The app's live URL
LOG_CHANNEL stderr
SESSION_DRIVER cookie

They are saved on the app, so they stay stable across deploys and you can change them under Environment. Add your own variables there too; they reach both the build and the running app. See Environment variables.

Warning

APP_URL and ASSET_URL are saved once, with the address the app had on its first deploy. After you add a custom domain, update both under Environment and redeploy, or Laravel keeps generating links to the old address.

Sessions default to the cookie driver because containers share no disk. If you move sessions to Redis or the database, set SESSION_DRIVER yourself.

Add a database

Without a database, the app boots on SQLite at /tmp/database.sqlite. dply migrates it on every start and, where available, saves the file while the app runs and restores it when the app wakes. A SQLite app is served by one instance.

For anything beyond a prototype, attach a managed database:

  1. On the app's Overview, choose Add resource, then Database.
  2. Pick Postgres or MySQL (or MongoDB) and a size, and confirm.
  3. Redeploy. The next deploy sets:
Variable Postgres MySQL
DB_CONNECTION pgsql mysql
DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD set set
DB_SSLMODE require —
DATABASE_URL postgresql://…?sslmode=require mysql://…?ssl-mode=REQUIRED

MongoDB sets MONGODB_URI instead. A value you save under Environment always wins over the injected one. Databases are billed by usage; see Postgres, MySQL & MongoDB.

Run migrations

There is no separate release step: containers start fresh each time. To migrate, turn on Run migrations when a container starts in Overview → App card → Sleep, scaling, region (also in the database's settings). Each start then runs php artisan migrate --force --isolated.

You can also run Migrate, Seed and roll back from the database's Settings without redeploying.

Tip

Migrating on start repeats on every wake from sleep and costs a second framework boot. For large apps, leave it off and run Migrate from the database settings after a deploy that changes the schema.

Add Valkey for cache and queues

  1. Choose Add resource, then dply Valkey.
  2. Start a new instance or paste the address of an existing Redis.
  3. Redeploy.

The next deploy sets REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_USERNAME and REDIS_PASSWORD, and for Laravel also CACHE_STORE=redis, REDIS_CLIENT=phpredis and REDIS_PERSISTENT=true. See Valkey (Redis).

Run queued jobs

You have two ways to process jobs.

Queue workers run php artisan queue:work as always-on instances of your app's image, pulling from Valkey or your database:

  1. Choose Add resource, then Queue workers.
  2. Set the connection, queue names, instance count and, on Pro and Team, autoscaling.

Starter allows 1 worker instance with no autoscaling; Pro allows 5 instances and 2 extra worker groups; Team allows 10 and 4. Workers never sleep, so they bill compute all month. See Queue workers.

A Queue resource pushes each job to the app over HTTP instead, so nothing runs while the queue is empty:

  1. Choose Add resource, then Queue.
  2. Redeploy. dply sets DPLY_QUEUE and QUEUE_CONNECTION=dply.

dispatch() then sends jobs to the queue and the app runs them as they arrive. See Queues.

Both paths rely on the dply/laravel package. dply adds it to the generated image when the app uses a database, queue, key-value store, bucket, queue workers or the scheduler, unless you already require it.

Run the scheduler

In Overview, select Add resource → Scheduled task → Run Laravel’s scheduler (or turn on Run the Laravel scheduler every minute in the App card's Sleep, scaling, region), then deploy. dply calls schedule:run every minute. When queue workers are running, the first worker runs the scheduler instead. To run a single artisan command on its own schedule, add it as a Scheduled task on Overview; Run now in its sheet runs it once in the live app and shows the output. See Scheduled tasks.

Broadcasting

For Laravel Echo and Reverb, add a Realtime resource. dply runs the WebSocket server; you do not run reverb:start. See Laravel broadcasting with Realtime.

Size, scaling and sleep

On the App card on Overview you set the size, Instances, Always awake and Sleeps after (5 minutes by default). An idle app sleeps and the next request wakes it, which adds a cold start to that request. Set Min instances above 0 to keep one warm. See Scaling & sleep.

Logs

  • Build output: Deploys, then the deployment, or Build & deploy logs.
  • Application logs: Build & deploy logs, then Everything it printed in the last 15 minutes. Laravel logs to stderr, so exceptions appear here.
  • Queue worker output: Worker logs in the queue workers panel.

Custom domain

Add the domain under Routing, Domains, and follow Domains. Then update APP_URL and ASSET_URL as described above.

Next steps

View as Markdown llms.txt

dply/edge