Vol. 01 — 2026

Laravel 13 Production: 12 Lessons Gujarat 2026 [Guide]

Laravel 13 production is where I spend most working days: twelve months of shipping client apps from my Junagadh lab, holding P95 at 210ms on a ₹1,400/month VPS. Zero-downtime deploys, boring schema habits, and queues that never page me at night.

I am Deepak Bagada, founder of SaaS Next in Junagadh, Gujarat. I run Laravel backends for catalog businesses, machinery traders, and my own products. This post is the unglamorous ledger: the queue war story, the migration scare, the deploy script, the cost table, and what I would skip.

Queues First: The 18:00 Bottleneck That Taught Me Everything

My first production lesson arrived as a traffic spike. A Surat catalog client synced 4,000 SKUs every evening at 18:00, and I had put the sync in a web request with a 60-second timeout. First big sync locked the products table, web requests queued behind it, and the storefront went dark for eleven minutes during peak browsing. The client called before my monitoring did, which still stings.

The fix took an afternoon: every slow job moved to Redis queues with a dedicated worker, the web process never waits on anything slower than 200ms, and Pydantic-style validation moved offline into the job. I also added Horizon-style supervision with auto-restart so a dead worker pages back to life alone. Evening syncs now finish in nine minutes without touching storefront latency, and P95 for shoppers stays at 210ms through the whole window. If your Laravel app still does heavy work inside controllers, my web development services start exactly here.

// File: app/Jobs/SyncCatalogJob.php — evening sync, offline, retryable
// (standard PHP opener omitted in print; file starts with the usual tag)
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SyncCatalogJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $backoff = 120;

    public function __construct(public int $clientId) {}

    public function handle(): void
    {
        // Chunked sync: 500 rows per chunk, never lock the table.
        // Validation runs here, offline — the web process stays under 200ms.
    }
}

The rule for my code samples: a space after every opening angle bracket, never a bare markup-looking sequence.

War Story 2: The Migration That Nearly Ate a Friday

Month seven, a machinery client needed a NOT NULL column on a 2-million-row orders table. My junior ran the migration at noon. Postgres rewrote the table, locks piled up, and writes blocked for six minutes. Quotes could not save during lunch hour, which for that business is money on fire.

Since that Friday I follow three rules. One, heavy migrations run inside maintenance windows with a lock-timeout statement first. Two, every migration gets a down path that I have actually executed once on staging. Three, schema changes ship separately from code changes — expand first, migrate data, then contract. Twelve months, forty-plus migrations since, zero incidents. Boring is the feature.

# File: deploy.sh — zero-downtime Laravel deploy I run from Junagadh
php artisan down --render="errors.503" || true
git pull --ff-only origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
php artisan queue:restart
php artisan up
curl -sf https://saasnext.example.com/health || php artisan down

The health gate at the end is the whole philosophy: the deploy is not done when files move, it is done when the app answers. That script also feeds the speed numbers in my load-time audit, and the same templates render the magazine layout in why this portfolio looks like a magazine.

Monitoring stays equally plain. One uptime check hits the health endpoint every minute, one log watcher alerts on queue-worker restarts, and one weekly report emails slow-query counts from Postgres with anything over 200ms highlighted. Twice this year the slow-query mail caught a missing index before users felt it — a five-minute fix that saved a weekend. Fancy observability suites can wait until revenue justifies them; attention is the actual monitoring tool.

Twelve Months of Costs: The Honest Table

Item Monthly cost What it replaced
Single VPS, 4GB RAM (Hostinger) ₹1,400 ₹9K managed platform bill
Managed Postgres add-on ₹0 (self-hosted in Docker) ₹2.5K hosted DB
Redis queue + cache (same VPS) ₹0 ₹1.8K cache service
Queue worker supervision ₹0 (systemd + restart) ₹3K worker dyno
Backups (daily snapshots) ₹450 Tape-era anxiety
Total ₹1,850 ~₹16K in managed equivalents

Self-hosting on one box demands respect — snapshots tested by restore, not by faith — but the savings fund real work. My AI development services run their embeddings workers on this same box, isolated in Docker with memory limits so a model can never starve checkout traffic.

What I Would Skip Next Time

Three things I bought or built and now regret. First, a managed queue dashboard subscription at ₹2,000 a month that duplicated what worker logs already told me; cancelled in month four. Second, premature test coverage on admin CRUD screens — hundreds of assertions guarding pages clients rarely open, while one critical checkout path went untested for months. Third, Docker for the smallest brochure jobs, where a plain static build would have deployed in minutes. Next year the rule is simpler: instrument the money paths first, insure the rest later, and match the tooling weight to the revenue at risk.

Developer reviewing Laravel Horizon queue dashboard with green workers and deployment terminal beside it

When NOT to Use Laravel

Skip Laravel when your product is a real-time game, a trading terminal, or a collaborative editor with sub-100ms sync. PHP request lifecycle and queue delays fight you there; reach for Go, Elixir, or a purpose-built sync service instead. I have referred two such projects out rather than force-fit my favorite tool.

Also skip the full framework for a three-page brochure site. A static build deploys faster, costs less, and has no queue worker to supervise. I say this as someone whose living depends on Laravel: the framework earns its keep at form-plus-auth-plus-database complexity and above. Below that line, it is overhead dressed as craftsmanship.

Frequently Asked Questions

What did 12 months of Laravel 13 production teach you?

Laravel 13 production taught me that queues, boring migrations, and health-gated deploys matter more than any new feature. I hold P95 at 210ms on a ₹1,850/month stack with zero migration incidents across forty-plus deploys in twelve months.

How much does hosting a Laravel app cost in India in 2026?

My single-VPS Laravel stack costs ₹1,850 a month including snapshots: ₹1,400 box plus ₹450 backups, with Postgres and Redis self-hosted. Managed equivalents of the same setup quoted near ₹16K monthly.

How do you deploy Laravel with zero downtime?

I take the app down with a friendly page, pull fast-forward only, install dependencies, migrate with force, warm all caches, restart queue workers, then lift the maintenance flag and hit a health endpoint. The health check at the end decides whether the deploy counts.

Is PHP dead for new projects in 2026?

No. PHP runs the unglamorous commerce that pays salaries, and Laravel 13 ships forms, auth, queues, and admin scaffolding faster than anything I have timed. I ship client products in weeks from my Junagadh lab while framework debates continue without me.

Bottom Line

Laravel 13 production, twelve-month verdict: queues offline, migrations boring, deploys health-gated — P95 210ms on ₹1,850 a month.

KEEP READING

← All journal articles Get in touch →