Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
DevOpsLaravelPHPResource guide

Laravel production deployment checklist: release gates, evidence and rollback

A Laravel production deployment checklist built around release evidence, compatible migrations, worker refreshes, persistent storage and recovery gates.

Cloudsoft Zone

7 min read

Laravel deployment checklist: Release acceptance gates. Sourced Laravel release gates artwork with article-specific deployment checkpoints.
Image: Cloudsoft Zone layout; Laravel / Public domain
In this article

A deployment can pass every command and still fail the first real customer. The web route works, but the queue runs yesterday's release, a migration removed a column the previous release needs, and nobody knows whether the latest backup can be restored. A command finishing successfully is only one piece of evidence.

This Laravel production deployment checklist turns a release into a set of explicit acceptance gates. It focuses on repeat deployments to an existing application, rather than installing a new server. For the foundation, start with our Laravel 13 server checklist. Here we will record what changed, prove the critical paths and decide when a release should stop.

The examples target a conventional Laravel 13 application behind nginx and PHP-FPM. Commands assume the reviewed release directory and deployment identity have already been selected. Validate the procedure on staging with representative data; the examples are not a claim that your application's migrations or jobs are safe.

Release evidence flows from reviewed artifact to compatible schema and verified workflows.
Successful commands support acceptance; they do not replace workflow checks.

Step 1: Write a release record before touching production

Keep a short record alongside the deployment artifact. It should identify the commit, lockfile, build output, schema changes, configuration changes, operator and previous compatible release. Include a maintenance contact and a clear stop condition. A small team can use a reviewed issue or release note; an unstructured chat history is harder to use during an incident.

Text
Release: <reviewed commit and artifact identifier>
Application: <service and environment>
Database changes: <migration names and compatibility review>
Configuration changes: <key names, never secret values>
Previous compatible release: <artifact identifier>
Backup restore evidence: <date and isolated restore record>
Verification owner: <named operator>
Stop conditions: <failed login, failed writes, rising queue age>

Record observable outcomes rather than “looks fine.” For example, a release that changes invoicing should prove one synthetic invoice can be created and read back. A CSS-only release does not need to replay real payments. Scale the checks to the change while retaining the basic health and recovery gates.

Step 2: Prove the artifact and runtime agree

A production server should receive a known artifact, not resolve a different dependency graph during deployment. Composer installs the committed lockfile; it should not update dependencies as part of a release. Confirm both the command-line PHP used for migrations and the PHP-FPM runtime used for requests.

Shell
cd /srv/laravel-app/releases/<release-id>
composer validate --no-check-publish
composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
composer check-platform-reqs --no-dev
php artisan about
php artisan migrate:status

Replace the release placeholder before running these commands. Review Composer scripts because they execute application code. check-platform-reqs verifies the current CLI environment; it does not prove a different FPM pool has the same extensions. Compare the installed FPM configuration privately and exercise the application through nginx.

Confirm the frontend artifact belongs to the same commit. A missing build manifest or old JavaScript bundle can break a form while server-side rendering appears healthy. Keep the previous asset generation available if old browser tabs still reference it.

Step 3: Separate configuration correctness from secret handling

Before caching configuration, verify required key names and their intended sources. Do not print the entire environment into the release log. Keep production debugging off, the application URL correct and the existing encryption key stable. Check that queue, cache and session configuration points to the intended private services.

Shell
php artisan config:cache
php artisan route:cache
php artisan view:cache

Run these against the new release with its final configuration. If one fails, stop and investigate rather than switching traffic to a half-prepared directory. Configuration caching changes how environment values are read: application code should use Laravel configuration, with env() calls confined to configuration files.

The cache files themselves may contain secrets. Keep them readable only by the required identities and never upload them as public diagnostic attachments. Configuration changes also need to reach long-lived workers; preparing a fresh FPM release does not refresh a worker already running in memory.

Step 4: Classify every migration before approving it

Use a compatibility review, not simply “migrate succeeded.” Ask whether the current release, the new release and the previous rollback candidate can all operate against the transitional schema. A new nullable column may be compatible; renaming a field that active workers still read may not be.

Migration change Review question
Add a nullable column Can old code ignore it and new code tolerate empty values?
Add an index What locking and resource cost will the real database version impose?
Backfill a large table Can the work run in bounded batches outside a request?
Rename or drop a column Have all old readers and workers stopped using it?
Add a uniqueness rule Has existing data been checked for conflicts?

MySQL DDL behaviour depends on the exact operation, version and table. Do not assume every migration is transactional or instantly reversible. Rehearse changes against an isolated representative database and measure their effect there. Preserve production records throughout the rehearsal.

