2026年7月15日
2026年7月15日
LaravelのJob・Queueでバックグラウンド処理を実装する方法
メール送信や画像処理、外部 API の呼び出しのような時間のかかる処理は、Laravel の Job(ジョブ)にしてキュー(Queue)へ積み、ワーカーに裏で実行させると、ユーザーを待たせずに済みます。この記事では、キューの接続先の選び方、ジョブの作成と投入、queue:work によるワーカーの起動、失敗時の再試行、本番で Supervisor を使って常駐させる方法、そして処理が進まないときの確認手順までを説明します。
キューの仕組みを押さえる
キューを使った処理は、3つの役割に分かれます。
- 投入する側: コントローラーなどで「この処理をあとでやって」とジョブをキューに入れる
- キュー: ジョブを順番に保管しておく場所(データベース、Redis、Amazon SQS など)
- ワーカー: キューからジョブを取り出して実行し続ける常駐プロセス
ワーカーは Web リクエストとは別のプロセスで動きます。ジョブを投入するだけでワーカーを起動していなければ、ジョブはキューにたまるだけで実行されません。これがキューを導入したときに最も多いつまずきです。
接続先(ドライバー)を決める
キューの接続先は .env の QUEUE_CONNECTION で決まります。
- sync: キューを使わず、その場で実行します。動作確認には便利ですが、バックグラウンド処理にはなりません。
- database: データベースの
jobsテーブルを使います。Laravel 11 以降の雛形では既定値で、テーブル作成用のマイグレーションも最初から含まれています。小〜中規模なら追加のサーバーなしで始められます。 - redis: 処理件数が多い場合に向きます。Redis のダッシュボード兼ワーカー管理ツールである Laravel Horizon も使えるようになります。
- sqs: AWS 上で運用する場合の選択肢です。
既定の接続や設定項目は版によって変わるため、手元の config/queue.php を確認してください(2026年10月現在)。まずは database で始め、件数が増えてから Redis に移る進め方で十分です。
QUEUE_CONNECTION=database
古いプロジェクトで jobs テーブルが無い場合は、Laravel 11 以降なら php artisan make:queue-table、それより前なら php artisan queue:table でマイグレーションを作り、php artisan migrate を実行します。
ジョブを作成する
php artisan make:job SendWelcomeMail
app/Jobs/SendWelcomeMail.php が作られます。ユーザー登録後にウェルカムメールを送るジョブにしてみます。
<?php
namespace App\Jobs;
use App\Mail\WelcomeMail;
use App\Models\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Throwable;
class SendWelcomeMail implements ShouldQueue
{
use Queueable;
public int $tries = 3; // 最大3回まで試す
public int $timeout = 120; // 1回の実行は120秒まで
public array $backoff = [10, 60]; // 再試行までの待ち秒数(2回目は10秒後、3回目は60秒後)
public function __construct(public User $user)
{
}
public function handle(): void
{
Mail::to($this->user->email)->send(new WelcomeMail($this->user));
}
public function failed(?Throwable $exception): void
{
// 個人情報やトークンをログに残さず、追跡に必要な ID だけ記録する
Log::warning('Welcome mail failed', [
'user_id' => $this->user->id,
'error' => $exception?->getMessage(),
]);
}
}
ShouldQueue を実装したクラスは、キューを通して実行されます。コンストラクターに Eloquent モデルを渡すと、キューにはモデルの ID だけが保存され、実行時にデータベースから取り直されます。そのため、投入から実行までの間にデータが更新されていれば、実行時点の最新の内容が使われます。大きな配列やファイルの中身を直接渡すとキューのデータが肥大化するので、ID やパスを渡してジョブの中で読み込むのが基本です。
handle() の中で例外が投げられると、そのジョブは失敗扱いになり、$tries の回数まで再試行されます。上限に達すると failed() が呼ばれ、failed_jobs テーブルに記録されます。
ジョブを投入する
use App\Jobs\SendWelcomeMail;
SendWelcomeMail::dispatch($user);
// 10分後に実行
SendWelcomeMail::dispatch($user)->delay(now()->addMinutes(10));
// 優先度の高いキューに入れる
SendWelcomeMail::dispatch($user)->onQueue('high');
データベースのトランザクションの中で投入する場合は注意が必要です。コミット前にワーカーがジョブを拾うと、まだ保存されていないデータを探しに行って失敗することがあります。->afterCommit() を付けると、トランザクションのコミット後に投入されます。
ワーカーを起動する
開発中は次のコマンドでワーカーを動かします。
php artisan queue:work
複数のキューを優先順位つきで処理するなら --queue=high,default のように指定します。queue:work はアプリケーションのコードをメモリに読み込んだまま動き続けるため、コードを変更しても自動では反映されません。開発中はワーカーを止めて起動し直すか、変更を検知して読み直す php artisan queue:listen を使います。
本番では Supervisor で常駐させる
本番サーバーでは、ワーカーが落ちても自動で再起動するように、Supervisor などのプロセス管理ツールに任せます。以下は設定例です。パスやユーザー名は自分の環境に合わせて書き換えてください。
; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/my-app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/my-app/storage/logs/worker.log
stopwaitsecs=3600
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "laravel-worker:*"
--max-time=3600 は1時間ごとにワーカーを入れ替えるための指定で、メモリの肥大化を防ぎます。stopwaitsecs は、停止時に実行中のジョブが終わるまで待つ秒数で、最も長いジョブの実行時間より長くしておきます。設定はステージング環境で試してから本番に反映してください。
デプロイ後は queue:restart
新しいコードをデプロイしたら、次のコマンドでワーカーに再起動を指示します。
php artisan queue:restart
ワーカーは実行中のジョブを終えてから終了し、Supervisor が新しいコードで起動し直します。この再起動の合図はキャッシュを通じて伝わるため、キャッシュの設定が正しく動いていることが前提です。
失敗したジョブを扱う
php artisan queue:failed # 失敗したジョブの一覧
php artisan queue:retry all # すべて再実行
php artisan queue:retry <ID> # 特定のジョブだけ再実行
php artisan queue:forget <ID> # 記録を削除
再実行する前に、failed_jobs の例外メッセージや storage/logs/laravel.log を読み、原因が解消されているかを確かめます。外部 API の一時的な障害なら再実行で通りますが、コードの不具合が原因なら、修正をデプロイしてからでないと同じ失敗を繰り返します。メール送信のように何度も実行されると困る処理は、二重実行されても問題が起きない作りにするか、ShouldBeUnique で重複投入を防ぎます。
処理が進まないときの確認手順
起きやすい順に確認します。
- ワーカーが動いていない:
sudo supervisorctl statusで状態を見ます。開発中ならqueue:workを起動しているかを確認します。 - **
QUEUE_CONNECTIONが sync のまま**: その場で実行されるため、画面の応答が遅いままになります。.envを確認し、php artisan config:clearを実行します。 - ワーカーが別のキューを見ている:
onQueue('high')で投入したのに、ワーカーがdefaultしか処理していないケースです。--queueの指定をそろえます。 - 古いコードで動いている: デプロイ後に
queue:restartを実行したかを確認します。 - タイムアウトと再投入の設定が合っていない:
config/queue.phpのretry_afterがジョブの$timeoutより短いと、実行中のジョブが別のワーカーに再び拾われ、二重実行の原因になります。retry_afterを数秒以上長くします。
まとめ
- 時間のかかる処理は
ShouldQueueを実装したジョブにしてdispatch()で投入する - 接続先は
databaseから始め、件数が増えたら Redis と Horizon を検討する - ワーカーは本番では Supervisor などで常駐させ、デプロイ後に
queue:restartを実行する $tries・$backoff・failed()で失敗時の動きを決め、queue:failedとqueue:retryで対処するretry_afterはジョブの$timeoutより長くし、二重実行を防ぐ