The use cases we have in mind include agent loops, ETL/data pipelines, billing flows, and other long-running jobs. The model is simple: mark an existing function as a Task, deploy the repo as a Workflow, then trigger runs from your application code or via our API.
Each task can run continuously for up to 24 hours and set its own retry policy, timeout, and compute instance type. Tasks can call other tasks like normal functions, and fan out in parallel with `Promise.all` or `asyncio.gather`. Each task run gets its own isolated container, and Render handles the queuing, orchestration, and observability.
You can inspect logs, traces, and metrics for each run in the Render dashboard. If you're running other services on Render, you can connect to them from your tasks using built-in private networking. Billing is based on task instance type and run duration, prorated to the second.
This is a public beta, so rough edges are possible. Today, task definitions are TypeScript and Python only, and built-in cron schedules aren't available yet. You can still trigger tasks from the SDK/API, or use a Render Cron Job for scheduled entry points. We're also working on pause/resume, checkpointing, vertical autoscaling, and support for more languages.
We're not claiming this replaces every task orchestration system (yet). The goal is to make the common cases much simpler to use and troubleshoot, especially for Render users.
Docs and examples: https://render.com/docs/workflows
I'd especially love feedback from folks who have built similar systems with Celery, BullMQ, Temporal, or other queues and workers. What would you want to see before GA?
[1] ---- TS example ----
import { task } from '@renderinc/sdk/workflows'
import { Render } from '@renderinc/sdk'
const chargeCard = task(
{
name: 'chargeCard',
retry: { maxRetries: 5, waitDurationMs: 1000, backoffScaling: 2 },
timeoutSeconds: 300,
plan: 'standard',
},
async function chargeCard(invoiceId: string) {
// call Stripe or your payment provider here
return { invoiceId, chargeId: `ch_${invoiceId}` }
}
)
const writeLedger = task(
{ name: 'writeLedger' },
async function writeLedger(invoiceId: string, chargeId: string) {
// persist accounting entry
}
)
const sendReceipt = task(
{ name: 'sendReceipt' },
async function sendReceipt(invoiceId: string, chargeId: string) {
// email customer
}
)
const processInvoice = task(
{ name: 'processInvoice' },
async function processInvoice(invoiceId: string) {
const charge = await chargeCard(invoiceId)
await Promise.all([
writeLedger(invoiceId, charge.chargeId),
sendReceipt(invoiceId, charge.chargeId),
])
return { chargeId: charge.chargeId }
}
)
// from your app:
const render = new Render()
const startedRun = await render.workflows.startTask(
'billing/processInvoice',
['inv_123']
)
const finishedRun = await startedRun.get()
console.log(finishedRun.results)