Back to Blog
Guide8 min read2026-05-01

Laravel Production Deployment: Step-by-Step Guide

A complete step-by-step guide to deploying Laravel applications to production using Docker, PHP-FPM, Nginx, and a cloud PaaS with managed databases.

Laravel Production Deployment: Step-by-Step Guide

Laravel is the most popular PHP framework, offering elegant syntax, a powerful ORM (Eloquent), queue system, and rich ecosystem. Deploying Laravel to production requires proper Docker configuration, PHP-FPM process management, Nginx as the web server, and careful attention to the APP_ENV and caching settings.

This guide walks through a complete Laravel production deployment on [PandaStack](https://pandastack.io).

Laravel's Production Requirements

Before deploying, Laravel needs:

  • PHP 8.2+ with required extensions (pdo_pgsql, mbstring, openssl, tokenizer, xml, ctype, json)
  • Composer dependencies installed without dev packages
  • Application key generated (APP_KEY)
  • Configuration cached for performance (php artisan config:cache)
  • Routes and views cached (php artisan route:cache, php artisan view:cache)

Project Structure

myapp/
├── app/
├── config/
├── database/
│   └── migrations/
├── public/
├── routes/
├── storage/
├── docker/
│   ├── nginx/
│   │   └── default.conf
│   └── php/
│       └── php.ini
├── Dockerfile
└── pandastack.json

Nginx Configuration

# docker/nginx/default.conf
server {
    listen 80;
    root /var/www/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location /health {
        access_log off;
        return 200 '{"status":"ok"}';
        add_header Content-Type application/json;
    }
}

Production Dockerfile

FROM php:8.3-fpm-alpine AS base

RUN apk add --no-cache     nginx     postgresql-dev     libzip-dev     && docker-php-ext-install pdo_pgsql zip bcmath opcache

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www

FROM base AS deps
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --optimize-autoloader --no-scripts

FROM base AS runner
COPY --from=deps /var/www/vendor ./vendor
COPY . .
COPY docker/nginx/default.conf /etc/nginx/http.d/default.conf

RUN chown -R www-data:www-data storage bootstrap/cache     && chmod -R 775 storage bootstrap/cache

RUN php artisan config:cache     && php artisan route:cache     && php artisan view:cache

EXPOSE 80

COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

Startup Entrypoint

#!/bin/sh
# docker/entrypoint.sh
set -e

php artisan migrate --force --no-interaction

php-fpm -D
nginx -g "daemon off;"

The --force flag runs migrations without prompting in production. Running PHP-FPM as a background daemon, then Nginx in the foreground, keeps the container alive.

Configuring pandastack.json

{
  "type": "container",
  "healthCheckPath": "/health"
}

The Nginx config above returns a health response from /health without invoking PHP, making it fast and reliable for platform health checks.

Environment Variables

Laravel reads configuration from environment variables. Set these in the PandaStack dashboard at [dashboard.pandastack.io](https://dashboard.pandastack.io):

APP_NAME=MyApp
APP_ENV=production
APP_KEY=base64:your-generated-key-here
APP_DEBUG=false
APP_URL=https://yourdomain.com

DB_CONNECTION=pgsql
DB_HOST=pg.internal.pandastack.io
DB_PORT=5432
DB_DATABASE=myapp_prod
DB_USERNAME=myapp_user
DB_PASSWORD=your-secure-password

CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
REDIS_URL=redis://redis.internal:6379

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_USERNAME=your-mailgun-user
MAIL_PASSWORD=your-mailgun-password

Generate your APP_KEY locally with:

php artisan key:generate --show

Copy the output and set it as APP_KEY in the dashboard.

Provisioning a Database

PandaStack provides managed PostgreSQL, MySQL, Redis, and MongoDB instances. Provision a PostgreSQL instance from the Databases section at [dashboard.pandastack.io](https://dashboard.pandastack.io) and copy the connection details into your environment variables.

OPcache Configuration

Add OPcache configuration for PHP performance:

; docker/php/php.ini
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.save_comments=1

Setting opcache.validate_timestamps=0 in production tells PHP not to check for file changes on every request, significantly improving throughput.

GitHub Integration and Deployment

Connect your GitHub repository from the PandaStack dashboard. Every push to your production branch builds and deploys automatically. For CLI deployments:

npm install -g @pandastack/cli
panda deploy

Production Checklist

  • APP_ENV=production and APP_DEBUG=false
  • APP_KEY is set and secure
  • Config, route, and view caches are built in the Docker image
  • Migrations run via php artisan migrate --force on startup
  • OPcache is enabled and tuned
  • Redis is used for session, cache, and queue
  • healthCheckPath points to /health in pandastack.json

Queue Workers with Laravel

Laravel's queue system (backed by Redis) processes background jobs asynchronously. To run queue workers in production, deploy a second container project using the same Docker image with the queue worker command. You can also configure this as a PandaStack cronjob that keeps the worker process running.

# Override the container command for queue workers
php artisan queue:work redis --sleep=3 --tries=3 --max-time=3600

Failed jobs are stored in the failed_jobs table and can be retried with php artisan queue:retry all. Always configure a dead-letter queue and monitoring for production job failures.

Laravel Horizon for Queue Monitoring

For production queue visibility, Laravel Horizon provides a beautiful dashboard for monitoring Redis queue throughput, job failure rates, and worker status. Add it to your project:

composer require laravel/horizon
php artisan horizon:install

Run Horizon instead of the plain queue worker for better observability:

php artisan horizon

Protect the Horizon dashboard in production by restricting access in app/Providers/HorizonServiceProvider.php to specific email addresses or IP ranges — Horizon exposes queue internals and should never be publicly accessible.

Visit [docs.pandastack.io](https://docs.pandastack.io) for more deployment resources.

Ready to deploy?

Start free on PandaStack — no credit card required.

Start free on PandaStack

More in Guide

Browse all Guide articles →

See also