Every notification in the application — quote confirmations, status changes, SMS alerts — was executing inside the HTTP request that triggered it. QUEUE_CONNECTION=sync. A customer confirms a service, the server sends the email, composes the SMS, notifies the admin, and only then returns the response. Total time: however long all of those external API calls take. When it took too long, nginx returned a 504 gateway timeout.

The queue infrastructure had never been set up. The jobs table didn't exist on the beta server. Both environments pointed at sync. Redis was already running on the Forge server — installed and idle, waiting for someone to use it.

Nine notification classes got ShouldQueue and the Queueable trait restored — they'd been temporarily removed while debugging email failures, and "temporary" had stretched into weeks. afterCommit() ensures notifications dispatch only after the database transaction commits, preventing race conditions where the notification references data that hasn't been written yet.

Queue workers launch through a keep-alive script watched by a cron job that runs every minute. If the worker dies — deploy restart, memory exhaustion, hourly max-time reset — cron brings it back within sixty seconds. Simpler than Supervisor for a single-worker setup. The Forge deploy scripts already include queue:restart, so workers pick up new code after each deploy.

The customer's experience changed from "click confirm, wait four seconds, see response" to "click confirm, see response immediately, receive email a few seconds later." The four seconds moved from blocking to background. Same work, different timing, completely different feel.