My Campaign Is Stuck in "Sending" — What to Check

A campaign that still shows "Sending" hours after you hit send. Walk through the three UI signals that tell you what's actually happening — then the two-click fix that resolves it for most teams.

What to check from the dashboard

A campaign that says Sending hours after you launched it isn't always actually stuck — most of the time AcelleMail's auto-rerun has already picked it back up and is finishing the send in the background. The dashboard tells you whether to wait or whether to act.

Open Campaigns in the left sidebar. Look at the row for your campaign — there are three things to read:

Campaign list — Sending badge visible

  1. The status badge — "Sending" means the campaign is in progress.
  2. The "Sent" counter — does it match your list size, or is it stuck at a lower number?
  3. The "Last updated" timestamp — when did the worker last touch this campaign?

If "Last updated" is less than 10 minutes ago, AcelleMail is actively sending. Refresh in a few minutes — it'll catch up.

If "Last updated" is 30+ minutes ago and Sent is below the list size, the worker hasn't been processing this campaign — keep reading.

Open the campaign and look at the live counts

Click the campaign name. The overview page shows the per-status breakdown — Delivered, Bounced, Failed, Opened, Clicked:

Campaign overview — sending state

The two numbers that matter:

  • Sent vs. list size — if Sent < list size and not moving, AcelleMail isn't picking up the campaign for processing.
  • Failed count — if this is growing, the SMTP server is rejecting messages and your campaign is stuck on the same recipient batch.

Check the tracking log for what AcelleMail tried to send

Inside the campaign, the Tracking log tab is the per-message audit. Every send attempt — successful, bounced, deferred — has a row:

Tracking log — per-message audit

Scroll to the latest entries:

  • All rows show recent timestamps → AcelleMail is sending right now, the dashboard is just showing slightly stale counts. Refresh in 5 minutes.
  • Latest row is hours/days old → no send activity. The worker is the bottleneck (see below).
  • Latest rows show SMTP errors repeatedly → your sending server credentials, IP reputation, or DNS records need attention. Open Settings → Sending servers and click Test connection on the active server.

Common UI causes and the fix from the dashboard

What you see Likely cause What to do
Status: Sending · Sent count not moving · Tracking log empty The sending worker is idle. Ask the operator to restart workers (see Advanced below). If you're self-hosted as a solo operator, see the operator section.
Status: Sending · Sent count = part of list · Last updated >1 hour A batch failed mid-run; AcelleMail auto-rerun will pick it up next 10-minute window. Wait 15 minutes. Refresh. If Sent still doesn't move, jump to the operator section.
Status: Sending · Failed count growing fast SMTP rejection. Settings → Sending servers → Test connection. Common causes: expired API key, exceeded daily quota, IP blocked.
Status: Sending · all recipients in tracking log show "deferred" Receiving servers are throttling. Wait — deferred messages are retried automatically. If still deferred after 24h, the sending server's IP needs a warm-up. See Why are my emails going to spam.

Pause and resume from the campaign UI

If you need to stop the send (e.g. wrong list, urgent edit), the campaign overview has a Pause button while it's in Sending state. Pause → fix the issue → click Resume — AcelleMail picks up where it left off, no duplicates.

Advanced: server-side checks for the operator

If the dashboard checks above point at the worker being idle, the operator needs to verify the queue worker process is running. AcelleMail pushes campaign sends onto a Laravel queue; if the worker process dies (OOM, missing cron, supervisor not running), campaigns sit in the "sending" state indefinitely.

Quick worker check (SSH to the AcelleMail host):

# Is the queue worker process up?
ps aux | grep "queue:work" | grep -v grep

# How big is the queue backlog?
php artisan queue:size

# How many failed jobs need a retry?
php artisan queue:failed | wc -l

Restart the worker (assumes Supervisor is managing it — the recommended install pattern):

supervisorctl restart acelle-queue-worker:*

If you're running the worker manually without Supervisor:

nohup php artisan queue:work --queue=high,default,low --sleep=3 --tries=3 &

Force a campaign rerun — AcelleMail's SendCampaign console command picks up stuck campaigns in 10-minute windows. To trigger it immediately:

php artisan acelle:send-campaign --force

Cron check: AcelleMail's acelle:run command is the master scheduler. If it stopped running (cron disabled, crontab edited), campaigns never get picked up. Confirm with:

crontab -l | grep acelle:run
# Expected: * * * * * php /path/to/acelle/artisan acelle:run >/dev/null 2>&1

Inspect failed jobs:

php artisan queue:failed
# If a campaign send failed and the worker gave up after `tries` exhausted,
# retry it with:
php artisan queue:retry all

Logs to read when the dashboard counts don't match worker behavior:

  • storage/logs/laravel.log — Laravel exceptions during send
  • storage/logs/queue-worker.log (if configured) — per-job timing
  • The campaign tracking log inside the dashboard remains the source of truth for per-recipient outcome; the operator logs explain WHY the worker stalled.

