Deployment
Website Rebuild with Laravel
Chapter 11 · Deployment
Next.js Rebuild 11 stood up PM2, nginx, and certbot from nothing. Django Rebuild 11 stood up gunicorn behind a new nginx reverse proxy from nothing. Neither course could reuse anything already running on the live server, because the live server wasn't running Next.js or Django. This course's own deployment chapter gets to be genuinely different: the live site is already PHP behind Apache — so this chapter evolves that exact stack instead of replacing it, the strongest "modernize in place" payoff of the whole course.
Production Environment Settings
APP_DEBUG=false parallels Django's own DEBUG=False from two directions at once: it hides Laravel's own detailed error pages (stack traces, file paths, query dumps) from real visitors, and it disables debug-only behavior that would otherwise leak information or cost performance in production. APP_KEY, generated once via php artisan key:generate, parallels Django's own SECRET_KEY directly — it's what Laravel uses to encrypt cookies and sign sessions. Rotating it has the identical consequence Django Rebuild 11 already flagged for SECRET_KEY: every existing session and encrypted cookie becomes unreadable, forcing every logged-in user to sign in again.
Reusing the Live Apache: PHP-FPM via mod_proxy_fcgi
mod_proxy_fcgi — the exact module Apache In Depth's own Chapter 7 covered as Apache's answer to reverse proxying — hands PHP requests straight to a PHP-FPM pool over a Unix socket. There's no nginx being introduced, no PM2 process manager, no separate reverse-proxy layer to learn or operate: the same Apache install already serving the live site's existing pages gets one more VirtualHost block and a FilesMatch directive routing PHP requests to FPM. TLS termination, logging, and the certificate already configured for the live domain are all reused unchanged.
Production Optimizations: The artisan Cache Commands
route:cache fails outright if any route is defined as a closure rather than a Controller-method reference. Every route in this course has pointed at a Controller method since Chapter 3 specifically so this command would work without rewriting anything at deployment time — the payoff of that earlier discipline lands here.
migrate --force is needed because Artisan normally prompts for confirmation before running migrations when APP_ENV=production — a safety guard that would otherwise block an unattended deploy script. config:cache and route:cache combine every config file and route definition into single, fast-loading compiled files, avoiding the cost of re-parsing them on every request.
Building Assets: Chapter 5's Vite Payoff
This produces the compiled, hashed asset files and the manifest Chapter 5's @vite directive reads at render time — the same role Django Rebuild 11's own collectstatic played for that course's static files, and the same underlying build step Chapter 5 warned would need to actually run before deployment, not just in local dev.
File Permissions: storage/ and bootstrap/cache/
Laravel writes logs, compiled views, and cached files into storage/ and bootstrap/cache/ at runtime — both directories need to be writable by the web server's own user (typically www-data), a detail easy to miss on a fresh deploy and one of the most common sources of a confusing "permission denied" error on a brand-new Laravel install.
No Laravel-Vercel Equivalent
Three Deployment Stories, Compared
| Next.js | Django | Laravel | |
|---|---|---|---|
| Process manager | PM2 (new) | gunicorn (new) | PHP-FPM (already running) |
| Reverse proxy | nginx (new) | nginx (new) | Apache (already live, extended) |
| TLS | certbot, configured fresh | certbot, configured fresh | Already configured for the live domain |
| Serverless equivalent | Vercel (free tier) | None | Laravel Vapor (paid, AWS-specific) |
Hands-On Exercises
Explain why this course's own deployment chapter can reuse Apache directly, while both the Next.js and Django rebuild courses had to stand up an entirely new nginx reverse proxy from nothing.
📄 View solutionExplain why route:cache works without any changes at deployment time in this course specifically, tracing the reason back to a decision made in an earlier chapter.
Explain what APP_KEY is used for, and what happens to existing logged-in users if it's rotated — drawing the parallel to Django's own SECRET_KEY.
Chapter 11 Quick Reference
- Strongest "modernize in place" payoff — PHP-FPM behind the site's own already-live Apache, via
mod_proxy_fcgi APP_DEBUG=false— hides detailed error pages and disables debug-only behavior, paralleling Django'sDEBUG=FalseAPP_KEY— Laravel's ownSECRET_KEYequivalent; rotating it invalidates every existing session/encrypted cookieconfig:cache/route:cache/view:cache— precompile for production;route:cachestill rejects closuresmigrate --force— bypasses the production confirmation prompt for unattended deploysnpm run build— Chapter 5's Vite payoff, producing the manifest@vitereads at render time- No free serverless equivalent — Laravel Vapor exists but is paid and AWS-specific, unlike Vercel's free tier for Next.js
- Next chapter: Capstone — A Complete, Working Flexible-Routing Site