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.
DockerfileFROM 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.
| Variable | Example value | Note |
|---|---|---|
| APP_ENV | production | Closer to real behavior than local; still safe for previews. |
| APP_KEY | base64:<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_DEBUG | true | Fine for previews. Never true in production. |
| DB_CONNECTION | pgsql | Or mysql / sqlite / sqlsrv — matches your config/database.php. |
| DB_HOST | dev-db.internal | Use a shared dev DB, or Neon per-branch. |
| LOG_CHANNEL | stderr | Streams 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