Related articles

27 comentarios

17 comentarios

  1. emma.whitaker
    This article saved me about 4 hours of debugging today. The diagnostic order at the top is exactly the workflow I needed.
  2. tnovak.cz
    We hit cause #5 last quarter — SES sandbox limits we didnt know about. The 'wait it out' advice is right. We tried aggressive retries first and it just made things worse.
  3. anna.k.pm
    sent this to my whole ops team. should be required reading before anyone touches the prod queue
  4. ahmed.hassan.c…
    Adding to this: we had a campaign stuck for 6 hours one time. Turned out the running_pid was alive but the worker was deadlocked on a slow MySQL query. ps showed it as running, kill -9 was the only fix. Now we monitor for stale running_pid > 30 min.
  5. rafa.silva.br
    I think the recommendation to wait 10-15 minutes is too patient for production. We alert at 5 minutes and our ops team triages. Stuck status > 5 min usually means something real.
  6. priya.iyer.ops
    What about the case where campaign:rerun itself crashes silently? We had cron running but :rerun was failing on a deleted customer and just bailing out. No alerts
    1. admin
      Good catch. The bounds (200/32) are hardcoded in the runtime. We've discussed making them configurable; not a near-term priority but it's tracked.
  7. v.petrova.ru
    curious if the 200-row / 32-advance bounds are configurable. we have one customer with very large automation flows and i wonder if they hit this...
    1. admin
      Suppression list import via CSV capturs all opt-outs including preference-center ones if you exported with the right field set. The export filter defaults exclude some — check the 'include unsubscribed' checkbox on Mailchimp's export wizard.
    2. admin (editado)
      suppression list import via csv captures all opt-outs including preference-center ones if you exported with the right field set. the export filter defaults exclude some — check the 'include unsubscribed' checkbox on mailchimp's export wizard.
  8. danrey.dev
    Thanks for grounding this in actual source — much better than the generic Laravel advice you find on Stack Overflow.
    1. admin
      glad it landed. drop suggestions in the comments and we'll incorporate them on the next refresh.
  9. m.schmidt78
    Question: in step 4, the campaign log line about 'force resuming' — does that show up in laravel.log or only the per-campaign log file? Our laravel.log seems silent on this...
    1. admin
      same answer as above for saas-tenant — works the same way per-tenant, with the caveat that the cron must be set per-customer (not just system-wide)
  10. linhpm.devs
    If you're on Ubuntu 22.04 with the default cron package, the time zone is UTC even if /etc/timezone says otherwise. Bit us once — the scheduled job timing was 7 hours off.
    1. admin
      Solid addition — adding to the article on the next refresh.
  11. jmorrison.itop…
    cause #2 (dead supervisor) hit us after a kernel-upgrade reboot. The systemctl enable bit was missing. Took 2 hours to figure out because nothing was logging.
    1. admin
      great real-world detail. your point about stale running_pid > 30 min as an alert is something we should add to the diagnostic flow.
  12. d.cohen.tlv
    For anyone running on EC2 t3.small — bump to t3.medium before yo scale past 50k subs. We learned the hard way that worker OOM kills explode when MySQL is on the same instance fwiw
  13. nadia.r.cl
    is there a way to detect cause #6 (lost DB connection) before workers wedge? Looking for a heartbeat metric to alert on.
    1. admin
      Depends on your version. 5.x supports it natively; 4.x needs a config flag set in `.env`. We'll note this caveat in the article on the next pass.
  14. i.rossi.mil
    When you say to bump wait_timeout to 86400, does that need a MySQL restart or is it dynamic? We're on RDS so restarts are expensive
    1. admin
      We're aware of the silent-bail-out on deleted customers — there's an open issue for it. Workaround for now: monitor the campaign:rerun log for absence of expected log lines, alert when silent for > 20 min
    2. admin (editado)
      Yes — strict alignment requires the From: domain to match exactly. Subdomain-level (`bounce.example.com` vs `example.com`) passes relaxed but fails strict. Most operators run relaxed; the rare strict-DMARC setups need explicit subdomain DKIM configuration.
  15. hung.nguyen.it
    one more thing worth adding: if you use cloudflare in front, make sure to whitelist the worker ips for outbound — we had retries failing because cloudflare was rate-limiting our own outbound traffic to ses.
  16. lucas.bernard.…
    Pro-tip: set `pm.max_children` on your PHP-FPM pool to at least 2x your supervisor worker count. Otherwise even ack-fast endpoints choke when the queue rate goes up.
  17. y.yamamoto
    Confirming the campaign:rerun auto-fix actually works. We had a worker OOM mid-batch last week, walked away thinking we'd need to manually intervene, came back in 15 minutes and it had recovered itself.

More in Troubleshooting