laravelcollective/iron-queue
IronQueue is a Laravel queue driver for IronMQ (Iron.io). It lets Laravel applications push, receive, and process queued jobs through IronMQ using the familiar Laravel queue API, making it useful for cloud-hosted background job processing.
1. Install the connector (Laravel 5.x / Lumen only).
composer require laravelcollective/iron-queue
The package pulls in the iron-io/iron_mq client and restores the iron queue driver that Laravel removed after 5.x. If Composer resolves to a Laravel 6+ app, stop here — the package was last released in 2018 and targets the 5.x Illuminate\Queue contracts.
2. Register the service provider. Laravel (config/app.php):
'providers' => [
// ...
LaravelCollective\IronQueue\IronQueueServiceProvider::class,
],
Lumen (bootstrap/app.php):
$app->configure('queue');
$app->register(LaravelCollective\IronQueue\IronQueueServiceProvider::class);
Lumen has no config/queue.php by default — create one; the driver is resolved from it.
3. Add the connection to config/queue.php.
'connections' => [
'iron' => [
'driver' => 'iron',
'host' => env('IRON_MQ_HOST', 'mq-aws-us-east-1.iron.io'),
'project' => env('IRON_PROJECT_ID'),
'token' => env('IRON_TOKEN'),
'queue' => env('IRON_QUEUE', 'default'),
'encrypt' => false,
],
],
Set QUEUE_CONNECTION=iron (or QUEUE_DRIVER on older 5.x point releases) and put the three IRON_* values in .env.
4. Look first at: the package's src/ for the exact provider/connector class names (they track the old framework IronConnector / IronQueue classes), then your Iron.io dashboard — confirm the project ID, token, and the host region of the queue cluster. A token is region-scoped; a wrong host returns auth failures that look like bad credentials.
5. First use case — a queued job round-trip.
php artisan make:job SendWelcomeEmail
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public int $userId) {}
public function handle()
{
User::find($this->userId)->sendWelcomeMail();
}
}
SendWelcomeEmail::dispatch($user->id);
php artisan queue:work iron --tries=3 -v
If the worker picks the job up and the IronMQ dashboard shows the message draining, you're wired correctly.
Named queues for workload isolation. IronMQ supports multiple queues per project; mirror them in Laravel:
ProcessPodcast::dispatch($podcast)->onQueue('media');
SendInvoice::dispatch($order)->onQueue('billing');
Run workers per tier so a slow media backlog can't starve billing:
php artisan queue:work iron --queue=billing --tries=5
php artisan queue:work iron --queue=media,default --tries=1
Delays for scheduled-ish work. IronMQ holds the message server-side until the delay elapses, so you don't need a scheduler for short offsets:
SendTrialReminder::dispatch($user)->delay(now()->addHours(48));
This is the cheapest pattern for "do X in N minutes" when a cron would be overkill — but see the delay ceiling in Gotchas.
Retries and backoff. Let the worker own the retry policy rather than relying on IronMQ reservation counts:
php artisan queue:work iron --tries=5 --backoff=30 --sleep=3
--backoff gives you escalating redelivery without touching the IronMQ config. Keep tries in the job (public $tries = 3;) when the semantics belong to the job rather than the deployment.
Explicit connection switching. When part of the app stays on Redis locally and IronMQ in production:
SendInvoice::dispatch($order)->onConnection('iron');
or set $connection = 'iron'; on the job class. This is how you keep php artisan test fast while production traffic goes to Iron.
Connection health as a first-class concern. Because IronMQ is a hosted dependency, wrap the enqueue path so failures surface as your own exception, not a raw IronMQ_Exception:
try {
SendInvoice::dispatch($order);
} catch (\Throwable $e) {
Log::error('iron dispatch failed', ['order' => $order->id, 'e' => $e->getMessage()]);
SendInvoice::dispatch($order)->onConnection('database'); // fallback spool
}
A database or redis fallback connection is the standard way to survive an Iron.io outage without losing jobs — dispatch to the fallback, and a periodic sweeper re-dispatches to iron.
Testing stays driver-agnostic. Queue::fake() intercepts at the facade, so it works unchanged:
Queue::fake();
SendInvoice::dispatch($order);
Queue::assertPushedOn('billing', SendInvoice::class);
Queue::assertPushed(SendInvoice::class, fn ($job) => $job->orderId === $order->id);
You only need a live IronMQ project for integration tests against queue:work.
Deployment shape. Run workers under Supervisor (or your platform's process manager) with queue:work, not queue:listen, for the usual reason — work boots the app once per long-running loop; use listen only while developing. Restart workers on deploy via php artisan queue:restart.
The package is archived — treat it as frozen, not maintained. There will be no fix for a PHP 8.1 deprecation, a Illuminate\Queue contract change, or an iron-io/iron_mq CVE. Pin composer.json tightly and plan a migration path (SQS and Redis are first-class drivers in modern Laravel and need no connector).
Verify Iron.io itself is still a viable target before you invest. Iron.io's hosted IronMQ has had a turbulent history since acquisition; confirm your account, project, and endpoint are alive and that your plan's limits match your volume. If you're building something new, choose a driver the current framework supports — this connector's value is almost entirely for legacy 5.x apps that can't move.
Wrong host looks exactly like bad credentials. Tokens are cluster-scoped. Use the host shown in your Iron.io project settings (e.g. mq-aws-us-east-1.iron.io), and double-check it when copying .env between environments.
Message size is a hard IronMQ limit (64 KB). Serialized Eloquent models with eager-loaded relations blow past this without warning. Pass IDs and re-query in handle(), and keep SerializesModels in use so you don't smear state across deployments.
encrypt => true changes the wire format. If you flip it, every producer and consumer must agree — mixing encrypted and plain payloads across two deploys produces garbage jobs that fail deserialization. Roll it out with a drain, or leave it off and rely on transport security.
retry_after doesn't mean what it does on Redis/database. IronMQ owns reservation and visibility timeouts server-side. If you see jobs reappearing mid-processing, the fix is usually the IronMQ reservation timeout (or a worker that's slower than it) — not config/queue.php. Keep queue:work --timeout comfortably below the reservation window.
Delay has a server-side ceiling. IronMQ caps delay (commonly quoted at 7 days). ->delay() beyond the cap fails at push time, not at dispatch time, so a try/catch around the dispatch is the only place you'll see it. For longer horizons, use the scheduler and a zero-delay dispatch.
Failed jobs live in failed_jobs, not IronMQ. Standard commands work — php artisan queue:failed, queue:retry all, queue:forget {id} — but IronMQ's own dashboard won't show them. Check both places when triaging.
Newer queue features are absent. No job batching, no RateLimited middleware semantics, no Horizon, no queue:clear-style driver introspection, no Queue::size() you can trust. If your design depends on those, this driver is the wrong layer.
Debugging order that actually converges: (1) php artisan queue:work iron -v in the foreground; (2) IronMQ dashboard to confirm the message was pushed and reserved; (3) failed_jobs table + storage/logs/laravel.log; (4) verify the iron-io/iron_mq HTTP calls with a tiny standalone script using the same token/host/project. Ninety percent of "it's not working" is a credential/region mismatch caught at step 4.
Extension point if you must patch. Register your own driver with Queue::extend('iron', fn () => new MyIronConnector(...)) in a boot() method that runs after the package provider, so you can decorate push/pop for logging or payload rewriting without forking. Forking is the other option — but on an archived package, a thin local override is the only version you'll be able to maintain.
How can I help you explore Laravel packages today?