{"id":2495,"date":"2025-09-17T16:16:31","date_gmt":"2025-09-17T16:16:31","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2025\/09\/17\/how-to-build-secure-ai-coding-agents-with-cerebras-and-docker-compose\/"},"modified":"2025-09-17T16:16:31","modified_gmt":"2025-09-17T16:16:31","slug":"how-to-build-secure-ai-coding-agents-with-cerebras-and-docker-compose","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2025\/09\/17\/how-to-build-secure-ai-coding-agents-with-cerebras-and-docker-compose\/","title":{"rendered":"How to Build Secure AI Coding Agents with Cerebras and Docker Compose"},"content":{"rendered":"<p>In the recent article, <a href=\"https:\/\/www.cerebras.ai\/blog\/DockerCompose\" target=\"_blank\">Building Isolated AI Code Environments with Cerebras and Docker Compose<\/a>, our friends at Cerebras showcased how one can build a coding agent to use worlds fastest Cerebras\u2019 AI inference API, Docker Compose, ADK-Python, and MCP servers.<\/p>\n<p>In this post, we\u2019ll dive deeper into the underlying technologies and show how the pieces come together to build an AI agent environment that\u2019s portable, secure, and fully containerized. You\u2019ll learn how to create multi-agent systems, run some agents with local models in Docker Model Runner, and integrate custom tools as MCP servers into your AI agent\u2019s workflow.<\/p>\n<p>We\u2019ll also touch on how to build a secure sandbox for executing the code your agent writes, an ideal use case for containers in real-world development.\u00a0<\/p>\n<h2 class=\"wp-block-heading\">Getting Started<\/h2>\n<p>To begin, clone the <a href=\"https:\/\/github.com\/dockersamples\/docker-cerebras-demo\" target=\"_blank\">repository from GitHub<\/a> and navigate into the project directory.<\/p>\n<p>Get the code for the agent, and prepare the .env file to provide your Cerebras API key:\u00a0<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\ngit clone https:\/\/github.com\/dockersamples\/docker-cerebras-demo &amp;&amp; cd docker-cerebras-demo\n<\/div>\n<p>Next, prepare the .env file to provide your Cerebras API key. You can get a key from the<a href=\"https:\/\/cloud.cerebras.ai\/\" target=\"_blank\"> Cerebras Cloud platform<\/a>.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n# This copies the sample environment file to your local .env file<br \/>\ncp .env-sample .env\n<\/div>\n<p>Now, open the .env file in your favorite editor and add your API key to the CEREBRAS_API_KEY line. Once that\u2019s done, run the system using Docker Compose:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\ndocker compose up &#8211;build\n<\/div>\n<p>The first run may take a few minutes to pull the model and containers. Once it\u2019s up, you can see the agent at localhost:8000.<\/p>\n<p>The first run may take a few minutes to pull the necessary Docker images and the AI model. Once it\u2019s running, you can access the agent\u2019s interface at http:\/\/localhost:8000. From there, you can interact with your agent and issue commands like \u201cwrite code,\u201d \u201cinitialize the sandbox environment,\u201d or request specific tools like \u201ccerebras, curl <a href=\"http:\/\/docker.com\/\" target=\"_blank\">docker.com<\/a> for me please.\u201d<\/p>\n<h2 class=\"wp-block-heading\">Understanding the Architecture<\/h2>\n<p>This demo follows the architecture from our<a href=\"https:\/\/www.google.com\/search?q=https:\/\/github.com\/dockersamples\/compose-for-agents\" target=\"_blank\"> Compose for Agents repository<\/a>, which breaks down an agent into three core components:<\/p>\n<p>The Agentic Loop: This is the main application logic that orchestrates the agent\u2019s behavior. In our case, it\u2019s an ADK-Python-based application. The ADK-Python framework also includes a visualizer that lets you inspect tool calls and trace how the system reached specific decisions.<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<p>The MCP Tools: These are the external tools the agent can use. We provide them securely via the Docker MCP Gateway. In this app we use context7 and node sandbox MCP servers.\u00a0<\/p>\n<p>The AI Model: You can define any local or remote AI model you want to use. Here, we\u2019re using a local Qwen model for routing between the local agent and the powerful Cerebras agent which will use Cerebras API.\u00a0<\/p>\n<p><a href=\"https:\/\/cloud.cerebras.ai\/\" target=\"_blank\">Cerebras Cloud<\/a> serves as a specialized, high-performance inference backend. It can run massive models, like a half-trillion parameter Qwen coder, at thousands of tokens per second. While our simple demo doesn\u2019t require this level of speed, such performance is a game-changer for real-world applications.<\/p>\n<p>Most of the prompts and responses are a few hundred tokens long, as they are simple commands to initialize a sandbox or write some JavaScript code in it. You\u2019re welcome to make the agent work harder and see Cerebras\u2019 performance on more verbose requests.\u00a0<\/p>\n<p>For example, you can ask the Cerebras agent to write some JavaScript code, and see it call the functions from the MCP tools to read and write the files and run them as you see on the screenshot below.\u00a0<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<h2 class=\"wp-block-heading\">Building a Custom Sandbox as an MCP Server<\/h2>\n<p>A key feature of this setup is the ability to create a secure sandbox for code execution. To do this, we\u2019ll build a custom MCP server. In our example, we enable two MCP servers:<\/p>\n<p>context7: This gives our agent access to the latest documentation for various application frameworks.<\/p>\n<p>node-code-sandbox: This is our custom-made sandbox for executing the code our agent writes.<\/p>\n<p>You can find the implementation of our Node.js sandbox server in the<a href=\"https:\/\/github.com\/shelajev\/node-sandbox-mcp\" target=\"_blank\"> node-sandbox-mcp GitHub repository<\/a>. It\u2019s a Quarkus application written in Java that exposes itself as an stdio mcp-server and uses the awesome <a href=\"http:\/\/testcontainers.com\/\" target=\"_blank\">Testcontainers library<\/a> to create and manage the sandbox containers programmatically.<\/p>\n<p>An important detail is that you have full control over the sandbox configuration. We start the container with a common Node.js development image and, as a crucial security measure, disable its networking. But since it\u2019s a custom MCP server, you can enable any security measures you deem necessary.\u00a0<\/p>\n<p>Here\u2019s a snippet of the Testcontainers-java code used to create the container:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\nGenericContainer sandboxContainer = new GenericContainer&lt;&gt;(&#8220;mcr.microsoft.com\/devcontainers\/javascript-node:20&#8221;)<br \/>\n                .withNetworkMode(&#8220;none&#8221;) \/\/ disable network!!<br \/>\n                .withWorkingDirectory(&#8220;\/workspace&#8221;)<br \/>\n                .withCommand(&#8220;sleep&#8221;, &#8220;infinity&#8221;);\n<p> sandboxContainer.start();\n<\/p><\/div>\n<p>Testcontainers provides a flexible, idiomatic API to interact with the sandbox. Running a command or writing a file becomes a simple one-line method call:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n\/\/ To execute a command inside the sandbox<br \/>\nsandbox.execInContainer(command);\n<p>\/\/ To write a file into the sandbox<br \/>\nsandbox.copyFileToContainer(Transferable.of(contents.getBytes()), filename);\n<\/p><\/div>\n<p>The actual implementation has a bit more glue code for managing background processes or selecting the correct sandbox if you\u2019ve created multiple, but these one-liners are the core of the interaction.<\/p>\n<h2 class=\"wp-block-heading\">Packaging and Using the Custom Server<\/h2>\n<p>To use our custom server, we first need to package it as a Docker image. For Quarkus applications, a single command does the trick:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n.\/mvnw package -DskipTests=true -Dquarkus.container-image.build=true\n<\/div>\n<p>This command produces a local Docker image and outputs its name, something like:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n[INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Built container image shelajev\/node-sandbox:1.0.0-SNAPSHOT\n<\/div>\n<p>Since we\u2019re running everything locally, we don\u2019t even need to push this image to a remote registry. You can inspect this image in Docker Desktop and find its hash, which we\u2019ll use in the next step.<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<h2 class=\"wp-block-heading\">Integrating the Sandbox via the MCP Gateway<\/h2>\n<p>With our custom MCP server image ready, it\u2019s time to plug it into <a href=\"https:\/\/github.com\/docker\/mcp-gateway\" target=\"_blank\">the MCP Gateway<\/a>. We\u2019ll create a custom catalog file (<a href=\"https:\/\/github.com\/dockersamples\/docker-cerebras-demo\/blob\/main\/mcp-gateway-catalog.yaml\" target=\"_blank\">mcp-gateway-catalog.yaml<\/a>) that enables both the standard context7 server and our new node-code-sandbox.<\/p>\n<p>Currently, creating this file is a manual process, but we\u2019re working on simplifying it. The result is a portable catalog file that mixes standard and custom MCP servers.<\/p>\n<p>Notice two key things in the configuration for the node-code-sandbox MCP server in the catalog:<\/p>\n<p>longLived: true: This tells the gateway that our server needs to persist between the tool calls to track the sandbox\u2019s state.\u00a0<\/p>\n<p>image:: We reference the specific Docker image using its sha256 hash to ensure reproducibility.<\/p>\n<p>If you\u2019re building the custom server for the sandbox MCP, you can replace the image reference with the one your build step produced.\u00a0<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n    longLived: true<br \/>\n    image: olegselajev241\/node-sandbox@sha256:44437d5b61b6f324d3bb10c222ac43df9a5b52df9b66d97a89f6e0f8d8899f67\n<\/div>\n<p>Finally, we update our docker-compose.yml to mount this catalog file and enable both servers:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n  mcp-gateway:<br \/>\n    # mcp-gateway secures your MCP servers<br \/>\n    image: docker\/mcp-gateway:latest<br \/>\n    use_api_socket: true<br \/>\n    command:<br \/>\n      &#8211; &#8211;transport=sse<br \/>\n      # add any MCP servers you want to use<br \/>\n      &#8211; &#8211;servers=context7,node-code-sandbox<br \/>\n      &#8211; &#8211;catalog=\/mcp-gateway-catalog.yaml<br \/>\n    volumes:<br \/>\n      &#8211; .\/mcp-gateway-catalog.yaml:\/mcp-gateway-catalog.yaml:ro\n<\/div>\n<p>When you run docker compose up, the gateway starts, which in turn starts our node-sandbox MCP server. When the agent requests a sandbox, a third container is launched \u2013 the actual isolated environment.\u00a0<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<p>You can use tools like Docker Desktop to inspect all running containers, view files, or even open a shell for debugging.<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<h2 class=\"wp-block-heading\">The Security Benefits of Containerized Sandboxes\u00a0<\/h2>\n<p>This containerized sandbox approach is a significant security win. Containers provide a well-understood security boundary with a smaller vulnerability profile than running random internet code on your host machine, and you can harden them as needed.<\/p>\n<p>Remember how we disabled networking in the sandbox container? This means any code the agent generates cannot leak local secrets or data to the internet. If you ask the agent to run code that tries to access, for example, google.com, it will fail.<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<p>This demonstrates a key advantage: granular control. While the sandbox is cut off from the network, other tools are not. The context7 MCP server can still access the internet to fetch documentation, allowing the agent to write better code without compromising the security of the execution environment.<\/p>\n<div class=\"wp-block-ponyo-image\"><\/div>\n<p>Oh, and a neat detail is that when you stop the containers managed by compose, it also kills the sandbox MCP server, and that in turn triggers Testcontainers to clean up all the sandbox containers, just like it cleans after a typical test run.\u00a0<\/p>\n<h2 class=\"wp-block-heading\">Next Steps and Extensibility<\/h2>\n<p>This coding agent is a great starting point, but it isn\u2019t production-ready. For a real-world application, you might want to grant controlled access to resources like the npm registry. You could, for example, achieve this by mapping your local npm cache from the host system into the sandbox. This way, you, the developer, control exactly which npm libraries are accessible.<\/p>\n<p>Because the sandbox is a custom MCP server, the possibilities are endless. You can build it yourself, tweak it however you want, and integrate any tools or constraints you need.<\/p>\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n<p>In this post, we demonstrated how to build a secure and portable AI coding agent using Docker Compose and the MCP Toolkit. By creating a custom MCP server with Testcontainers, we built a sandboxed execution environment that offers granular security controls, like disabling network access, without limiting the agent\u2019s other tools.\u00a0 We connect this coding agent to Cerebras API, so we get incredible inference speed.\u00a0This architecture provides a powerful and secure foundation for building your own AI agents. We encourage you to clone the repository and experiment with the code! You probably already <a href=\"https:\/\/docs.docker.com\/get-started\/get-docker\/\" target=\"_blank\">have Docker<\/a> and can sign up for a Cerebras API key <a href=\"http:\/\/inference.cerebras.ai\/\" target=\"_blank\">here<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>In the recent article, Building Isolated AI Code Environments with Cerebras and Docker Compose, our friends at Cerebras showcased how [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[4],"tags":[],"class_list":["post-2495","post","type-post","status-publish","format-standard","hentry","category-docker"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/2495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=2495"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/2495\/revisions"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=2495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=2495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=2495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}