Compare

self-hosted orchestration vs a packaged platform

LangGraph and Deep Agents are a graph-native agent harness with a packaged deployment platform behind LangSmith. ZenML is self-hosted AI orchestration: versioned artifacts, step caching, approvals, one stack for your own cloud.

LangGraph models an agent as a graph: nodes, edges, and state that persists across steps. Checkpointers add durability, and Deep Agents layers planning and sub-agents on top. Behind it sits a packaged path to production through LangSmith Deployment (formerly LangGraph Platform) and LangSmith tracing, which handle deployment, persistence, and observability for you.

That packaging is the real decision. Adopting the graph is one commitment. Adopting the platform underneath it is a much bigger one, and they arrive together. ZenML sells the second half separately: keep the graph, run it as a step inside a pipeline on your own infrastructure, with artifacts in your own bucket and run metadata in a server you can self-host.

Weighing LangGraph purely as a way to build agents? The ZenML vs LangGraph comparison covers that. This page is about deployment and ownership.

ZenML

Use ZenML if you are

  • Wanting production infrastructure you run yourself, with artifacts and run history in your own account
  • Unwilling to route agent state and traces through a vendor's managed control plane
  • Running more than one agent framework across teams and wanting a single orchestration layer under all of them
  • Targeting Kubernetes, Vertex AI, SageMaker, or AzureML with one pipeline definition
  • Needing versioned artifacts, step caching, and approvals around the agent more than graph semantics inside it

LangGraph & Deep Agents

Use LangGraph & Deep Agents if you are

  • Modeling the agent as an explicit graph with branching, loops, and persisted state
  • Wanting checkpointers, interrupts, and time-travel over graph state as first-class features
  • Building planning and sub-agent patterns where Deep Agents does the heavy lifting
  • Happy to adopt LangSmith Deployment and LangSmith tracing for deployment and observability
LangGraph asks you to adopt a graph and, for production, a platform. ZenML asks you to adopt a pipeline, and leaves the infrastructure yours.

Self-hosted vs packaged

Both get a working agent into production. Here’s the difference: who operates it, and where the data sits.

LangSmith · packaged platformRuntime + infra bundled as a managed product
LangSmith Deployment
runtimesandboxesauth proxytracinghosted infra
Control planehosted by LangChain
One product. Everything inside the box.
ZenML · self-hosted primitiveSingle service, your infra, your data
zenml-serversingle service · Helm-deployable
S3 / GCS / Azure Blobartifacts in your own bucket
service accountsAPI keys you issue and rotate
your cloudAWS · GCP · Azure · Kubernetes
No mandatory hosted control plane in the data path.
  • ZenML: A server you deploy (Helm chart or ZenML Pro), an artifact store that is your own S3, GCS, or Azure Blob bucket, and an orchestrator that is your existing Kubernetes, Vertex AI, SageMaker, or AzureML.
  • LangSmith Deployment: Deployment, persistence, and scaling as a managed product, with hybrid and self-hosted options on the Enterprise plan.
  • The trade: The packaged path is faster to production and has graph-aware features ZenML has no equivalent for. The self-hosted path keeps prompts, retrieved documents, and model outputs in your own account.

Where the sensitive values live

For agent workloads the intermediate values are the sensitive part: prompts, retrieved documents, customer records, model outputs. Where those get written and where generated code runs usually decides a security review.

LangSmith · packaged securityIsolation bundled with the runtime
LangSmith Sandboxesephemeral, locked-down environments
Auth proxysecrets never enter the sandbox
OpenTelemetry tracingnative to the platform
Usable isolation story out of the box.
ZenML · configurable boundaryIsolation is a stack component
@step(runtime="isolated")separate pod / job on your stack
SandboxDocker · Kubernetes · Modal
Secretszenml secret create, secrets= on the step
Tool permissionsstay with the harness
Your sandbox, your secrets, your cloud. Tool policy is the harness's job.
  • Artifacts: ZenML writes every step output to your artifact store. Nothing about a run’s contents leaves your infrastructure when the server is self-hosted.
  • Credentials: Model keys live in zenml secret create and are attached with secrets= on the step. ZenML has no LLM primitive and never brokers a provider call.
  • Sandboxes: A sandbox stack component (Docker, Kubernetes, Modal) gives a step an isolated environment for running generated code, reached with Client().active_stack.sandbox.create_session(). What ZenML doesn’t give you is a per-tool permission model; which tools the model may call stays with the harness.

