PreviewDrop for Laravel

A live Laravel preview for every branch

Laravel apps run end-to-end on PreviewDrop: artisan migrate, queue workers, scheduler, Livewire, Inertia, Reverb websockets. No Vapor, no serverless adapter, no cold starts.

TL;DR
Add a Dockerfile, set APP_KEYand a DB URL in the Variables tab, push a branch. Works with Laravel 10.x and 11.x, PHP 8.2+. Remember to set the PreviewDrop project's exposed port to 80 — Apache doesn't honour $PORT out of the box.

Dockerfile

Drop this at the root of your repo. PreviewDrop picks it up on the next push and builds it into a container behind a TLS-terminated subdomain.

Dockerfile
FROM php:8.3-apache RUN apt-get update && apt-get install -y --no-install-recommends \ libpq-dev libpng-dev libonig-dev libzip-dev unzip git \ && docker-php-ext-install pdo pdo_pgsql mbstring zip gd \ && rm -rf /var/lib/apt/lists/* COPY --from=composer:2 /usr/bin/composer /usr/bin/composer WORKDIR /var/www/html COPY composer.json composer.lock ./ RUN composer install --no-dev --no-scripts --optimize-autoloader COPY . . RUN composer dump-autoload --optimize ENV APACHE_DOCUMENT_ROOT=/var/www/html/public RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' \ /etc/apache2/sites-available/*.conf \ /etc/apache2/apache2.conf \ && a2enmod rewrite RUN chown -R www-data:www-data storage bootstrap/cache EXPOSE 80

Environment variables

Set these in the Variables tab of your PreviewDrop project. They're encrypted at rest and injected into every preview container at start.

VariableExample valueNote
APP_ENVproductionCloser to real behavior than local; still safe for previews.
APP_KEYbase64:<run php artisan key:generate --show>Required. Generate once and reuse across previews.
APP_URL${PREVIEWDROP_URL}PreviewDrop injects PREVIEWDROP_URL automatically. Laravel uses APP_URL for generated links.
APP_DEBUGtrueFine for previews. Never true in production.
DB_CONNECTIONpgsqlOr mysql / sqlite / sqlsrv — matches your config/database.php.
DB_HOSTdev-db.internalUse a shared dev DB, or Neon per-branch.
LOG_CHANNELstderrStreams logs to the PreviewDrop runtime logs tab.

Common gotchas

Exposed port must be 80, not 3000

Apache doesn't read $PORT natively. The image above exposes port 80 and Apache listens there by default. In PreviewDrop Project Settings, set Exposed port to 80.

APP_URL and trustworthy proxies

PreviewDrop terminates TLS at the edge. In App\Http\Middleware\TrustProxies set $proxies = '*' so Laravel honours X-Forwarded-Proto: https. Without this, generated URLs come out as http:// and things like OAuth callbacks break.

Migrations and seeds

Add RUN php artisan migrate --force && php artisan db:seed --force to the CMD (wrap in sh -c) so each preview gets a fresh schema. Pair with Neon branching and each branch gets its own DB — destructive migrations can't corrupt your main preview DB.

Queue workers and scheduler

For queue workers, create a second PreviewDrop project on the same repo with a worker Dockerfile that runs php artisan queue:work. The scheduler (schedule:run every minute) is usually fine to skip in previews — but if you need it, a tiny cron alongside PHP-FPM in the same image works.

Why not Vercel or Netlify?

Vercel doesn't run Laravel. Laravel Vapor is the canonical serverless path, but it means rewriting queue jobs to respect 15-minute Lambda limits, giving up long-polling websocket support, and operating three different environments (web, queue, scheduler) via three CloudFormation stacks. For a preview pipeline, that's absurd overhead. PreviewDrop runs the same container Forge would run — Apache, PHP-FPM, your vendor folder, the whole thing.
More Laravel patterns
Frameworks & Docker has the full guide. For Octane + Swoole-style long-running processes, reach out on support — a few teams run these on PreviewDrop already.

Preview your Laravel app in 5 minutes

Connect a repo, push a branch, get a URL. No credit card, no trial clock.

Start free