Apply reviewed migrations with the deployment identity and the intended migration privileges. Laravel's --force avoids the production prompt; it provides no extra safety. Avoid executing migrations simultaneously from multiple release workers. Use your deployment lock, or Laravel's isolated migration option where the configured shared cache supports it.

Compatible schema evolution adds fields, migrates data, then removes old schema only after old readers retire.
Destructive cleanup belongs after old readers have stopped using the field.

Step 5: Verify persistent state before switching code

A replaceable release must not own the only copy of uploads, sessions or operational logs. Inspect the environment and storage links, plus permissions for both the deployment user and FPM. Verify the complete path, including parent directory traversal.

Shell
namei -l /srv/laravel-app/current/public/index.php
namei -l /srv/laravel-app/shared/storage
php artisan storage:link

The storage-link command is relevant when the application uses Laravel's public local disk. Private files should remain private, and object-storage applications have different checks. Do not create a public link for every upload directory just to silence an error.

A useful staging exercise uploads a synthetic file, switches to another release and reads the same file back. The exercise proves retention across release changes. Checking only that a directory exists cannot establish that the application uses it.

Step 6: Refresh workers and prove one background effect

Laravel queue workers are long-lived. They need a controlled refresh after code changes, and a process monitor must bring them back. With conventional queue:work workers, a graceful restart signal lets a worker finish its current job before exiting.

Shell
php artisan queue:restart
sudo supervisorctl status laravel-app-worker
php artisan queue:failed
php artisan schedule:list

Use the actual monitored program name. A restart signal relies on the cache configuration being visible to the running workers. Confirm that new processes use the new release path and intended environment. Horizon has its own lifecycle commands; do not run a second Supervisor worker fleet over the same queues accidentally.

Dispatch a safe synthetic job with a traceable identifier. Confirm its recorded completion once, and check queue age rather than just process count. Test a due scheduler action too. A worker marked RUNNING and a cron entry on disk do not prove either business effect occurred.

Step 7: Check the public path and define rollback limits

Inspect nginx syntax, then use real HTTPS routes through the normal hostname. Verify authentication, one relevant write, assets and a bounded dependency check. An /up success is a useful framework boot signal, but it is not automatically a database, Redis or payment-service check.

Shell
sudo nginx -t
curl --fail --max-time 10 https://app.example.com/up
curl --head --max-time 10 https://app.example.com/.env

The second response must not expose configuration; inspect the actual status and routing. Replace the example host and health route. HEAD may differ from GET in your application, so use the implemented method for health verification.

Release acceptance checks HTTPS workflows, queue and scheduler effects, and backup recovery evidence.
A healthy homepage cannot prove queues, persistent state or restoration.

Release gate Pass evidence Stop condition
Artifact Recorded commit, locked dependencies, matching assets Unknown or mixed artifact
Schema Rehearsed compatible migrations Unreviewed destructive change
Runtime HTTPS workflow and synthetic write succeed Authentication or persistence fails
Background work Job and scheduler effects observed Backlog age rises or jobs duplicate
Recovery Previous compatible code and restored backup verified Recovery depends on guesswork

Code rollback cannot undo accepted payments, customer writes or destructive DDL. Decide whether to revert the artifact, disable one feature or follow a database recovery procedure. Blindly running migrate:rollback can cause more damage than the failed release.

Common mistakes

Running composer update on the server changes the release you reviewed. Clearing every cache can remove useful locks or application state. Restarting all services may disrupt healthy workloads and erase transient evidence. Using real customer actions as smoke checks can create duplicate orders or messages.

A repeatable release ends with recorded evidence and an explicit acceptance decision. Keep the checklist small enough to run, but specific enough to catch the failures your application can actually have. For a scoped deployment review, discuss your application with Cloudsoft Zone.

Featured image: Cloudsoft Zone explanatory layout with sourced product artwork: laravel — Laravel (Public domain). Product marks resized and arranged for identification; no endorsement implied. Layout: CC BY-SA 4.0.

Frequently asked questions

  • Does migrate --force make a migration safe?

    No. It suppresses the production confirmation. Compatibility, locking, data preservation and recovery still need review.

  • Is Laravel's up route enough to accept a release?

    No. Add checks for the dependencies and business effects the release changes, keeping detailed diagnostics private.

  • Should every deployment roll back migrations on failure?

    No. Code rollback and database recovery are different operations. Use the rehearsed compatible recovery procedure.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Laravel deployment — Laravel
  2. Laravel migrations — Laravel
  3. Laravel queues — Laravel
  4. Composer CLI — Composer

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.