Back to Blog
Tutorial8 min read2026-07-17

Deploy a CrewAI Multi-Agent API to Production (2026)

CrewAI orchestrates multiple AI agents into a 'crew' that collaborates on a task. Here's how to wrap a crew in a FastAPI endpoint and deploy it on PandaStack — with the async pattern that keeps it from timing out.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Multi-agent frameworks went from research toys to production tools fast, and CrewAI (https://docs.crewai.com) is one of the most approachable: you define agents with roles and goals, give them tasks, and the crew collaborates to produce a result. Writing the crew is the fun part. Deploying it — so a real app can call it over HTTP without timing out on a five-minute agent run — is the part that trips people up. I run PandaStack; here's the deployment pattern that actually holds up, including the async detail most tutorials skip.

A minimal crew

# crew.py
from crewai import Agent, Task, Crew

researcher = Agent(
    role="Researcher",
    goal="Find accurate, current facts about the topic",
    backstory="A meticulous analyst who never guesses.",
    verbose=False,
)
writer = Agent(
    role="Writer",
    goal="Turn research into a clear, concise summary",
    backstory="A former editor who hates filler.",
    verbose=False,
)

def run_crew(topic: str) -> str:
    research = Task(description=f"Research: {topic}", agent=researcher, expected_output="Bullet-point findings")
    summary = Task(description="Write a 150-word summary from the findings", agent=writer, expected_output="A tight summary")
    crew = Crew(agents=[researcher, writer], tasks=[research, summary])
    return str(crew.kickoff())

CrewAI needs an LLM. Point it at your provider via env vars (an OPENAI_API_KEY, or better, your self-hosted LiteLLM proxy so you control cost and keys).

The deployment problem: crews are slow

A crew run can take seconds to minutes — multiple LLM calls, tool use, back-and-forth. If you expose it as a *synchronous* HTTP endpoint, the request hangs, clients time out, and you can't tell a slow run from a stuck one. The fix is a job pattern: accept the request, return a job ID immediately, run the crew in the background, and let the client poll for the result.

Step 1: FastAPI with a background job

# main.py
import os, uuid
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
from crew import run_crew

app = FastAPI()
JOBS: dict[str, dict] = {}  # swap for Redis/Postgres in real use

class Req(BaseModel):
    topic: str

def _run(job_id: str, topic: str):
    try:
        JOBS[job_id] = {"status": "done", "result": run_crew(topic)}
    except Exception as e:
        JOBS[job_id] = {"status": "error", "error": str(e)}

@app.post("/crew")
def start(req: Req, bg: BackgroundTasks):
    job_id = str(uuid.uuid4())
    JOBS[job_id] = {"status": "running"}
    bg.add_task(_run, job_id, req.topic)
    return {"job_id": job_id}

@app.get("/crew/{job_id}")
def status(job_id: str):
    return JOBS.get(job_id, {"status": "unknown"})

@app.get("/health")
def health():
    return {"ok": True}

The client POSTs a topic, gets a job_id instantly, then GETs the status until it's done. No hanging requests, no timeouts. For anything beyond a demo, back JOBS with managed Redis or Postgres so state survives restarts and scales across replicas.

Step 2: requirements and Dockerfile

crewai
fastapi
uvicorn[standard]
pydantic
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Step 3: Deploy on PandaStack

  1. 1Push to Git.
  2. 2https://dashboard.pandastack.io → New App → connect repo. It detects the Dockerfile and deploys.
  3. 3Set environment variables: your LLM key (OPENAI_API_KEY) or your LiteLLM proxy base URL. Encrypted, never in the repo.
  4. 4Want durable job state? Databases → New Database → Redis (or PostgreSQL), attach it, and replace the in-memory JOBS dict with reads/writes against it — DATABASE_URL/REDIS_URL are available as env vars.
  5. 5Point your health check at /health and give it a generous timeout, since a busy instance running a crew shouldn't be killed mid-run.

CLI:

npm install -g @pandastack/cli
panda login
panda deploy

Step 4: Guardrails you'll want in production

  • Cap concurrency. Each crew run fans out into several LLM calls; unbounded concurrency will nuke your provider rate limits and your bill. Limit how many jobs run at once.
  • Set a per-job timeout. An agent stuck in a loop should fail, not run forever. Enforce a max duration in _run.
  • Route LLM calls through LiteLLM. It gives you per-key budgets and fallbacks — invaluable when agents make unpredictable numbers of calls. (See the companion post on deploying a LiteLLM proxy.)

Honest tradeoffs

  • Agent frameworks are non-deterministic and can be expensive. Costs vary run to run; budget and monitor. This is a CrewAI reality, not a hosting one.
  • The background-job pattern adds moving parts (job store, polling) versus a simple sync endpoint — but a sync endpoint that times out isn't production, so it's necessary complexity.
  • Free-tier apps scale to zero and cold-start; for an interactive agent API, keep it warm on a paid tier.

Wrap-up

CrewAI writes the multi-agent logic; the job-pattern FastAPI wrapper makes it survive contact with real HTTP clients. Deploy it on PandaStack, back the job state with managed Redis/Postgres, route LLM calls through LiteLLM, and cap concurrency and timeouts. That's a crew that ships.

Docs: https://docs.pandastack.io. Start free: https://dashboard.pandastack.io.

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also