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: connectionString

Recipe: 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