Laravel Deployment and Hosting: Cloud, Forge, VPS, Docker, and Octane

Laravel deployment offers numerous options, each with potential pitfalls. Whether deploying a Laravel 12 or 13 application for the first time or migrating from a VPS, your choices in hosting, process management, and zero-downtime strategy will impact your app's reliability. This guide explores major deployment paths for 2025-26: Laravel Cloud, Laravel Forge (with Laravel VPS integration), self-managed VPS, Docker, Laravel Octane (FrankenPHP, Swoole, RoadRunner), and Laravel Vapor. By the end, you'll know which stack suits your project, budget, and team's skills.
For app speed optimization, read our Laravel performance optimization guide alongside this one — deployment strategy and performance tuning are interconnected.
Need a Laravel application deployed, scaled, and secured by a team with nearly three decades of experience in Mumbai? We handle end-to-end custom web application delivery.
Explore Our Laravel Development ServicesPrerequisites and Version Requirements
Laravel 12, released on 24 February 2025, requires PHP 8.2 or higher (PHP 8.3 recommended). Laravel 13, released on 17 March 2026, requires PHP 8.3 minimum. Ensure your server or container image supports the correct PHP version before deployment.
Additional composer requirements before upgrading:
- Carbon 3.x is mandatory for Laravel 12+. Update
"nesbot/carbon": "^2.0"to"^3.0"incomposer.jsonto avoid dependency issues. - Laravel 13 introduces no framework-level breaking changes from 12, but PHP 8.3 enforcement means any Alpine or Ubuntu image still on PHP 8.2 will break your pipeline.
- If using Livewire v3, update to at least v3.6.4 to avoid a critical remote code execution vulnerability (CVE-2025-54068).
Confirm versions with:
php -v # must show 8.2+ for L12, 8.3+ for L13
php artisan --version
composer show laravel/framework | grep versionsThe Five Deployment Paths Compared
Choosing the right deployment path depends on your team's ops expertise, traffic pattern, and budget tolerance for management overhead versus raw infrastructure cost.
| Platform | Managed? | Scaling | Cold Starts | Approx. Monthly Cost | Best For |
|---|---|---|---|---|---|
| Laravel Cloud | Fully managed | Auto (EC2) | None (always-on) | Free (Starter) / $20+ (Growth) | Teams wanting zero ops overhead |
| Laravel Forge + VPS | Partially managed | Manual / Envoyer | None | $6-24 (droplet) + Forge subscription | Full server control, predictable billing |
| Self-managed VPS | None | Manual | None | $6-30 (DigitalOcean/Hetzner) | Maximum control, experienced DevOps teams |
| Docker (Compose/K8s) | None / varies | Compose / K8s | None | Varies by host | Full environment reproducibility, CI/CD |
| Laravel Vapor | Fully managed (Lambda) | Auto (scale to zero) | 100ms–2s | Usage-based (can be near-zero) | Bursty/background workloads, cost optimisation |
Laravel Cloud: Fully Managed EC2 Deployment
Laravel Cloud is a fully managed platform powered by Amazon EC2, launched on 24 February 2025 with Laravel 12. It offers push-to-deploy from Git, automatic scaling, preview environments per branch, and VPC support without SSH.
How Laravel Cloud Works
Laravel Cloud runs applications on always-on EC2 instances, ensuring zero cold starts and predictable response latency. Each environment maps to one or more replicas. The Starter plan is free; Growth plans start at $20/month with usage-based per-replica billing.
Key Cloud behaviors to understand:
- Deployments are automatic on Git push — no Forge webhook or Envoyer pipeline needed.
- Worker restarts after deployment are handled by the platform — no manual
php artisan queue:restartrequired. - Environment variables are managed via the Cloud dashboard with encryption at rest — never commit
.envto version control. - Preview environments provide isolated URLs for each pull request — invaluable for code review and stakeholder demos.
Cloud vs. Vapor: The Serverful vs. Serverless Trade-off
Laravel Vapor is ideal for scale-to-zero economics, abstracting Lambda, RDS, ElastiCache, SQS, CloudFront, and Route 53. However, Vapor introduces cold start latency (100ms–2s) and Lambda's execution time limit, making it unsuitable for WebSocket connections or long-running queue jobs. For latency-sensitive APIs or high-concurrency CRUD apps, Laravel Cloud's always-on EC2 containers outperform in latency benchmarks.
Laravel Forge and Laravel VPS: Managed Provisioning, Full Control
Laravel Forge was rebuilt on 1 October 2025, introducing a redesigned dashboard, native zero-downtime deployments, and Laravel VPS — a DigitalOcean-backed product provisioning Ubuntu servers directly from the Forge UI with consolidated billing.
Laravel VPS Explained
Laravel VPS is integrated within Forge. When creating a new server, select Laravel VPS (powered by DigitalOcean), and Forge provisions the droplet, configures Nginx, PHP-FPM, MySQL/PostgreSQL, Redis, and Supervisor, and sets up SSL — all in one workflow. You retain full SSH and root access. PostgreSQL 18 is supported for new server deployments, alongside MySQL.
Zero-Downtime Deployments in Forge
Forge's zero-downtime deployments use an atomic symlink strategy: each deployment clones your code into a timestamped directory inside a releases/ folder, runs all build steps, then atomically switches a current symlink to the new release. Forge retains the last four releases by default, providing instant rollback capability.
Critical configuration steps before enabling zero-downtime in Forge:
- List
.envandstorage/in the shared paths list — these must persist across releases. Skipping this step results in an empty storage directory and no environment file for each new release. - Forge zero-downtime works on a single server only. For multi-server atomic deployments, pair Forge with Laravel Envoyer — Forge alone cannot coordinate a simultaneous release across multiple servers.
- After October 2025, Forge webhook URLs changed. Update any CI/CD pipelines that POST to old Forge deployment trigger endpoints.
Forge vs. Envoyer: When to Use Both
Forge handles server provisioning and single-server deployments. Envoyer handles deploying one project across multiple servers simultaneously with pre-release health checks and traffic cut-over. If your Laravel app runs on two or more servers behind a load balancer, use both: Forge manages each server's configuration, Envoyer orchestrates the coordinated release.
Self-Managed VPS: Nginx + PHP-FPM Production Stack
A self-managed VPS on DigitalOcean, Hetzner, or Vultr offers the lowest hosting cost for steady-state traffic — a 2 vCPU / 4 GB Hetzner CX22 costs around ₹800/month and handles moderate production load. The trade-off is full ops responsibility: OS updates, SSL renewal, firewall rules, and log rotation are your responsibility.
Production Artisan Commands
Run these commands in order on every deployment. Each is a discrete, reversible step — do not combine them into a single script without testing each individually in staging first:
# 1. Cache configuration (reads all config files, writes bootstrap/cache/config.php) php artisan config:cache2. Cache routes (writes bootstrap/cache/routes-v7.php)
php artisan route:cache
3. Cache compiled views (writes storage/framework/views/)
php artisan view:cache
4. Cache event-listener map
php artisan event:cache
5. Run migrations (--force bypasses the production environment confirmation prompt)
php artisan migrate --force
Critical warning: After running config:cache, Laravel does not read .env at runtime. Any call to env() outside a config/ file returns null. Always read environment values through config('app.debug'), not env('APP_DEBUG'), in application code.
Nginx Server Block for Laravel
# /etc/nginx/sites-available/example.com server { listen 80; server_name example.com; root /var/www/public; index index.php;location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.3-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; }
}
Supervisor for Queue Workers
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=8
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/worker.log
stopwaitsecs=3600After updating Supervisor config: supervisorctl reread && supervisorctl update && supervisorctl restart laravel-worker:*
Docker for Laravel: Multi-Stage Production Builds
Docker ensures full environment reproducibility — the same image runs on a developer's laptop, in CI, and in production. The official Docker guide for Laravel is at docs.docker.com/guides/frameworks/laravel/. Multi-stage builds are the recommended production pattern: a builder stage installs Composer dependencies and caches config, while a slim runtime stage copies only the application artifacts.
Multi-Stage Dockerfile for Laravel
# Stage 1: Build FROM php:8.3-fpm-alpine AS builder WORKDIR /app COPY composer.json composer.lock ./ RUN composer install --no-dev --optimize-autoloader --no-interaction COPY . . RUN php artisan config:cache
&& php artisan route:cache
&& php artisan view:cacheStage 2: Runtime (slim image, no build tools)
FROM php:8.3-fpm-alpine WORKDIR /app COPY --from=builder /app . EXPOSE 9000 CMD ["php-fpm"]
Security rules for Docker in production:
- Run PHP-FPM and Nginx containers as non-root users — add
USER www-datato your Dockerfile. - Never bake
.envfiles or secrets into Docker images. Inject them at runtime via Docker secrets, Kubernetes Secrets, or a cloud secrets manager. - Use Alpine-based images to minimize the attack surface and image size.
- Laravel Sail is the official local Docker development environment — keep Sail for development and use the multi-stage Dockerfile above for production. Never use Sail images in production.
Laravel Octane: FrankenPHP, Swoole, and RoadRunner
Laravel Octane (current version v2.17.4) keeps the application framework booted in memory between requests, eliminating the per-request bootstrap overhead that PHP-FPM incurs. In real-world benchmarks, Octane delivers 2.5–3.1x more requests per second than PHP-FPM, and four Octane workers outperform ten PHP-FPM workers under equivalent load.
Choosing an Octane Driver
| Driver | Language | HTTP/3? | Install Complexity | Best For |
|---|---|---|---|---|
| FrankenPHP | Go (Caddy) | Yes | Single binary | Greenfield projects, no separate reverse proxy |
| RoadRunner | Go (binary protocol) | No | Binary download | Existing CI pipelines, typical CRUD apps |
| Swoole | C++ (PHP extension) | No (via Nginx) | Compile extension | Coroutine concurrency, maximum raw throughput |
| OpenSwoole | C++ (PHP extension) | No | Compile extension | Community fork of Swoole with additional features |
Octane Installation Commands
# Install Octane package composer require laravel/octane--- FrankenPHP (recommended for new projects) ---
php artisan octane:install --server=frankenphp php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=8000
--- RoadRunner ---
php artisan octane:install --server=roadrunner ./rr get-binary php artisan octane:start --server=roadrunner --workers=4
--- Swoole ---
php artisan octane:install --server=swoole php artisan octane:start --server=swoole --workers=4 --task-workers=6
Preventing Memory Leaks in Octane
Octane boots the framework once and reuses the same memory across thousands of requests. Static properties and singleton classes that accumulate state between requests cause memory leaks that are difficult to diagnose. Use lifecycle hooks to reset state explicitly:
use Laravel\Octane\Facades\Octane;// Runs every 60 seconds across all workers Octane::tick('cleanup', function () { cache()->forget('stale-key'); })->seconds(60);
// In a service provider boot() — resets after each request $this->app->terminating(function () { MyStaticService::reset(); });
Octane's Swoole table cache can reach 2 million cache operations per second — far beyond Redis over a network socket. Use it for hot lookup tables that are safe to share across workers (e.g. read-only config data, feature flags).
For deeper performance tuning beyond the deployment layer, our Laravel performance optimization guide covers query optimization, caching strategies, and profiling tools in detail.
Building a high-traffic Laravel application and unsure whether to use Octane, Cloud, or a self-managed VPS? Our team of web developers in Mumbai will analyze your workload and recommend the right architecture — no guesswork.
Get an Architecture ConsultationLaravel 12 and 13: Deployment-Specific Changes
Laravel 12 introduced native health check routes that expose database, cache, and queue status without any third-party packages. Wire these into your load balancer health probe for automatic instance removal when a dependency fails.
Laravel 13 (March 2026) adds PHP Attributes as an alternative to class properties for model configuration, a stable Laravel AI SDK, a Reverb database driver, Passkeys support, and Cache::touch(). From a deployment perspective, the only action required is confirming your server runs PHP 8.3+ and updating your Docker base images accordingly.
Breaking changes affecting deployment pipelines:
- Carbon 3.x: Required for Laravel 12. Pin to
"^3.0"incomposer.jsonbefore deploying. - PHP 8.2 dropped in L13: Update all Dockerfiles, CI runner images, and server PHP versions before upgrading the framework.
- Forge webhook URLs changed (October 2025 rebuild): Update CI/CD integrations that call Forge deployment triggers.
- Octane 2.x: If upgrading from Octane 1.x, read the full UPGRADE.md at
github.com/laravel/octane/blob/2.x/UPGRADE.md— worker lifecycle APIs changed.
Security Considerations for Laravel Deployment
Insecure Laravel deployments are an active target. CISA added a Laravel vulnerability to its Known Exploited Vulnerabilities catalog in March 2026 with a mandatory patching deadline of 3 April 2026. The CVEs you must patch before any production deployment in 2025-26 are:
- CVE-2024-52301 (CVSS 8.7, HIGH): Environment manipulation via crafted query strings when
register_argc_argvis enabled inphp.ini. Disable this directive in every production PHP configuration and update to a patched Laravel version. - CVE-2025-27515: File validation bypass / authentication bypass. Fixed in Laravel 11.44.1 and 12.1.1 — update immediately if on earlier versions.
- CVE-2024-13918 / CVE-2024-13919: Reflected XSS via debug-mode error pages. Mitigation is straightforward: set
APP_DEBUG=falsein every production environment. This one CVE alone is why debug mode must never be on in production. - CVE-2025-54068: Remote code execution in Livewire v3 up to and including v3.6.3. Update Livewire to v3.6.4+ immediately on any app using Livewire.
For a comprehensive treatment of authentication, authorization, and dependency hardening, read our Laravel security guide.
Additional security hardening checklist for deployment:
- Confirm
APP_KEYis a unique, random 32-character string per environment — sharing keys across environments enables session and cookie forgery. - Lock MySQL, Redis, and PostgreSQL ports to internal network only via Forge's firewall UI. Never expose database ports publicly.
- For Forge servers, configure firewall rules inside the Forge dashboard — not just at the OS level — so rules survive server reprovisioning.
- For Vapor (Lambda) deployments, use AWS Secrets Manager for sensitive values rather than plain Lambda environment variables.
- Database migrations must be additive only (backward-compatible) during zero-downtime deployments. The old release symlink remains active for a brief window during the switch — the old codebase must be able to operate against the new schema during that period.
Common Deployment Mistakes and How to Fix Them
Most Laravel production incidents are caused by a small set of repeatable mistakes. Knowing them in advance costs nothing; discovering them at 2 AM during a production outage costs significantly more.
- Calling
env()in application code: Afterphp artisan config:cache, the.envfile is not read at runtime.env('APP_DEBUG')returnsnull. Always useconfig('app.debug')instead. Wrap every.envvalue in aconfig/file. - Deploying with dev dependencies: Always run
composer install --no-dev --optimize-autoloader. Never deploy avendor/directory that includes packages like Faker, PHPUnit, or Laravel Telescope. - Forgetting to restart long-running processes: Octane workers and queue workers hold the old codebase in memory after a deployment. In Forge, trigger a worker reload; in Laravel Cloud this is automatic.
- Skipping shared paths in Forge zero-downtime: Without listing
.envandstorage/as shared paths, each new release starts with a blank environment and empty storage directory. - Using Forge's atomic deployment for multi-server setups: Forge zero-downtime supports one server only. Add Envoyer for multi-server atomic deployments.
- Using Octane without auditing packages: Third-party packages that hold stale references to the request object between Octane requests cause subtle, hard-to-diagnose bugs. Audit every package against the Octane compatibility list before enabling it in production.
- Running
migrate --forcewithout a staging test: Schema changes causing irreversible data loss are the leading cause of Laravel production disasters. Always test migrations in a staging environment against a production data clone first. - Not pinning Carbon 3.x for Laravel 12: If
nesbot/carbonis still at^2.xincomposer.json, the upgrade to Laravel 12 will fail with a dependency conflict duringcomposer install.
Testing and Verifying Your Deployment
A deployment is not complete until it is verified. Run this checklist after every production release:
- Visit the application URL and confirm the expected homepage renders without errors.
- Check
/api/health(or your health check route) — Laravel 12+ exposes database, cache, and queue status by default. All three should returnok. - Confirm
APP_DEBUG=falsein production by intentionally triggering a 404 — you should see a generic error page, not the Ignition debug screen. - Verify queue workers are running:
supervisorctl statusshould show alllaravel-workerprocesses asRUNNING. - For Octane deployments:
php artisan octane:statusshows worker health and memory usage per worker. - Run your smoke test suite against the production URL (read-only tests only — no mutations in production).
- Check application logs:
tail -n 100 storage/logs/laravel.logfor any errors logged during the first 60 seconds of traffic. - Confirm Supervisor is restarting failed queue workers: check
storage/logs/worker.logfor restart events.
Frequently Asked Questions
What is the difference between Laravel Cloud and Laravel Forge?
Laravel Cloud is a fully managed, serverful platform powered by Amazon EC2 that handles all server configuration, scaling, and deployments automatically — you push code and the platform handles the rest. Laravel Forge is a server management panel that provisions and configures VPS servers (including DigitalOcean droplets via the new Laravel VPS integration) while giving you full SSH and root access to the underlying machine. Cloud is ideal for teams that want zero operations overhead; Forge is better when you need custom Nginx configuration, cron job management, or predictable flat-rate monthly billing.
When should I use Laravel Octane instead of PHP-FPM?
Use Laravel Octane when your application needs significantly higher throughput and you can ensure your codebase is stateless between requests. Octane keeps the Laravel framework booted in memory and delivers 2.5–3.1x more requests per second than PHP-FPM in real workloads. However, it requires auditing all third-party packages for Octane compatibility, implementing lifecycle hooks to reset static state, and monitoring memory usage per worker. PHP-FPM is simpler to operate, compatible with all packages, and is the correct default choice for most applications — switch to Octane only when benchmark data shows PHP-FPM is the bottleneck.
How do I achieve zero-downtime deployments with Laravel?
Laravel Forge now enables zero-downtime deployments by default for all new sites using an atomic symlink strategy — each release is deployed to a new timestamped directory, then a symlink switches to the new release instantaneously. For single-server setups, Forge's built-in zero-downtime is sufficient. For multi-server setups behind a load balancer, pair Forge with Laravel Envoyer, which coordinates a simultaneous release across all servers with pre-release health checks. Always list .env and storage/ as shared paths in Forge, and ensure your database migrations are additive-only so the old release can operate against the new schema during the switchover window.
Is Docker the best way to deploy Laravel in production?
Docker is the best choice when environment reproducibility, portability across cloud providers, and CI/CD pipeline integration are your top priorities. A multi-stage Dockerfile using the php:8.3-fpm-alpine base image produces a slim, auditable artifact that runs identically in development, staging, and production. The trade-off is operational complexity: you must manage Dockerfile maintenance, an image registry, and container orchestration (Docker Compose or Kubernetes). Managed platforms like Laravel Cloud and Forge abstract this complexity at the cost of reduced customization and, in some cases, higher monthly spend at scale.
Should I use Laravel Vapor for my Laravel application?
Laravel Vapor is the right choice for workloads with highly variable or bursty traffic patterns where the ability to scale to zero cost during idle periods outweighs the latency cost of Lambda cold starts (100ms–2s per new Lambda container). It is not suitable for applications that require long-running processes such as WebSocket connections (use Laravel Reverb on a traditional server instead), persistent TCP connections, or execution times exceeding Lambda's maximum timeout. For steady-state, latency-sensitive APIs and high-concurrency applications, Laravel Cloud's always-on EC2 containers or Octane on a VPS deliver lower and more predictable response times than Vapor.
