Not everything fits in a function that wakes up per request
Serverless is a wonderful default for request and response work. It is a poor home for anything that needs to stay awake: a worker draining a queue, a scheduled job that runs at a fixed time, a process holding a long lived connection. Forcing that work into a scale to zero function means rediscovering that it was asleep when it mattered.
What an always on host is for
Render keeps a process running. That makes it the right place for three kinds of work that the request driven hosts handle badly:
- Background workers that pull from a queue and process jobs continuously.
- Cron jobs that must run on a schedule whether or not a user is around to trigger them.
- Services that maintain a persistent connection, for example a websocket server or a database listener.
A scheduled job, defined as infrastructure
The schedule lives in configuration next to the service, so it is reviewed and versioned like everything else rather than configured by hand in a dashboard nobody remembers editing.
# render.yaml
services:
- type: cron
name: nightly-reconciliation
schedule: "0 2 * * *"
buildCommand: npm ci && npm run build
startCommand: node dist/jobs/reconcile.js
Splitting the workload across hosts on purpose
The web app can live on a git native application host with per branch previews, while the worker and the cron live on the always on host, and both read the same database and the same secrets. This is the host portfolio idea in practice: each workload sits where its shape fits, and the seams between them are just configuration.
The rule of thumb
If the work is "respond to this request", reach for a function. If the work is "keep doing this", reach for an always on service. Putting each on the host built for it removes a whole class of "why did the job not run" incidents before they happen.
