---
title: How to Run a LangGraph Agent on DigitalOcean Harness Runtime (public)
description: Package a LangGraph repository, configure model credentials, and deploy your graph on Harness Runtime.
product: Managed Agents
url: https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/how-to/run-langgraph-agent/
last_updated: "2026-10-13"
---

> **For AI agents:** The documentation index is at [https://docs.digitalocean.com/llms.txt](https://docs.digitalocean.com/llms.txt). Markdown versions of pages use the same URL with `index.html.md` in place of the HTML page (for example, append `index.html.md` to the directory path instead of opening the HTML document).

# How to Run a LangGraph Agent on DigitalOcean Harness Runtime (public)

DigitalOcean Harness Runtime provides managed, hardware-isolated microVM sandboxes with built-in tools such as Chromium to run harnesses and execute arbitrary code. Rich lifecycle APIs preserve conversational history and working state across sessions, letting you pause, resume, and fork work to control costs and adapt to the nonlinear nature of agentic workflows. Scale complete agents such as Claude Code or use sandboxes independently for code execution, all through the same service. See [What You Can Build](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/details/what-you-can-build/index.html.md) for example use cases.

Managed Agents runs your LangGraph application in an isolated sandbox. Unlike coding adapters, the LangGraph runtime runs your graph, rather than a general-purpose coding agent. At session start, we clone your repository at a pinned commit, install your Python dependencies, and start the LangGraph Agent Server (`langgraph dev`) against your graph.

Your application stays a standard LangGraph app. You do not need platform-specific imports or an SDK.

To run a LangGraph agent, you need a GitHub repository that contains your LangGraph app, with `langgraph.json` at the repository root (or in a subdirectory you point to) and the exact commit SHA you want to run.

## Set Up the Repository

A deployable LangGraph app needs a compiled graph, a `langgraph.json` manifest, and a dependency file. We support both of the following layouts:

### requirements.txt

```text
my-app/
|-- my_agent/
| |-- __init__.py
|   `-- agent.py
|-- requirements.txt
`-- langgraph.json
```

### pyproject.toml

```text
my-app/
|-- my_agent/
| |-- __init__.py
|   `-- agent.py
|-- pyproject.toml
`-- langgraph.json
```

`langgraph.json` must be at the repository root, or in the directory you set with `FRAMEWORK_SUBDIR` for monorepos.

### Create the langgraph.json Manifest

Create `langgraph.json` at the root of your repository. This file declares your dependencies and the compiled graph the Agent Server should load. The session fails to start if the file is missing.

```json
{
  "dependencies": ["langchain_openai", "."],
  "graphs": {
    "my_agent": "./my_agent/agent.py:agent"
  }
}
```

The manifest supports the following fields:

- `dependencies` (required): Packages and local paths your graph needs. Use `"."` or `"./your_package"` to include your repository code.
- `graphs` (required): Maps a graph name to a compiled graph as `"./path/to/file.py:variable_name"`. The variable must be the object returned by `.compile()`.
- `env` (optional): Path to a local `.env` file for local development. Use environment variables in the environment spec instead.
- `python_version` (optional): Python version your graph expects.
- `pip_config_file` (optional): Path to a custom pip configuration file.
- `dockerfile_lines` (optional): Extra image build lines for system-level binaries your dependencies need.

**Warning**:

  Do not commit real secrets to a `.env` file in your repository. Put credentials under `secrets` and non-secret config under `env`, then read them with `os.environ` in your Python code.

### Declare Dependencies

At boot, we install the following dependencies from your checked-out repository:

- `requirements.txt`: `pip install --no-cache-dir -r requirements.txt`
- `pyproject.toml`: `pip install --no-cache-dir -e .`
- Neither: No install step. Your graph can only use packages preinstalled in the image.

If both `requirements.txt` and `pyproject.toml` exist, `requirements.txt` takes precedence and `pyproject.toml` is ignored.

For a typical LLM-backed graph, use a `requirements.txt` similar to:

```text
langgraph>=0.2
langchain-core>=0.3
langchain-openai>=0.2
```

### Expose a Compiled Graph

Point the `graphs` key at a variable produced by `.compile()`. The following example defines a minimal chat-style graph with a `messages` array in state. The last assistant message is shown as streamed reply text. Graphs without `messages` still run; you receive node-level events instead of a chat transcript.

Create or open `my_agent/agent.py`:

```python
import os
from typing import Annotated, TypedDict

from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages

llm = ChatOpenAI(
    api_key=os.environ["HARNESS_INFERENCE_API_KEY"],
    base_url=os.environ["HARNESS_INFERENCE_BASE_URL"],
    model=os.environ["HARNESS_INFERENCE_MODEL"],
    temperature=0,
)

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]

def respond(state: AgentState):
    return {"messages": [llm.invoke(state["messages"])]}

workflow = StateGraph(AgentState)
workflow.add_node("respond", respond)
workflow.add_edge(START, "respond")
workflow.add_edge("respond", END)

agent = workflow.compile()
```

## Write the Environment Spec

Create a file such as `langgraph-agent.yaml` that points at your repository and commit:

```yaml
name: my-langgraph-agent
agent: langgraph
size: mars-2vcpu-4gb
idle_timeout: 10m
env:
  FRAMEWORK_REPO: "https://github.com/<owner>/<repo>.git"
  FRAMEWORK_REPO_SHA: "<exact-commit-sha>"
  HARNESS_INFERENCE_MODEL: "<model-name>"
secrets:
  HARNESS_INFERENCE_API_KEY: ${HARNESS_INFERENCE_API_KEY}
permissions:
  default: ask
```

Export the inference API key so `doctl` can expand `${HARNESS_INFERENCE_API_KEY}`:

```shell
export HARNESS_INFERENCE_API_KEY=<your-inference-api-key>
```

The spec uses the following fields and environment variables:

- `agent` (required): Must be `langgraph`.
- `FRAMEWORK_REPO` (required): Full `https://github.com/...` URL. HTTP URLs and non-GitHub hosts are rejected.
- `FRAMEWORK_REPO_SHA` (required): Exact commit SHA to check out. Boot fails without it.
- `FRAMEWORK_SUBDIR` (optional): Repository-relative directory that contains `langgraph.json`, for monorepos. Defaults to the repository root.
- `HARNESS_INFERENCE_*` (required if your graph calls a model): Inference credentials your code reads from `os.environ`. Put the API key under `secrets`. Non-secret values such as base URL and model may stay in `env`.
- `GITHUB_TOKEN` (required for private repositories): Authenticate the clone with an OAuth slot that names your GitHub connection’s actor ID, or with a personal access token. See [Connect GitHub](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/how-to/connect-github/index.html.md).
- `permissions.default` (recommended): `ask` pauses for approval when the agent requests a human-in-the-loop decision.

**Note**:

  Always pin `FRAMEWORK_REPO_SHA` to an exact commit so every session boots the same code. Do not use a floating branch name.

**Warning**:

  Declare every credential under `secrets`.

## Connect a Model

Your graph owns model configuration. We do not inject a model client for you. Pass credentials in the environment spec (API key under `secrets`), then read them from `os.environ` in Python.

Use the following environment variables:

- `HARNESS_INFERENCE_API_KEY`: API key for your inference endpoint. Prefer `secrets`.
- `HARNESS_INFERENCE_MODEL`: Model name to request from that endpoint.

Any OpenAI-compatible endpoint works with `langchain-openai` when you set these variables and pass `base_url` explicitly. You can use other environment variable names if your code expects them (for example `OPENAI_API_KEY` under `secrets`). Prefer `os.environ["KEY"]` over silent defaults so missing configuration fails at import time.

## Access a Private Repository

If `FRAMEWORK_REPO` is private, declare `GITHUB_TOKEN` under `secrets`. Harness Runtime uses the token only for a shallow clone of the pinned commit and does not write the token into the repository URL or git remotes.

```yaml
env:
  FRAMEWORK_REPO: "https://github.com/<owner>/<private-repo>.git"
  FRAMEWORK_REPO_SHA: "<exact-commit-sha>"
secrets:
  GITHUB_TOKEN:
    source: oauth
    provider: github
    actor: <actor-id>
```

Create the GitHub connection first with `doctl harness-runtime connections create --actor <actor-id>`, and use the same actor ID in the spec. You can also declare a personal access token instead. See [Connect GitHub](https://docs.digitalocean.com/products/managed-agents/agent-harness-runtime/how-to/connect-github/index.html.md).

## Start and Attach to the Session

Start the session from your spec:

```shell
doctl harness-runtime launch ./langgraph-agent.yaml
```

Cold start typically takes about 20 seconds while the sandbox clones the repository, installs dependencies, and starts the Agent Server. When the status is ready, `launch` attaches the session to your terminal:

Detach with `Ctrl+D`. The session stays alive until you pause or remove it. To inspect the event stream, run:

```shell
doctl harness-runtime logs <session-id-or-name>
```

Remove the session when you are finished:

```shell
doctl harness-runtime remove <session-id-or-name>
```

Session names must be unique within your team. Remove the previous session or change `name` before reusing a name. The LangGraph guest idle timeout is 10 minutes between turns. Send input within that window, or start a new session after an idle timeout ends the run.

## Troubleshoot LangGraph Sessions

Use this table to diagnose common LangGraph deployment errors:

| Symptom | Likely cause | What to do |
|---|---|---|
| Session fails to boot; no graph running | `langgraph.json` missing at the resolved directory | Add `langgraph.json` at the repository root, or set `FRAMEWORK_SUBDIR` to the directory that holds it. |
| Boot fails immediately | `FRAMEWORK_REPO` or `FRAMEWORK_REPO_SHA` missing | Set both values. Boot fails closed without your code. |
| `FRAMEWORK_REPO` rejected | URL is not an HTTPS `github.com` address | Use a full `https://github.com/<owner>/<repo>.git` URL. |
| Clone or fetch fails | Private repository, bad SHA, or invalid token | Verify the URL, confirm the commit exists on the remote, and set a valid `GITHUB_TOKEN`. |
| `ModuleNotFoundError` at run time | Dependency missing from your dependency file | Add the package, push a new commit, and update `FRAMEWORK_REPO_SHA`. |
| Unexpected dependency install | Both dependency files present | Consolidate into one file. `requirements.txt` wins over `pyproject.toml`. |
| Authentication error from the model | Credentials absent or not read by your code | Confirm `HARNESS_INFERENCE_*` keys are injected (API key via `secrets`) and your code reads them from `os.environ`. |
| Wrong model endpoint | `base_url` not passed to the client | Pass `base_url=os.environ["HARNESS_INFERENCE_BASE_URL"]`, including the `/v1` suffix. |
| Session create fails on name | Session name already in use | Remove the old session or change `name`. |