Migrating production infrastructure? Get up to ₹10,000 in migration credits. Apply now
voroaDocs
Compute

Host cron jobs and background workers: which one you need

How to tell whether your code belongs in a Voroa background worker or a cron job, why a program that finishes ends up restarting forever, and how to move it.

Both a background worker and a cron job run code with no web address. The difference is not what they run, it is when they are expected to stop.

  • A background worker is expected to keep running. It listens on a queue, holds a connection, polls something in a loop. It should still be running tomorrow.
  • A cron job is expected to finish. It runs, does its work, ends, and waits for the next scheduled time. Ending is the normal outcome, not a fault.

If you get this the wrong way round, nothing crashes and nothing tells you off. Your program simply starts over, again and again.

The symptom: a worker that starts from the beginning every few minutes

A background worker is meant to stay running, so Voroa starts it again whenever it stops. That is deliberate: it is what brings your worker back after a bad night, a lost connection, or the machine underneath it restarting.

But if your program was written to finish - process a list, send a batch, run a report, then end - Voroa sees it stop and starts it again immediately. Your logs then read like this, over and over:

Starting run
Progress: 25/838
Progress: 838/838
Done
Starting run
Progress: 25/838

Nothing has failed. The work completed. It is just being asked to do it again the moment it finishes, which is almost certainly not what you wanted - and it means your service is using its full instance, without pause, all day.

The other symptom: it was created as a web service

The same code deployed as a Web Service fails differently, and the failure looks like a platform problem rather than a mismatch. A web service is expected to answer on a port, so Voroa waits for one. A bot or a queue consumer never opens a port - it makes outbound calls only - so nothing ever answers, the deploy is failed at the end of the startup window, and the container is removed. The work itself may have been running perfectly the whole time.

The deploy log names this case:

We did not see your service listening on any port while we waited.

If that is your program, it is a background worker, not a web service. Create it as a Worker - the sections below cover choosing between a worker and a cron job. See also deploy statuses for the other failure messages.

Which one do I need?

Ask one question: is your program supposed to end?

Your program Use
Waits for jobs on a queue and processes them as they arrive Background worker
Holds a websocket, a subscription, or a long-lived connection Background worker
Loops forever with a sleep inside it Background worker
Runs a report every night and ends Cron job
Sends a daily digest email and ends Cron job
Backfills or cleans up data on a schedule and ends Cron job
Scrapes or refreshes data every hour and ends Cron job

If your main finishes on its own, you want a cron job.

Moving a worker to a cron job

Nothing is deleted or migrated automatically, and your existing service keeps running while you do this.

  1. Choose New, then Cron Job.
  2. Pick the same repository and branch the worker uses.
  3. Enter the same command you gave the worker.
  4. Choose a frequent schedule to start with - Every 15 minutes - so you can watch the first run instead of waiting for tonight.
  5. Copy your environment variables across, or attach the same environment group.
  6. Wait for a run, then confirm it did what you expected by checking its effect - the rows it wrote, the file it produced, the message it sent. Voroa does not show per-run output for a scheduled command, so the result is what tells you it worked, not a log.
  7. Now set the schedule you actually want - one of the buttons under the field, such as Every hour or Every day (midnight), or type a cron expression like 0 2 * * * for 2:00 AM every day.
  8. When you are happy, delete the old worker, so you are not paying for both.

For how schedules are written and what a cron job costs, see Cron jobs and scheduled tasks.

If it really should keep running

Sometimes the service is right and the program is wrong: it was meant to keep going and it is ending by accident. Common causes:

  • The program does its setup, starts nothing that waits, and reaches the end of the file.
  • The loop that was supposed to run forever exits after the first pass.
  • Your queue library is started but never awaited, so the process has nothing left to do.

In that case, keep the background worker and fix the program so it stays running - for example by awaiting the consumer, or keeping the event loop alive.

What Voroa does about it

Voroa watches for services that stop and start again without end, and writes to the account owner. It tells the two cases apart:

  • If your program is failing - ending with an error - you are told your service keeps restarting, and after a day of no change the service is stopped so it is not burning an instance for nothing (one hour if we already stopped it in the last 7 days; 72 hours if our email cannot be sent to you). A program that ends cleanly is treated as finished work only up to 200 restarts; past that it counts as failing. You can start it again yourself at any time; nothing is deleted.
  • If your program is finishing normally, that is not a fault, so the service is never stopped for it. You get a single message pointing you at this page.

The two are sent at different rates, because they mean different things:

  • The cron job message above is sent once. Nothing is broken, so repeating it would only be noise.
  • The keeps restarting message repeats about once a day while the service is still restarting, and stops as soon as it stays up. If it is stopped, you are told that once.
Last updated November 12, 2026