How to Deploy with LangGraph Platform: A Complete Walkthrough
If you have built an agent with LangGraph and want to run it as a real service instead of a local script, langgraph platform deploy is the path most teams end up on. LangGraph Platform packages your graph into a versioned API server with persistence, streaming, and a task queue already wired up, so you are not hand-rolling FastAPI routes and a Postgres schema for checkpoints. This guide walks through the whole flow: project layout, local testing with the LangGraph CLI, the langgraph.json config, and the actual deploy step, whether you use the managed cloud offering or a self-hosted target.
What LangGraph Platform actually is
LangGraph the library is a graph-based orchestration framework: you define nodes, edges, and state, and it runs your agent logic with support for cycles, branching, and human-in-the-loop interrupts. LangGraph Platform is the deployment layer built on top of that library. It takes a graph you have already written and wraps it with:
- A REST and streaming API (threads, runs, assistants) so any client can call your agent over HTTP.
- Built-in persistence for checkpoints and thread state, backed by Postgres.
- A task queue for background runs, so long agent loops do not block an HTTP connection.
- Horizontal scaling of workers, since each run is dispatched to a worker process rather than living inside a single request handler.
- Native integration with LangSmith for tracing, so every run you deploy is observable without extra instrumentation.
The key distinction to keep in mind: LangGraph (the library) is what you pip install or npm install and import into your code. LangGraph Platform is the infrastructure that runs that code as a service. You can use LangGraph without ever touching the Platform, and plenty of teams do, running graphs inside their own FastAPI app or a batch job. Platform exists for when you need multi-tenant threads, durable execution across restarts, and a standard API surface that a frontend or another service can call.
Prerequisites
Before deploying anything, you need:
- A working LangGraph graph, defined in Python or JavaScript, that compiles and runs locally.
- The LangGraph CLI installed (
pip install langgraph-clifor Python projects, or the equivalent JS tooling for a TypeScript graph). - A LangSmith account if you are targeting the managed Cloud deployment, since Cloud deploys are tied to a LangSmith organization and API key.
- Docker installed locally if you plan to test the server image or self-host, since both local testing and self-hosted deploys run through a Docker image under the hood.
Start by confirming your graph runs standalone:
python -c "from my_agent.graph import graph; print(graph.get_graph().draw_mermaid())"If that prints a graph structure without errors, your compiled graph object is valid and you are ready to wire it into a Platform project.
Project structure
A LangGraph Platform project needs a predictable layout. A typical Python project looks like this:
my-agent/
langgraph.json
requirements.txt
.env
my_agent/
__init__.py
graph.py
state.py
nodes.pyThe two files that matter most are langgraph.json, which tells the Platform how to find and run your graph, and your dependency manifest (requirements.txt or pyproject.toml for Python, package.json for JS).
graph.py should expose a compiled graph as a module-level variable:
from langgraph.graph import StateGraph, END
from my_agent.state import AgentState
from my_agent.nodes import call_model, call_tool, should_continue
builder = StateGraph(AgentState)
builder.add_node("agent", call_model)
builder.add_node("tools", call_tool)
builder.set_entry_point("agent")
builder.add_conditional_edges("agent", should_continue, {"continue": "tools", "end": END})
builder.add_edge("tools", "agent")
graph = builder.compile()Note that this graph is compiled without a checkpointer argument. LangGraph Platform injects its own Postgres-backed checkpointer at deploy time, so you should not hardcode MemorySaver or a local SQLite checkpointer into the graph you intend to deploy. If you need a checkpointer for local testing outside the Platform, keep that in a separate script or gate it behind an environment check.
The langgraph.json config file
This file is the contract between your code and the Platform. A minimal example:
{
"dependencies": ["."],
"graphs": {
"agent": "./my_agent/graph.py:graph"
},
"env": ".env",
"python_version": "3.11"
}Field by field:
dependencieslists the local paths or package names the Platform should install."."means install the current project directory as a package, which requires apyproject.tomlorsetup.pyat the root.graphsmaps a public name (the name clients will reference when calling the API) to the module path and variable name of your compiled graph. You can register more than one graph in the same deployment, which is useful if you want to expose several agent variants from one deployment.envpoints at a.envfile for local development. In production you set environment variables through your deployment target's secret store instead of shipping a.envfile.python_versionpins the runtime.
For a JavaScript/TypeScript graph, the shape is similar but the graph entry points at a compiled .ts or .js export:
{
"dependencies": ["."],
"graphs": {
"agent": "./src/agent/graph.ts:graph"
},
"env": ".env",
"node_version": "20"
}If your graph needs extra system packages (for example, a PDF library that needs poppler, or an OCR tool), add a dockerfile_lines array to langgraph.json:
{
"dependencies": ["."],
"graphs": {
"agent": "./my_agent/graph.py:graph"
},
"dockerfile_lines": [
"RUN apt-get update && apt-get install -y poppler-utils"
]
}Those lines get inserted into the generated Dockerfile, so you rarely need to write your own Dockerfile by hand for a Platform deployment.
Running locally with the LangGraph CLI
Before deploying anywhere, run the graph through the same server process the Platform uses. This catches config errors, missing environment variables, and serialization issues that only show up once your graph is behind an API rather than called directly in Python.
langgraph devThis starts a lightweight local server, watches for file changes, and gives you a local Studio UI (usually at a smith.langchain.com URL pointing at your local server, or a local URL depending on your CLI version) where you can invoke the graph, inspect state at each step, and replay from a checkpoint. langgraph dev uses an in-memory or lightweight local persistence layer, so it is fast to iterate with but is not representative of production durability.
For something closer to production, run:
langgraph upThis builds the actual Docker image defined by your langgraph.json and starts it alongside a local Postgres container, so checkpointing, threads, and the task queue all behave the way they will once deployed. Use this before your first real deploy, and again any time you change dependencies or the Dockerfile lines, since langgraph dev will not catch a broken system dependency that only fails at Docker build time.
Once the server is running, exercise it with the SDK rather than raw curl, since the SDK handles thread creation and streaming for you:
from langgraph_sdk import get_client
client = get_client(url="http://localhost:8123")
async def main():
thread = await client.threads.create()
async for chunk in client.runs.stream(
thread["thread_id"],
"agent",
input={"messages": [{"role": "user", "content": "hello"}]},
stream_mode="updates",
):
print(chunk.event, chunk.data)If this streams back updates without errors, your graph is ready for the actual deploy step.
Deployment targets
LangGraph Platform offers a few deployment shapes. Pick based on where your data needs to live and who operates the infrastructure.
- Cloud (SaaS): LangChain hosts the deployment for you. You push your repo, LangChain builds and runs the image, and you get a managed URL with autoscaling and managed Postgres handled behind the scenes. This is the fastest path from zero to a running deployment and the default choice unless you have a specific hosting constraint.
- Self-hosted (data plane in your cloud): The control plane (build orchestration, the deployment dashboard) stays with LangChain, but the actual containers and data run inside your own cloud account (AWS, GCP, or Azure), typically on Kubernetes. Pick this when you need your data and compute to stay inside your own VPC for compliance reasons.
- Fully self-hosted / standalone: You run the container image LangGraph Platform produces on your own infrastructure with no dependency on LangChain's control plane. This gives you full control but means you own the build pipeline, the Postgres instance, scaling, and upgrades yourself.
For most teams starting out, Cloud is the right first target because it removes almost all the operational surface area. Move to self-hosted only once you have a concrete requirement (data residency, an existing Kubernetes platform team, or a cost model that favors owning the infrastructure).
Deploying to LangGraph Platform Cloud
The Cloud path is driven from the LangSmith UI, connected to a GitHub repository.
- Push your project, including
langgraph.jsonand your dependency manifest, to a GitHub repo. - In LangSmith, open the Deployments section and choose to create a new deployment from that repo, pointing at the branch you want to deploy.
- Select the
langgraph.jsonfile location if it is not at the repo root, and choose the deployment tier (this controls the compute resources allocated to the deployment). - Add your environment variables and secrets (model provider API keys, database URLs for any external tools your nodes call, and so on) in the deployment's environment settings. These are injected at runtime and never committed to your repo.
- Trigger the build. LangGraph Platform builds the Docker image from your
langgraph.json, provisions a managed Postgres instance for checkpoints, and stands up the API server. - Once the build finishes, you get a deployment URL and an API key scoped to that deployment.
From that point on, every push to the tracked branch can trigger a new revision, and the Deployments UI keeps a history of revisions so you can roll back if a new version misbehaves.
You can also drive this from the CLI if you prefer not to leave the terminal:
langgraph login
langgraph deploy --project my-agentThe CLI flow authenticates against your LangSmith account, packages the project, and kicks off the same build pipeline the UI uses. Use whichever fits your team's workflow; both end up at the same managed deployment.
Deploying self-hosted
For a self-hosted deployment, you build the image yourself and run it wherever you already run containers.
Build the image with the CLI:
langgraph build -t my-agent:latestThis reads langgraph.json, generates the Dockerfile, and produces an image tagged my-agent:latest that contains your graph, dependencies, and the LangGraph server runtime.
Push it to your registry:
docker tag my-agent:latest your-registry.example.com/my-agent:latest
docker push your-registry.example.com/my-agent:latestThen run it with the environment variables it needs, most importantly a DATABASE_URI pointing at a Postgres instance for checkpoints and thread persistence:
docker run -p 8123:8000 \
-e DATABASE_URI="postgresql://user:pass@db-host:5432/langgraph" \
-e OPENAI_API_KEY="$OPENAI_API_KEY" \
-e LANGSMITH_API_KEY="$LANGSMITH_API_KEY" \
your-registry.example.com/my-agent:latestIf you are running on Kubernetes, wrap this into a Deployment manifest with the same environment variables sourced from a Secret, and put a Service and Ingress in front of it. The container exposes a standard HTTP API, so it behaves like any other stateless-looking web service from the orchestrator's point of view, even though the actual state lives in Postgres rather than in the container.
One detail that trips people up: the Postgres instance needs to be reachable and already exist before the container starts, since LangGraph Platform runs its schema migrations on boot. If the database is not reachable, the container will fail health checks and restart in a loop, so check your network policy and connection string carefully before assuming the image itself is broken.
Secrets and environment variables
Never bake API keys into langgraph.json or your graph code. The convention is:
- Locally, use a
.envfile referenced by theenvfield inlanggraph.json, and keep that file out of version control. - On Cloud, set secrets through the Deployments UI's environment variable panel.
- Self-hosted, inject them through your container runtime's secret mechanism (Kubernetes Secrets, Docker secrets, or your cloud provider's secret manager mounted as environment variables).
Your nodes should read these the normal way, through os.environ or your framework's settings loader, not through anything LangGraph-specific:
import os
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4.1", api_key=os.environ["OPENAI_API_KEY"])Testing a deployed graph
Once you have a deployment URL, whether from Cloud or your own self-hosted endpoint, point the SDK at it instead of localhost:
from langgraph_sdk import get_client
client = get_client(url="https://my-agent-abc123.us.langgraph.app", api_key="lsv2_...")
async def main():
thread = await client.threads.create()
run = await client.runs.create(
thread["thread_id"],
"agent",
input={"messages": [{"role": "user", "content": "What is LangGraph Platform?"}]},
)
result = await client.runs.join(thread["thread_id"], run["run_id"])
print(result)For streaming responses back to a frontend, use client.runs.stream with stream_mode="messages" or "updates" depending on whether you want token-level output or state diffs after each node. If you are building a chat UI, "messages" mode gives you incremental tokens that map naturally onto a typing-indicator style interface.
You can also skip the SDK entirely and call the REST API directly, which is useful if your frontend is not in Python or JS:
curl -X POST https://my-agent-abc123.us.langgraph.app/threads \
-H "x-api-key: $LANGGRAPH_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'That returns a thread object with a thread_id you then use to create runs against, the same shape the SDK wraps for you.
Background runs and cron jobs
Not every agent invocation needs to happen inline with a request. LangGraph Platform supports background runs, where you kick off a run and poll or subscribe for the result later instead of holding a connection open:
run = await client.runs.create(
thread["thread_id"],
"agent",
input={"messages": [{"role": "user", "content": "Summarize this week's tickets"}]},
multitask_strategy="reject",
)This is the right pattern for agents that take minutes rather than seconds, since it avoids tying up an HTTP connection or a serverless function's execution limit.
Platform deployments also support scheduled runs (cron-style), configured either through the Deployments UI or the SDK's cron API, which is useful for agents that should run on a fixed cadence, such as a daily digest agent or a periodic data-quality check, without an external scheduler triggering them.
CI/CD considerations
Treat a LangGraph Platform deployment like any other service in your pipeline:
- Run your graph's unit tests (individual node functions, state transitions) before triggering a deploy, the same as you would for any other codebase.
- Use
langgraph upin CI to build the full Docker image and run integration tests against it, catching Dockerfile and dependency issues before they reach a real deployment. - For Cloud deployments tied to GitHub, gate deploys on a passing CI run by only allowing merges to the tracked branch after tests pass, rather than deploying directly from a feature branch.
- For self-hosted deployments, add the
langgraph buildanddocker pushsteps to your existing CI/CD pipeline (GitHub Actions, GitLab CI, or whatever you already use) exactly like you would for any other containerized service.
Monitoring and observability
Every run through a LangGraph Platform deployment is automatically traced in LangSmith if you have set a LANGSMITH_API_KEY in the deployment's environment, with no extra instrumentation code required. This gives you a trace per run showing every node execution, the state at each step, token usage per model call, and latency breakdowns.
For infrastructure-level monitoring (CPU, memory, request rate, error rate), Cloud deployments expose this through the LangSmith Deployments dashboard. Self-hosted deployments rely on whatever monitoring stack you already run against your containers (Prometheus, Datadog, CloudWatch), since the LangGraph server exposes standard health check endpoints your orchestrator can poll.
Common pitfalls
- Compiling the graph with a local checkpointer and shipping it as-is. If your
graph.pycalls.compile(checkpointer=MemorySaver()), that in-memory checkpointer will not survive a worker restart or scale beyond a single process. Let the Platform inject its own checkpointer for deployed graphs. - Missing `dockerfile_lines` for system dependencies. If a node depends on a system package (image processing libraries, headless browser binaries), the build will succeed but the graph will fail at runtime the first time that node executes. Test with
langgraph uplocally, which uses the same Docker build path, to catch this before deploying. - Forgetting to scope secrets per environment. Using the same API keys for a staging and production deployment makes it easy to burn through rate limits or budget on the wrong environment. Set distinct environment variables per deployment.
- Not handling interrupts correctly for human-in-the-loop flows. If your graph uses
interrupt()for a human approval step, make sure your client code checks for the run's status becoming"interrupted"and resumes withclient.runs.create(..., command={"resume": value})rather than assuming every run completes in one shot. - Ignoring thread cleanup. Threads persist indefinitely by default. If your application creates a new thread per user session at high volume, put a retention or archival policy in place, either through periodic cleanup calls against the threads API or through your Postgres instance's own retention rules, so old thread state does not grow unbounded.
FAQ
Do I need LangGraph Platform to use LangGraph in production? No. You can run a compiled LangGraph graph inside your own FastAPI, Flask, or Node server and manage your own persistence. LangGraph Platform is an optional layer that gives you a standard API, managed persistence, and a task queue out of the box, which saves you from building that infrastructure yourself, but it is not a requirement for running LangGraph in production.
What is the difference between `langgraph dev` and `langgraph up`? langgraph dev runs a lightweight local server with hot reload and in-memory or minimal persistence, optimized for fast iteration while you write nodes. langgraph up builds and runs the actual Docker image with a local Postgres container, giving you a much closer approximation of what your deployed environment will behave like, at the cost of a slower startup.
Can I deploy multiple graphs from one project? Yes. The graphs field in langgraph.json accepts multiple entries, each mapping a name to a compiled graph. All graphs in the same langgraph.json are built into the same image and served from the same deployment, so clients pick which graph to invoke by name when creating a run.
How does LangGraph Platform handle long-running or paused agents? Through checkpointing and the interrupt() primitive. Every step of a graph's execution is checkpointed to Postgres, so a run can pause at an interrupt() call, wait indefinitely for external input (a human approval, a webhook callback), and resume exactly where it left off, even if the original worker process is no longer running.
Is my data sent to LangChain if I self-host? With a fully self-hosted deployment, your graph execution and data stay inside infrastructure you control; LangChain's control plane is not in the runtime path for that model. With the hybrid self-hosted option, the control plane (build orchestration and the dashboard) is managed by LangChain while your data plane, meaning the actual containers and Postgres instance, runs in your cloud account. Check the current LangGraph Platform documentation for the exact data flow of each option before committing to one for a compliance-sensitive workload.
What happens if a node throws an exception mid-run? The run transitions to an error state, visible through the run's status field and in the LangSmith trace if tracing is enabled. Because state is checkpointed after each successfully completed node, you can typically resume from the last successful checkpoint after fixing the underlying issue, rather than restarting the entire run from the beginning.
Can I run LangGraph Platform without LangSmith? Cloud deployments require a LangSmith account since the Deployments UI and build pipeline live inside LangSmith. Self-hosted deployments can run without an active LangSmith API key, but you lose automatic tracing, so you will want your own observability in place if you go that route.
BootcampA 30-day guided bootcamp: build, harden and ship a production autonomous agent from scratch.
AI AgentsUnderstand how AI agents really work: the loop, the tools, the memory, and why most agent projects fail.