For a managed replacement with an explicit background-worker service, start with Render. Choose Railway when usage-based resources and connected services matter. Choose a Cloud Run worker pool when the workload already belongs in Google Cloud. Choose Spocket for one small Node or Python process with fixed per-app pricing. Choose a VPS or self-hosted platform when root access matters.
Heroku is still supported and production-ready. In February 2026 it moved to a sustaining engineering model focused on security, stability, reliability, and support. That is a reason to review future fit, not a reason to rush a healthy worker through an unsafe migration.
Shortlist by worker shape
| ALTERNATIVE | CHOOSE IT WHEN | DO NOT OVERLOOK |
|---|---|---|
| Render background worker | You want a named continuous worker and managed data services nearby | Worker, queue, database, and web service can be separate billable resources |
| Railway service | You want broad service primitives and resource-based billing | Estimate steady RAM and CPU because continuous workers consume them continuously |
| Cloud Run worker pool | You use Google Cloud and need continuous pull-based work | Worker pools do not autoscale by themselves |
| Spocket worker | You have a small Node or Python consumer and prefer fixed capacity | No managed queue, private network, root access, or horizontal replicas |
| VPS or self-hosted PaaS | You need system packages, several processes, or custom networking | Patching, monitoring, backups, and failover become your work |
The lowest entry price is not automatically the lowest worker cost. A queue consumer spends most of the month waiting, so the useful comparison is the cost of its required memory and one continuously active instance, plus the queue and database it depends on.
Inventory the Heroku contract before moving code
A worker line in a Procfile hides several contracts. Write them down before translating it into another host's start command.
| CONTRACT | QUESTION TO ANSWER |
|---|---|
| Start command | Which file and arguments actually launch the consumer? |
| Queue | Where is the durable queue, and can the new host reach it? |
| Release behavior | Does a deploy interrupt a job, and can that job be retried safely? |
| Concurrency | Does one process handle one job or several at once? |
| State | What is held in memory or local disk that must move elsewhere? |
| Signals | How does the worker handle termination before the host stops it? |
A safe migration keeps both queues from racing
- 01Deploy the new worker pausedInstall dependencies and confirm the start command without letting it claim production jobs.
- 02Copy configuration deliberatelyMove queue URLs, database URLs, API keys, and concurrency settings through the new host's secret store.
- 03Send one test jobUse a separate queue or a test routing key. Confirm success, failure, retry, and log visibility.
- 04Drain the Heroku workerStop new claims, let in-flight work finish, and only then start the new production consumer.
- 05Watch queue age, not just process stateA running process can still be stuck. Backlog depth and the age of the oldest job show whether work is moving.
- 06Keep rollback simpleDo not delete the Heroku app until the new worker has handled a normal workload window.
Retries decide whether the cutover is safe
Assume a worker can stop between performing a side effect and acknowledging the job. Make handlers idempotent, put a unique key on external changes, and cap retries. Failed work should remain inspectable instead of disappearing or retrying forever.
Where a small fixed worker fits
Spocket can replace a single Heroku worker when it is a Node 22 or Python 3.11 process that connects outward to a queue you already operate. It restarts a failed process, keeps deploy versions for rollback, and exposes logs. It does not supply Redis, a database, worker autoscaling, or a private service network.
The Solo plan is $3 per month for one 512MB app. Larger browser workers belong on Builder or Fleet, and a horizontally scaled queue consumer belongs on a platform built for replicas. Compare the workload on /run/workers, then use /blog/what-is-a-background-worker to review queue behavior before cutting over.