Versioned runs and deployments

Both let you put a version of an agent behind an endpoint. What differs is what you get back per call.

LangSmith · packaged endpointsProtocol surface bundled with the runtime
MCPA2AAgent ProtocolHITLMemory APIs
Endpoints as part of the packaged runtime.
ZenML · named deployment, versioned snapshotsOne name, updated in place
agent-service
v1v2v3v4
servingsnapshot v4
each requesta tracked run
Invoke via
CLIPython SDKcurl
zenml deployment provision agent-service --snapshot v4
  • Artifacts per run: Every step output is stored, versioned, and loadable by name with get_artifact_version(...).load(), so a change to a prompt is visible as a new artifact version rather than a new log line.
  • Serving: zenml pipeline deploy run.agent_pipeline --name agent-service stands the pipeline up behind an endpoint, and every request is a run with the same artifacts and lineage as a batch job. Deploying a new snapshot to the same name updates it in place.
  • Caching: Unchanged steps are served from cache on every run. The key covers the step, its parameters, and its inputs. That matters most when you’re iterating on the step after the agent.

Control flow, approvals, and recovery

The graph loops, pauses, and rewinds inside the agent. ZenML does the equivalent around the agent, at step granularity.

  • Loops and branches: A @pipeline(dynamic=True) builds its graph at run time from ordinary Python: for loops over an artifact’s contents, if branches on a step’s result, and fan-out with .map(). Each iteration is a real step with its own artifacts.
  • Waiting for a human: wait(schema=bool, question="Approve?") in a dynamic pipeline pauses the run until someone resolves it from the dashboard, the CLI, or the API. Once the wait’s timeout elapses and nothing else is in flight, the run is marked PAUSED so the orchestration process can be torn down, and zenml pipeline runs resume continues it, or ZenML Pro resumes it automatically. Both on_pause and on_resume hooks are available.
  • Recovery: Steps retry with StepRetryConfig, and a failed dynamic pipeline run is retried with zenml pipeline runs retry, reusing the completed steps. ZenML doesn’t resume a step mid-body, and it doesn’t rewind a run to an earlier state the way a checkpointer rewinds a graph.

The gap, named

LangGraph has a set of features ZenML doesn’t attempt, and pretending otherwise helps nobody.

  • Graph semantics inside the agent. Nodes, conditional edges, and cycles over a shared state object are LangGraph’s model. A ZenML dynamic pipeline gives you loops and branches between steps, but it doesn’t model the agent’s own reasoning loop, and it doesn’t try to.
  • Checkpointers and time travel over graph state. A checkpointer lets a graph pause mid-node, be inspected, resumed, and rewound to an earlier state. ZenML’s wait() pauses between steps and its retry resumes from the last completed step. That’s coarser, and step caching isn’t a checkpointer.
  • Deep Agents patterns. Planning, sub-agents, and file-system tooling are harness concerns. ZenML orchestrates whatever you build; it doesn’t help you build it.
  • The packaged path. LangSmith Deployment gives you deployment, persistence, and observability as one product. ZenML gives you the pieces and your own cloud.

What ZenML gives you in exchange is narrower: artifacts you own, caching across runs, approvals that outlive the process, sandboxes for generated code, and one orchestration layer that works the same whether the team picked LangGraph, CrewAI, Pydantic AI, or the OpenAI SDK.

To replay a LangGraph or Deep Agents agent against a prompt or model change, see Kitaru vs LangGraph & Deep Agents.

What makes ZenML different

