Two Tools for Two Problems
Background processing in web applications generally falls into two categories: work that happens on a schedule and work that happens in response to an event. Cron jobs and task queues are built for these two cases respectively — and mixing them up leads to unnecessary complexity.
This guide explains how each works, the trade-offs involved, and a practical framework for deciding which one to reach for.
What Is a Cron Job?
A cron job is a task that runs at a fixed, recurring time interval defined by a cron expression. The schedule is the trigger — not a user action or system event.
# Examples of cron schedules
0 2 * * * # Every day at 2 AM
0 9 * * 1 # Every Monday at 9 AM
*/15 * * * * # Every 15 minutes
0 0 1 * * # First of every month at midnightCron jobs are ideal for:
- Nightly database backups: Run at 2 AM every day regardless of user activity.
- Weekly email digests: Send at 8 AM every Monday to all subscribers.
- Periodic data synchronization: Pull from an external API every 30 minutes.
- Cleanup routines: Archive old records or delete expired sessions every hour.
- Report generation: Generate and email a PDF report on the 1st of each month.
The defining characteristic is predictability. You know exactly when the job runs and it runs whether users are active or not.
What Is a Task Queue?
A task queue is a broker-based system where producers enqueue tasks and workers dequeue and execute them. Work enters the queue when something happens — a user action, an incoming webhook, a state change in your application.
// Producer: triggered by user uploading a file
app.post('/upload', async (req, res) => {
const file = await saveFile(req.file);
await imageQueue.add({ fileId: file.id, size: 'thumbnail' });
res.json({ success: true, fileId: file.id });
});
// Worker: processes jobs as they arrive
imageQueue.process(async (job) => {
await resizeImage(job.data.fileId, job.data.size);
});Task queues are ideal for:
- Image and video processing: Triggered when a user uploads a file.
- Email sending: Triggered by registration, purchase, or password reset.
- Webhook processing: Handle incoming events from Stripe, GitHub, or Twilio.
- Order fulfillment: Triggered when a payment is confirmed.
- Third-party API calls: Offload slow external calls from the request cycle.
The defining characteristic is event-driven arrival. The rate of work is determined by user activity and system events, not a clock.
Decision Framework
Ask yourself these three questions:
1. Is the trigger time-based or event-based?
If you know exactly when the job should run (every night at midnight, every Monday morning, every 15 minutes), use a cron job. If the job runs because something happened (a user signed up, a file was uploaded, a payment was processed), use a task queue.
2. Does the volume vary with user activity?
Cron jobs have predictable load — the same job runs at the same time. Task queues experience load spikes when users are active. If your background work spikes with traffic, a queue with multiple concurrent workers is the right tool.
3. Do you need retries on failure?
Both support retries, but task queues have richer retry semantics — exponential backoff, dead-letter queues, and manual re-processing. Cron jobs retry simply by running again at the next scheduled interval.
When to Use Both
Most production applications need both. A common pattern:
- Use a cron job to trigger periodic batch work (e.g., find all users whose trial expired today).
- Have the cron job enqueue individual tasks into a task queue (one task per user).
- Workers process each user's task concurrently.
This gives you the scheduling simplicity of cron and the concurrency of a queue.
Running Cron Jobs on PandaStack
PandaStack is a cloud PaaS with native support for scheduled Docker containers. If your use case is time-based, you can run any containerized job on a cron expression with full execution history and log streaming — no broker to manage.
npm install -g @pandastack/cli
# Daily report generation at 6 AM
panda cronjob create \
--name daily-reports \
--image your-registry/report-generator:latest \
--schedule "0 6 * * *"
# Every 15-minute data sync
panda cronjob create \
--name api-sync \
--image your-registry/sync:latest \
--schedule "*/15 * * * *"
# Check execution status
panda cronjob executions daily-reportsThe dashboard at [dashboard.pandastack.io](https://dashboard.pandastack.io) shows the execution history for all your cronjobs, including duration, exit codes, and streaming logs for any run.
Summary
| Cron Jobs | Task Queues | |
|---|---|---|
| Trigger | Time-based schedule | Event-based (user action, webhook) |
| Load pattern | Predictable | Varies with activity |
| Concurrency | One run at a time | Many workers in parallel |
| Infrastructure | Scheduler only | Broker + workers |
| Best for | Periodic, predictable work | Event-driven, variable-load work |
Use cron jobs for scheduled, time-driven work. Use task queues for event-driven, user-triggered work. When in doubt, start with a cron job — it's simpler to operate. Visit [docs.pandastack.io](https://docs.pandastack.io) to learn more about PandaStack's cronjob platform.