You built a LangGraph agent. It runs locally. You've got nodes, edges, conditional routing, state that flows through the graph. Maybe it's a research assistant that searches the web and writes reports. Maybe it's a multi-step tool-calling agent with loops. Whatever it does, it works on your machine.
Now you want to put it somewhere other people can use it. Or somewhere your backend can call it. And this is where LangGraph gets interesting, because it's not a simple stateless function you can throw behind a Lambda.
LangGraph adds real complexity that plain LangChain chains don't have: state management, cycles, conditional edges, long-running executions with multiple tool calls. That complexity matters when you try to run it in production.
The state problem
LangGraph graphs are stateful by design. Your StateGraph defines a typed state object, and every node reads from and writes to that state as execution flows through the graph. Nodes accumulate results, branch based on previous outputs, loop back when conditions aren't met.
Here's a basic example of what that looks like:
from langgraph.graph import StateGraph, END
from typing import TypedDict
class AgentState(TypedDict):
messages: list
next_step: str
graph = StateGraph(AgentState)
graph.add_node("research", research_node)
graph.add_node("analyze", analyze_node)
graph.add_conditional_edges("research", should_continue)
Locally, this all lives in memory. State gets created, passed around, updated. When the graph finishes, you read the final state and move on. Simple.
In production, that in-memory model breaks. You need state to survive process restarts. You need it isolated between concurrent runs so one user's execution doesn't bleed into another's. If you're running workers across multiple machines, state can't just sit in a local variable. And you need to be able to inspect the state of a running or failed graph to understand what happened.
You still need an API
Same problem as any agent framework: graph.invoke() works fine in a script, but nobody can call a Python script running on your laptop. You need an HTTP API in front of it.
from fastapi import FastAPI, BackgroundTasks
from uuid import uuid4
app = FastAPI()
@app.post("/run")
async def start_run(inputs: dict, background_tasks: BackgroundTasks):
run_id = str(uuid4())
store[run_id] = {"status": "running"}
background_tasks.add_task(execute_graph, run_id, inputs)
return {"run_id": run_id}
The catch with LangGraph is that runs tend to be long. A graph with conditional loops, multiple tool calls, and multi-step reasoning can easily run for several minutes. That's well past the default timeout on most reverse proxies and serverless platforms. So you can't just wait for the result inside the request handler. You need async execution, a way to store results, and a way for clients to check back or get notified when it's done.
You also want streaming. One of LangGraph's strengths is that you can stream events as nodes execute: which node just ran, what it produced, what the state looks like at each step. Losing that in production means losing one of the main benefits of using LangGraph in the first place. So now you need SSE or WebSocket support in your API too.
Containerization
LangGraph pulls in langgraph, langchain-core, and depending on what tools and models you're using, potentially langchain-community, langchain-openai, langchain-anthropic, and a handful of other packages. If your agents use custom tools, add those dependencies to the pile.
Dependency resolution in the LangChain ecosystem can be painful. Version conflicts between langchain-core and community packages are common. Pinning versions in a requirements.txt or pyproject.toml helps, but you'll still spend time debugging import errors that only show up in the container and not on your machine.
Docker solves the environment consistency problem, but now you're maintaining Dockerfiles, dealing with image builds, and pushing to a registry. If your graph uses any tools that need system-level dependencies (browsers, ffmpeg, etc.), the Dockerfile gets more complex.
Tracing graph execution
This is where LangGraph really differs from other frameworks. When a CrewAI crew fails, you have a sequence of agent actions to trace through. When a LangGraph graph fails, you have a directed graph where node A called node B which conditionally routed back to node A, which then called node C.
Without proper tracing, debugging a failed graph run is rough. Which node threw the error? What was the state when it happened? Did a conditional edge route to the wrong node? Was the state corrupted by a previous node? Did a cycle run more times than expected?
You could pipe everything to stdout and read logs, but that gets unreadable fast with complex graphs. LangSmith exists for this, but it's a separate hosted service with its own pricing and setup. And even with LangSmith, you still need to wire up the integration and make sure traces are actually being captured in your production environment.
The same infrastructure problems
Everything else from the CrewAI deployment guide applies here too. Scaling workers, job queues, versioning deployments, authentication, rate limiting, secret management. These are the same problems regardless of whether you're running CrewAI or LangGraph.
I won't rehash all of that here. The short version: you end up building a container orchestration system, a job queue, a versioning pipeline, an auth layer, and an observability stack. None of it is your actual product.
Deploy with Crewship
Crewship now supports LangGraph natively. If your project has a langgraph.json file, Crewship auto-detects it and handles everything from there.
Here's the full deployment flow.
Install the CLI
curl -fsSL https://www.crewship.dev/install.sh | bash
Log in
crewship login
Set up your project
crewship init
Crewship detects LangGraph from your langgraph.json and generates a crewship.toml:
[deployment]
framework = "langgraph"
entrypoint = "src.my_graph.graph:graph"
python = "3.11"
profile = "slim"
The entrypoint points to your compiled StateGraph object. Crewship uses this to invoke your graph without you needing to write any API code.
Deploy
crewship deploy
Your code gets packaged, built into a container, and deployed. You get a deployment URL and a link to the Crewship console where you can manage it.
Add your secrets
crewship env set OPENAI_API_KEY=sk-... TAVILY_API_KEY=...
Or import from your .env:
crewship env import -f .env
Secrets are encrypted and injected at runtime. Nothing gets baked into the container image.
Run it
From the CLI:
crewship invoke --input '{"messages": [{"role": "user", "content": "Research the latest AI papers"}]}'
The CLI streams events as nodes execute, so you can watch your graph work through its steps in real-time.
From the REST API:
curl -X POST https://api.crewship.dev/v1/runs \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"deployment": "my-graph", "input": {"messages": [{"role": "user", "content": "Research the latest AI papers"}]}}'
Execution traces in Crewship map to your graph nodes. You can see which node ran, what state it received, what it produced, how long each step took. When a node fails or a conditional edge routes somewhere unexpected, you'll see where and why.
LangGraph.js support
If you're building with LangGraph.js (the TypeScript/JavaScript version), Crewship supports that too. The deployment experience is the same: same CLI workflow, same API, same execution traces. Check out the LangGraph.js page for details.
What you get out of the box
All of that infrastructure from steps 1 through 6 that you'd otherwise build yourself:
- Isolated execution — every run gets its own environment, no interference between runs
- Auto-scaling — scales up when there's work, scales to zero when there isn't
-
Deployment versioning — each
crewship deploycreates a new version, roll back to any previous one - Graph-aware execution traces — see which nodes ran, state at each step, timing, token usage
- Webhooks — trigger runs from CI/CD, cron jobs, or Zapier; get notified on completion
- Token auth — API key authentication, generate and rotate keys from the console
- Real-time SSE streaming — watch graph execution live, or poll for the result
Get started
You can deploy your first LangGraph graph on Crewship in a few commands. No credit card required for the free tier.
If you're already using Crewship for CrewAI, the same account and CLI work for LangGraph. Just crewship init in your LangGraph project and deploy.
Questions about deploying LangGraph? Check the docs or reach out at mail@crewship.dev.
Top comments (0)