Most Laravel launch problems are not exotic. They are a missed environment variable, a queue worker that was never started, a debug page left switched on, or a migration that locked a table. This Laravel production deployment checklist is the list I run through before and after a go-live. It works whether you deploy with a managed tool, a container platform or your own server. If you want help doing it, my cloud and DevOps deployment service covers exactly this kind of work.
Use it as a working document: copy it into your repository and tick items off per release.
Before you deploy: environment and configuration#
-
APP_ENV=productionandAPP_DEBUG=false. Debug mode can leak stack traces, environment values and queries to anyone who triggers an error. - A unique
APP_KEYgenerated for production and stored outside the repository. Never reuse the development key. - Secrets live in the environment, not in committed files. Rotate anything that was ever pushed to a repository.
-
APP_URLis correct, including the scheme, so generated links and signed URLs work. - Cache the configuration. Run
php artisan config:cache,route:cache,view:cacheandevent:cacheas part of the deploy. Afterconfig:cache, only callenv()inside config files, because it returns null elsewhere. - Optimise the autoloader:
composer install --no-dev --optimize-autoloaderon the server or in your build.
The Laravel docs describe each of these in the official deployment guide.
Security checks#
- HTTPS everywhere, with HTTP redirected, and
SecureandHttpOnlycookie flags set. - Only the
publicdirectory is web-reachable. Your.env,storageandvendorfolders must never be served. - Trusted proxies configured if you sit behind a load balancer or CDN, otherwise client IPs and HTTPS detection break.
- Rate limiting on login and API routes. I cover design choices in API rate limiting architecture.
- Dependencies audited: run
composer auditand update anything with a known vulnerability. - Admin areas protected with strong authentication, and ideally restricted by role, network or both.
⚠️Warning
If .env was ever reachable over HTTP, treat every secret in it as compromised and rotate it.
Database and migrations#
- Back up before you migrate, and confirm you can restore. A backup you have never restored is a hope.
- Run migrations with
--forcein production, as part of the deploy script, not by hand. - Check migrations for locking. Adding an index or altering a large table can block writes. Use the safe patterns: add nullable columns first, backfill in batches, then add constraints.
- Make migrations backward compatible with the code that is currently running, so a rolling deploy does not break mid-flight. The expand-then-contract approach covers it.
- Database credentials use a least-privilege user, not root.
Queues, scheduler and background work#
- Run a process supervisor (Supervisor or systemd) for queue workers so they restart after a crash or deploy.
- Restart workers on each deploy with
php artisan queue:restart, otherwise they keep running old code. - Add the scheduler cron entry:
* * * * * php /path/artisan schedule:run. Missing this is a classic launch bug. - Set job timeouts and retry limits, and watch the failed jobs table. For heavier workloads I use Redis and Horizon, which give visibility into throughput and failures.
On the Violerts NYC platform, the stack includes Redis, Laravel Horizon and asynchronous processing. Any system built that way depends on supervised workers that restart cleanly on deploy, which is why I treat this section of the checklist as non-negotiable.
🛠 Engineering Service
Need specialized help with Cloud, DevOps & Deployment?
Set up and improve production infrastructure for web applications, including VPS environments, Docker, CI/CD, web servers, migrations, and reliable deployments.
Discuss Architecture →About this service
Performance and caching#
- Use a real cache and session driver such as Redis in production, not
fileon a multi-server setup. - Run OPcache with sensible settings, and clear or reset it on deploy.
- Serve assets through a CDN or at least with long-lived cache headers and hashed filenames.
Deploy process, monitoring and rollback#
- Make deploys repeatable. A script or pipeline should do the same steps each time: pull, install, build, migrate, cache, restart. Doing it by hand invites mistakes. See zero-downtime deployments in Laravel for the release-directory approach with atomic switching.
Beyond the numbered list, these matter just as much:
- Error tracking such as Sentry, so exceptions reach you before customers do.
- Uptime and health checks on a route that also tests the database and cache.
- Structured logs sent somewhere you can search. See observability for Laravel.
- A rollback plan. Know which release to go back to and whether the database changes can be reversed. If a migration cannot be undone, plan a forward fix.
- Disk and memory alerts. Full disks and log files are a common silent failure.
A simple deploy sequence#
Here is a minimal pattern that covers the essentials:
bash
Copy
git pull origin main
composer install --no-dev --optimize-autoloader --no-interaction
npm ci && npm run build
php artisan down --retry=30 # skip if you do atomic releases
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan queue:restart
php artisan up
This is a starting point. With zero-downtime tooling, you build into a new release directory and switch a symlink instead of using maintenance mode.
Post-deploy smoke test#
Right after a release, check:
- Home page, login and one key transaction work.
- A queued job runs to completion.
- A scheduled command appears in the logs.
- No new errors are showing in your tracker.
- Emails send, using a real test address.
On the Henceforward WordPress migration, moving to a new infrastructure on Scaleway with Laravel Forge meant that the checks around server configuration, security and production optimisation were as important as the application itself. The same discipline applies to a Laravel app: the environment is part of the product.
If you are still choosing where to host, read my comparison of Laravel hosting options and cost.
Frequently asked questions#
Should I use php artisan down for every deploy?#
Only if you cannot do atomic releases. Maintenance mode shows users an error page, so for busy sites a symlink-based zero-downtime release is better.
Why does config:cache break my app?#
Because env() returns null outside config files once the config is cached. Read environment values in config files and use config() everywhere else.
How often should I back up?#
At a frequency matching how much data you can afford to lose. Before every migration, and on a schedule for everything else, with periodic restore tests.
Do I need Kubernetes?#
Usually not. Many Laravel apps run well on a single well-configured server or a managed platform. Pick complexity when the load demands it.
Key takeaways#
- Debug off, secrets safe, config cached, HTTPS on: the basics prevent most launch incidents.
- Back up before migrating, and write migrations that are safe for running code.
- Supervise queue workers, restart them on deploy, and add the scheduler cron.
- Automate the deploy, monitor errors, and know your rollback before you ship.
If you would like a second pair of eyes on a launch, book a call or get in touch.
Originally published at aqibjavaid.site.
Top comments (0)