Every AI application eventually needs work that happens outside the HTTP request cycle — document ingestion, embedding generation, email sending, report compilation. Render Background Workers run the same code as your web service but without a public URL, on a persistent process that you control.
Background Worker vs Cron Job on Render
| Background Worker | Cron Job | |
|---|---|---|
| Process model | Always running, pulls from a queue | Spawned on schedule, terminates after |
| Best for | Queue-based processing, event-driven work | Scheduled reports, daily cleanup |
| Trigger | Responds to queue messages | Time-based (cron syntax) |
| Cost | Continuous instance-hours | Only billed during execution |
Recipe: Document Embedding Worker
When a user uploads a document, the API enqueues an embedding job. The worker processes it asynchronously — users get an immediate response and the embedding happens in the background.
# worker.py — Render Background Worker
import os, json, time
import redis
from openai import OpenAI
import psycopg2
r = redis.from_url(os.environ['REDIS_URL'])
client = OpenAI()
conn = psycopg2.connect(os.environ['DATABASE_URL'])
def embed_document(doc_id: str, content: str):
response = client.embeddings.create(
model='text-embedding-3-small',
input=content
)
embedding = response.data[0].embedding
with conn.cursor() as cur:
cur.execute(
'UPDATE documents SET embedding = %s WHERE id = %s',
(json.dumps(embedding), doc_id)
)
conn.commit()
print(f'Embedded document {doc_id}')
def main():
print('Embedding worker started')
while True:
job = r.brpop('embed_jobs', timeout=5)
if job:
data = json.loads(job[1])
embed_document(data['doc_id'], data['content'])
if __name__ == '__main__':
main()# api.py — Web Service enqueues jobs
import redis, json
r = redis.from_url(os.environ['REDIS_URL'])
@app.post('/documents')
async def upload_document(content: str):
doc_id = save_to_db(content)
r.lpush('embed_jobs', json.dumps({'doc_id': doc_id, 'content': content}))
return {'id': doc_id, 'status': 'processing'}Setting Up the Background Worker on Render
# render.yaml
services:
- type: web
name: api
runtime: python
startCommand: uvicorn api:app --host 0.0.0.0 --port $PORT
- type: worker
name: embed-worker
runtime: python
startCommand: python worker.py
envVars:
- key: REDIS_URL
fromService:
name: redis
type: redis
property: connectionStringRecipe: Cron Job for Scheduled Reports
# report.py — runs on schedule, terminates after
import os
from datetime import datetime
from db import get_usage_stats
from email_client import send_report
def main():
stats = get_usage_stats(period='last_7_days')
send_report(
to='team@yourcompany.com',
subject=f'Weekly AI Usage Report — {datetime.now():%Y-%m-%d}',
data=stats
)
print('Report sent.')
if __name__ == '__main__':
main()# render.yaml — cron job
- type: cron
name: weekly-report
runtime: python
schedule: '0 9 * * 1' # 9 AM UTC every Monday
startCommand: python report.py| Metadata | Value |
|---|---|
| Title | Render Background Workers: Running AI Jobs Without Blocking Your API |
| Tool | Render |
| Primary SEO keyword | render background worker |
| Secondary keywords | render worker service, render cron job, render queue processing, render async jobs |
| Estimated read time | 7 minutes |
| Research date | 2026-04-14 |