{"slug": "how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore", "title": "How to Deploy a LangGraph Agent on AWS Bedrock AgentCore", "summary": "A developer has published a guide for deploying LangGraph agents on AWS Bedrock AgentCore, a serverless hosting platform that supports open-source frameworks like LangGraph, Strands, CrewAI, and LlamaIndex. The approach uses the AgentCore CLI to scaffold projects, run local development servers, and deploy to AWS via CDK, requiring only a wrapper around existing agent logic rather than a full redesign. The deployment process involves installing the CLI, adding dependencies including `bedrock-agentcore` and `aws-opentelemetry-distro`, and creating a `main.py` entrypoint with a `BedrockAgentCoreApp` instance and an `@app.entrypoint` decorated function.", "body_md": "You’ve built a LangGraph agent that works fine on your laptop. The next challenge is getting it running in a scalable, serverless production infrastructure without having to redesign the whole thing.\n\nThat’s where AWS Bedrock AgentCore comes in. In this guide, I’ll show you how to put a wrapper for your existing agent to make it run on AgentCore, set up an AgentCore project, test it locally, deploy it to AWS, and invoke it after deployment.\n\nAgentCore is a serverless hosting platform designed by AWS to deploy, scale, and operate your AI agents securely without you managing the infrastructure. It works with any open-source framework like LangGraph, Strands, CrewAI, or LlamaIndex and supports Large Language Models like OpenAI's GPT, Google's Gemini, or Anthropic's Claude. So you don’t have to rewrite the agent logic. It also provides session isolation, persistent memory, observability and identity management.\n\nThe deployment is managed through the **AgentCore CLI**, a Node.js tool that scaffolds projects, runs a local dev server, and deploys to AWS using CDK under the hood.\n\nBefore you start, make sure you have the following in place:\n\n`npm install -g aws-cdk`\n\n)`aws configure`\n\n)The AgentCore workflow starts with a single command-line tool. You’ll use it to create the project, run the app locally, and deploy it to AWS cloud.\n\nInstall the CLI:\n\n```\nnpm install -g @aws/agentcore\n```\n\nVerify it after the installation:\n\n```\nagentcore --help\n```\n\nAdd the following packages to your agent's dependencies:\n\n- `bedrock-agentcore`\n\n- the Python SDK that provides the `BedrockAgentCoreApp`\n\nwrapper class.\n\n- `aws-opentelemetry-distro`\n\n- AWS-supported distribution of the OpenTelemetry Python Instrumentation package.\n\n```\n# pyproject.toml\n[project]\nname = \"my-agent\"\nversion = \"0.1.0\"\nrequires-python = \">=3.12\"\n\ndependencies = [\n    \"aws-opentelemetry-distro==0.17.0\",\n    \"bedrock-agentcore>=1.6.3\",\n    \"boto3>=1.42.0\",\n    \"langgraph>=1.1.0\",\n    \"langchain-core>=1.2.0\",\n    # ... your other existing dependencies\n]\n```\n\nInstall all dependencies with your usual workflow:\n\n```\nuv sync\n# or: pip install -e .\n```\n\n`main.py`\n\n)\nAgentCore expects a single Python file as the entrypoint with a `BedrockAgentCoreApp`\n\ninstance and a function decorated with `@app.entrypoint`\n\n. The important part is that your LangGraph logic does not need to change much; you’re just wrapping it in a small runtime entrypoint.\n\nHere is the basic structure:\n\n``` python\n# main.py\n\nfrom langchain_core.messages import HumanMessage\nfrom bedrock_agentcore.runtime import BedrockAgentCoreApp\n\nfrom graphs.my_agent_graph import build_my_agent  # your existing graph builder\n\n# 1. Instantiate the AgentCore app and logger\napp = BedrockAgentCoreApp()\nlog = app.logger\n\n# 2. Build your graph at module load time (startup)\n#    AgentCore initialises the module once, then handles concurrent invocations.\n#    Any failure here will prevent a broken agent from going live.\ndef create_agent():\n    log.info(\"Initialising agent...\")\n    graph = build_my_agent()   # returns your compiled LangGraph StateGraph\n    log.info(\"Agent ready\")\n    return graph\n\ntry:\n    graph = create_agent()\nexcept Exception as e:\n    log.error(f\"Critical failure during agent initialisation: {e}\")\n    raise  # fail fast — don't let a broken agent start serving requests\n\n# 3. Async helper that drives the LangGraph streaming loop\nasync def run_agent(user_input: str, session_id: str = \"default-session\") -> str:\n    responses = []\n    config = {\"configurable\": {\"thread_id\": session_id}}\n\n    async for chunk in graph.astream(\n        {\"messages\": [HumanMessage(content=user_input)]},\n        config=config,\n        stream_mode=\"values\",\n    ):\n        messages = chunk.get(\"messages\", [])\n        if messages:\n            last = messages[-1]\n            if getattr(last, \"type\", None) == \"ai\":\n                responses.append(last)\n\n    if not responses:\n        return \"No response\"\n\n    content = getattr(responses[-1], \"content\", \"\")\n    return content if isinstance(content, str) else str(content)\n\n# 4. The entrypoint — this is what AgentCore calls on every invocation\n@app.entrypoint\nasync def invoke(payload, context):\n    try:\n        log.info(\"Invoke received\")\n        user_input = payload.get(\"prompt\", \"\")\n        session_id = payload.get(\"session_id\", \"default-session\")\n\n        if not user_input.strip():\n            return {\"error\": \"Prompt cannot be empty\"}\n\n        response = await run_agent(user_input, session_id)\n        return {\"response\": response}\n\n    except Exception as e:\n        log.error(f\"Error: {e}\")\n        return {\"error\": str(e)}\n\n# 5. Run the app (only executed when running locally using agentcore dev)\nif __name__ == \"__main__\":\n    app.run()\n```\n\n** BedrockAgentCoreApp()** gives you the AgentCore runtime wrapper. It sets up the HTTP server, health check endpoints, and structured logging for you.\n\n** Module-level graph initialisation** — the graph is created once when the module loads, not on every request. That catches startup failures early, which is good because it prevents a broken app from going live.\n\n** @app.entrypoint** — this decorator registers the function as the handler for incoming invocations. It receives\n\n`payload`\n\n(the parsed JSON body) and `context`\n\n(AgentCore request context). It should return a plain JSON-serializable dictionary, not a raw string or a custom object.** session_id → thread_id** — The session_id is passed into LangGraph as thread_id, which enables per-session memory with\n\n`MemorySaver`\n\n. Next, we need to generate our project layout. Run the initialization wizard inside a clean root folder and let it create the basic project layout for you.\n\n```\nagentcore create\n```\n\nThe setup wizard will ask a few simple questions, like the project name, language, Python version, entrypoint file, etc. The important part is that the entrypoint and the Python version matches your app.\n\nAfter the project is created, you’ll get an agentcore.json file (in agentcore folder) that tells AgentCore where your code lives and how to run it.\n\n```\n{\n  \"name\": \"MyAgentOnAgentcore\",\n    \"runtimes\": [\n    {\n      \"name\": \"my-agent\",\n      \"build\": \"CodeZip\",\n      \"entrypoint\": \"main.py\",\n      \"codeLocation\": \"app/my-agent/\",\n      \"runtimeVersion\": \"PYTHON_3_12\",\n      \"networkMode\": \"PUBLIC\",\n      \"protocol\": \"HTTP\",\n      \"envVars\": []\n    }\n  ]\n}\n```\n\nCopy or move your agent source into the directory specified by `codeLocation`\n\nin `agentcore.json`\n\n. This part is easy to get wrong, and when it’s wrong, deployment becomes unnecessarily frustrating. I struggled for a while after accidentally putting it in the agentcore folder.\n\nA typical layout should look like this:\n\n```\nMyAgentOnAgentcore/\n├──agentcore\n    └── agentcore.json\n├──app/\n    └──my-agent/                       ← codeLocation \n        ├── main.py                  ← entrypoint (matches agentcore.json)\n        ├── pyproject.toml           ← or requirements.txt\n        ├── src/\n        │   └── my_agent_graph.py    ← your LangGraph graph builder\n        │   └── tools.py             ← your tools, utilities, etc.\n        └── .env                   ← your configuration variables\n```\n\nMake sure `main.py`\n\nsits at the top level of `codeLocation`\n\nand matches the entrypoint in `agentcore.json`\n\nexactly.\n\nAgentCore can pass environment variables into the runtime container. For local testing, a `.env`\n\nfile is usually the easiest option.\n\nFor production, you can put values in `agentcore.json`\n\nunder `envVars`\n\n(`.env`\n\nfile also works). But secrets like passwords and API keys are better stored in AWS Secrets Manager and loaded at runtime. If you do that, the AgentCore execution role needs permission to read those secrets.\n\n**Note:** `envVars`\n\nshould be an array of JSON objects with `name`\n\nand `value`\n\nfields.\n\nBefore you try to run anything, use AgentCore CLI to validate the project configuration:\n\n```\nagentcore validate\n```\n\nThis catches the common mistakes early, like a missing entrypoint file or a bad path in the config.\n\nStart the local runtime to test the agent locally:\n\n```\nagentcore dev\n```\n\nThis spins up a local HTTP server that closely mirrors the production environment for testing. If everything is wired up properly, you should see that the app is ready and listening on a local port.\n\nYou can test an invocation using either the AgentCore CLI or curl:\n\n```\nagentcore invoke \"Summarise last month sales\"\n```\n\nOr\n\n```\ncurl -X POST http://localhost:8080/invocations \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prompt\": \"Summarise last month sales\", \"session_id\": \"test-001\"}'\n```\n\nA successful response should come back as JSON with the agent’s answer in it.\n\n```\n{\"response\": \"Last month, total sales were $1.2M across 3 regions...\"}\n```\n\nOnce local testing looks solid, deploy to AWS Bedrock AgentCore Runtime:\n\n```\nagentcore deploy\n```\n\nUnder the hood, the CLI:\n\nThe first deploy takes a few minutes. Subsequent deploys tend to be faster.\n\nWhen it’s successfully completed, check the runtime status. It will display the runtime ARN and HTTP URL to invoke the deployed agent.\n\n```\nagentcore status\n```\n\nThe easiest way to test the deployed version is with the CLI:\n\n```\nagentcore invoke --prompt \"Who are the top 5 customers by revenue?\" --session-id \"session-id-with-length-greater-than-or-equal-33\"\n```\n\nIf you want to call it using HTTP, you’ll need to sign the request with AWS SigV4.\n\n```\ncurl -X POST \"https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn-of-MyAgentOnAgentcore-xxxx/invocations\" \\\n  --user \"$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY\" \\\n  --aws-sigv4 \"aws:amz:us-east-1:bedrock-agentcore\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-amzn-bedrock-agentcore-runtime-session-id: session-id-with-length-greater-than-or-equal-33\" \\\n  -d '{\"prompt\": \"Who are the top 5 customers by revenue?\"}'\n```\n\nUse the HTTP URL displayed by `agentcore status`\n\n.\n\nWhen integrating your agent with other Python applications, using the SDK is usually the cleanest approach. You can keep the runtime ARN in an environment variable, send the prompt, and pass the same session ID back on follow-up requests.\n\n``` python\n# invoke_agent.py\nimport json\nimport boto3\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nclient = boto3.client(\"bedrock-agentcore\", region_name=\"us-east-1\")\nruntime_arn = os.getenv(\"AGENTCORE_RUNTIME_ARN\")\n\ndef invoke(prompt: str, session_id: str) -> str:\n    payload = json.dumps({\"prompt\": prompt, \"session_id\": session_id})\n\n    response = client.invoke_agent_runtime(\n        agentRuntimeArn=runtime_arn,\n        payload=payload,\n        contentType=\"application/json\"\n    )\n\n    body = json.loads(response[\"response\"].read().decode())\n    return body[\"response\"]\n\nif __name__ == \"__main__\":\n    session_id = \"\"\n    print(\"Ready. Type 'exit' to quit.\")\n\n    while True:\n        prompt = input(\"Your question: \").strip()\n        if prompt == \"exit\":\n            break\n        if not prompt:\n            continue\n\n        params = {\n            \"agentRuntimeArn\": runtime_arn,\n            \"payload\": json.dumps({\"prompt\": prompt}),\n            \"contentType\": \"application/json\"\n        }\n        if session_id:\n            params[\"runtimeSessionId\"] = session_id\n\n        try:\n            response = client.invoke_agent_runtime(**params)\n            body = json.loads(response[\"response\"].read().decode())\n            session_id = response.get(\"runtimeSessionId\", session_id)\n            print(f\"Agent: {body['response']}\\n\")\n        except Exception as e:\n            print(f\"Error: {e}\")\n```\n\nSet `AGENTCORE_RUNTIME_ARN`\n\nin your `.env`\n\nfile:\n\n```\nAGENTCORE_RUNTIME_ARN=arn:aws:bedrock-agentcore:us-east-1:123456789012:agent-runtime/MyAgentOnAgentcore-xxxx\n```\n\nNotice `runtimeSessionId`\n\nin the boto3 example. AgentCore returns a `runtimeSessionId`\n\nin every response. Passing it back in the next request tells AgentCore to route subsequent calls to the same session context — which, combined with LangGraph's `MemorySaver`\n\n, gives the agent full conversation memory across multiple HTTP calls.\n\nStartup failures are intentional. If `create_agent()`\n\nraises at module load, AgentCore will refuse to start the runtime. This is a feature, not a bug — it prevents an incorrectly configured agent (missing credentials, wrong DB URL) from going live silently.\n\nThe execution role AgentCore creates needs explicit permissions for any AWS service your agent touches, including Secrets Manager and S3. Add those permissions after the first deploy.\n\nThe Python version in `pyproject.toml`\n\nshould match the runtime version in `agentcore.json`\n\n.\n\nThe entrypoint should return a JSON-serialisable dictionary. If it returns a plain string or a custom object, the runtime boundary will reject it.\n\nThe transition from a local LangGraph agent to an AgentCore deployment really comes down to a few practical changes: add the right dependencies, wrap the graph in BedrockAgentCoreApp, scaffold the project, test locally, then deploy and invoke it.\n\nEverything else — the graph, the tools, the model calls, and the memory setup — stays mostly the same. AgentCore handles the runtime side, and LangGraph handles the agent logic.\n\nA full working example is available at [github.com/thedataengr/data-agent-on-aws-agentcore](https://github.com/thedataengr/data-agent-on-aws-agentcore).", "url": "https://wpnews.pro/news/how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore", "canonical_source": "https://dev.to/vfazal/how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore-2a9p", "published_at": "2026-05-25 15:54:19+00:00", "updated_at": "2026-05-25 16:03:36.117207+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-tools", "ai-products", "mlops"], "entities": ["AWS Bedrock AgentCore", "LangGraph", "OpenAI", "Google", "Anthropic", "CrewAI", "LlamaIndex", "Strands"], "alternates": {"html": "https://wpnews.pro/news/how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore", "markdown": "https://wpnews.pro/news/how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore.md", "text": "https://wpnews.pro/news/how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore.txt", "jsonld": "https://wpnews.pro/news/how-to-deploy-a-langgraph-agent-on-aws-bedrock-agentcore.jsonld"}}