What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Laravel does not check that a job’s timeout is shorter than the queue connection’s retry_after value. The ordering is a rule you have to maintain yourself: set the effective job or worker timeout several seconds below retry_after, so the worker can exit before the queue makes the job available again. On Amazon SQS the equivalent control is the queue’s visibility timeout, not a Laravel setting.
Two settings that control two different events
The timeout and retry_after are easy to confuse because both are measured in seconds and both relate to a job that is taking a long time. They govern different things.
- Timeout limits how long a worker may keep running a job. Laravel’s worker timeout is intended to stop a job that runs too long.
- retry_after tells the queue how long to wait for a reserved job to be deleted or released before it treats the job as unacknowledged and makes it available to another worker.
A worker that is still running a job is not stopped by retry_after. The queue simply stops holding the job for that worker. That is the mechanism behind duplicate processing: if the queue hands the job out again while the first process is still executing it, two workers run the same job.
The ordering rule and what happens when it is reversed
Laravel’s Queues documentation (version 13.x) states the rule directly:
#1 Best Overall
“The –timeout value should always be at least several seconds shorter than your retry_after configuration value.”
The same documentation describes the consequence of breaking it:
“If your –timeout option is longer than your retry_after configuration value, your jobs may be processed twice.”
Recommended Free Tools
The rule is stated as guidance. Laravel does not validate the pair at runtime, and nothing in the framework will warn you when a timeout is set longer than retry_after. Treat the ordering as a configuration invariant that your deployment must keep true, and check it whenever either value changes.
A timeline with illustrative values
The documentation’s examples are a 60-second worker timeout and a 90-second retry_after. These are illustrative values, not mandatory settings. The timeline below shows why the ordering matters, using those numbers.
- t = 0 s: A worker reserves the job. The queue hides it from other workers.
- t = 0–60 s: The worker runs the job. If it finishes and deletes the job, the queue never redelivers it.
- t = 60 s: If the job is still running, the worker timeout fires and the worker process exits.
- t = 90 s: The job is still unacknowledged, so the queue makes it available again. Because the first worker has already exited, only one execution is in progress.
Now reverse the values: a 120-second timeout with a 90-second retry_after. At t = 90 s the queue makes the job available while the first worker is still inside its 120-second budget. A second worker can reserve it and begin running it. Both executions are now active.
Rank #3
The 30-second gap in the first example is wider than the “several seconds” minimum in Laravel’s rule. The rule is a floor, not a target. Size the gap from the behaviour of your jobs and your tolerance for delayed retries, not from the example.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallWhere the effective timeout comes from
More than one layer can set a timeout, and they do not all apply to the same job. The table lists the layers that matter for this ordering.
| Layer | Setting | What it controls | Precedence and notes |
|---|---|---|---|
| Job class | $timeout property |
Maximum run time for that job | Takes precedence over the command-line value, per Laravel’s Queues documentation (13.x) |
| Worker command | queue:work --timeout |
Maximum run time for jobs that do not set $timeout |
Documented default is 60 seconds |
| Horizon supervisor | Supervisor-level timeout | Timeout for the Horizon worker process | Should exceed any job-level timeout and stay a few seconds below retry_after |
| Queue connection | retry_after |
Seconds before an unacknowledged job becomes available again | Documentation example is 90 seconds; not a universal default |
| Amazon SQS queue | Default Visibility Timeout (set in AWS) | Redelivery window for SQS messages | Replaces retry_after for SQS; Laravel does not use the connection option |
When a job defines $timeout, that value is the one to compare with retry_after. Comparing the command-line default against retry_after while jobs override it can give a false sense of safety.
Rank #4
Settings the timeout does not cover
Blocking I/O
Laravel warns that blocking I/O, such as sockets and outgoing HTTP requests, may not respect the job timeout. An external call that hangs can keep the worker busy past its budget. Set connection and request timeouts in the client itself, and keep them comfortably inside the job timeout so the job fails on its own terms before the worker is forced to exit.
The PCNTL extension and process monitors
Laravel states that the PCNTL PHP extension must be installed for job timeouts to work. Without it, the timeout cannot be enforced as described above, and retry_after becomes the only effective limit.
When a worker exits after a timeout, a process monitor such as Supervisor restarts it. If nothing restarts the worker, the queue stops processing until someone intervenes. Verify that your monitor’s own timeouts agree with the layers above rather than assuming one setting governs all of them.
Best Value
Amazon SQS
SQS does not use Laravel’s retry_after option. Redelivery follows the queue’s Default Visibility Timeout, which you set in AWS. Make that visibility timeout exceed the applicable worker timeout by several seconds. Do not edit retry_after in a connection that uses the SQS driver expecting it to change redelivery behaviour, because it does not.
Attempts, timeouts and failed jobs
A timeout ends one execution. Attempts determine how many executions a job may have. If a job repeatedly times out and reaches its maximum attempts, Laravel marks it as failed. Laravel’s versioned documentation also describes a failOnTimeout job property, which changes how a timeout is handled; confirm that the property exists in the Laravel version you run before relying on it.
Because a timed-out job can also be redelivered, a job with side effects should be idempotent. Duplicate execution is the consequence the ordering rule exists to prevent, and idempotency is the protection for the cases the rule does not cover.
Quick Recap
Checklist for checking your configuration
- Identify the queue driver. If it is SQS, the redelivery control is the Default Visibility Timeout in AWS; otherwise it is
retry_afteron the connection. - Identify the effective timeout for each job class, including any
$timeoutproperty, and thequeue:work --timeoutvalue for jobs that do not define one. - Confirm that the effective timeout is several seconds below the redelivery threshold, using values sized to the longest realistic run of each job.
- Set connection and request timeouts on every external client the jobs use.
- Confirm that the PCNTL extension is installed and that your process monitor restarts exited workers.
- Check the retry count and failed-job behaviour, and confirm that jobs with side effects can safely run more than once.
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