FeatureZenMLLangGraph & Deep AgentsWhat that means
Artifacts and run data stay in your own infrastructureYesPartial supportZenML writes to your bucket by default. LangSmith Deployment self-hosting is an Enterprise-plan option.
Versioned artifacts and lineage across runsYesNot supportedStep outputs are stored and loadable by name; checkpointers persist graph state, not artifacts.
Caching that skips unchanged steps on a re-runYesNot supportedKeyed on the step, its parameters, and its inputs.
Loops and branches decided at run timeYesYesZenML: dynamic pipelines, between steps. LangGraph: edges and cycles, inside the graph.
Pause for a human and resume laterYesYesZenML: `wait()` between steps. LangGraph: interrupts inside a node, backed by a checkpointer.
Isolated environment for generated codeYesPartial supportZenML: the sandbox stack component. LangGraph: LangSmith Sandboxes, part of the LangSmith platform.
One stack abstraction for Kubernetes, Vertex AI, SageMaker, AzureMLYesNot supportedConfigure once, every pipeline uses it.
Works with any agent framework, not just oneYesNot supportedA step is ordinary Python; LangGraph is the harness itself.
Graph semantics inside the agent: conditional edges, cycles over stateNot supportedYesZenML loops between steps, not inside the agent's reasoning.
Time travel and mid-node resume over graph stateNot supportedYesCheckpointers are LangGraph's. ZenML retries at step granularity.
Planning and sub-agent patterns (Deep Agents)Not supportedYesHarness concerns. ZenML doesn't define agents, it runs them.
Open source, self-hostableYesPartial supportLangGraph is open source; LangSmith Deployment self-hosting sits on the Enterprise plan.

How the two surfaces map

ConceptLangGraph & Deep AgentsZenML
Unit of workGraph node@step (the graph runs inside it)
BoundaryStateGraph@pipeline
Control flowEdges, conditional edges, cyclesPython control flow in @pipeline(dynamic=True)
StatePersisted graph state via checkpointersVersioned artifacts in your bucket
Pause and resumeInterrupts, time travelwait() and zenml pipeline runs resume
IsolationLangSmith Sandboxessandbox stack component
Avoiding repeat workCheckpointer resumeenable_cache across runs
Where it runsYour process, or LangSmith DeploymentStack orchestrator (zenml stack set)
ServingLangSmith Deploymentzenml pipeline deploy --name ...

Code comparison

ZenML (wrapping the graph)
from typing import Annotated

from zenml import pipeline, step, wait
from zenml.client import Client

from my_agent import build_graph   # your existing StateGraph

graph = build_graph()

@step
def gather_context(topic: str) -> Annotated[str, "context"]:
  return retrieve_documents(topic)

@step(enable_cache=False)
def run_graph(context: str, topic: str) -> Annotated[str, "draft"]:
  # The graph is untouched: branching, cycles and state all
  # stay LangGraph's. ZenML records what came out.
  result = graph.invoke({"context": context, "topic": topic})
  return result["draft"]

@step
def publish(draft: str) -> None:
  push_to_cms(draft)

@pipeline(dynamic=True)
def agent_pipeline(topic: str):
  draft = run_graph(gather_context(topic), topic)
  # The run pauses here until a human answers from the
  # dashboard, the CLI, or the API. After the wait's timeout
  # the run is PAUSED and the process can be torn down.
  approved = wait(schema=bool, question="Publish this draft?")
  if approved:
      publish(draft)

agent_pipeline("AI orchestration")

# Artifacts are in your bucket, versioned, loadable by name:
Client().get_artifact_version("draft").load()

# Your cloud, no platform:
#   zenml stack set k8s_stack && python agent_pipeline.py
LangGraph alone
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver

builder = StateGraph(AgentState)
builder.add_node("research", research_node)
builder.add_node("draft", draft_node)
builder.add_conditional_edges(
  "draft", needs_more_research, {"yes": "research", "no": END}
)
builder.set_entry_point("research")

# Checkpointers persist graph state so a run can be paused,
# inspected, resumed, and rewound. Swap MemorySaver for a
# Postgres saver in production.
graph = builder.compile(checkpointer=MemorySaver())

result = graph.invoke(
  {"topic": "AI orchestration"},
  config={"configurable": {"thread_id": "run-1"}},
)

# Cycles, interrupts and time travel over state are all here.
# Getting to production usually means LangSmith Deployment and
# tracing, which is a larger adoption than the graph itself.

Pick the orchestration layer
without picking the platform

If the graph is the right model for your agent, keep it: cycles over state, mid-node interrupts, and time travel are things ZenML doesn’t do inside the agent. What you don’t have to accept along with it is the packaged platform. Run the graph inside a ZenML pipeline and the infrastructure stays yours: artifacts in your own bucket, approvals and sandboxes around the step, and the same orchestration layer under whatever the next team picks.