{"slug": "i-set-up-my-first-docker-sbx-kit-and-here-s-how-i-did-it", "title": "I set up my first Docker SBX kit, and here's how I did it", "summary": "Docker Captain and AI Engineer Yhary from Colombia built a Docker Sandbox Kit mixin that automatically starts an MLflow tracking server for Claude Code, enabling out-of-the-box experiment logging and model versioning. The kit uses a declarative spec.yaml file to configure the sandbox environment, with heavy dependencies baked into a Docker image and dynamic settings in the YAML to avoid configuration drift.", "body_md": "A practical guide to building a real MLflow mixin kit from scratch, errors included.\n\nHey! Let me tell you something. When I first heard about **Docker Sandbox Kits**, my first reaction was: \"Okay, another YAML thing. How hard can it be?\"\n\nSpoiler: it was harder than expected. But also way more interesting.\n\nI'm Yhary, a Docker Captain and AI Engineer from Colombia. I recently built my first SBX kit as part of a Docker Captains community activity, and I want to walk you through the whole journey: what it is, why it matters, how I built it, and most importantly, every single thing that broke along the way.\n\nLet's go.\n\nFirst Things First: **What Even Is a Docker Sandbox Kit?**\n\nImagine you're an AI engineer. You spin up a new sandbox to run some experiments with Claude Code. But before you can actually do anything, you need to:\n\nEvery. Single. Time.\n\nThat's configuration drift. And it's been a problem since the 90s.\n\nA **Docker Sandbox Kit (SBX Kit)** solves this. It's a declarative YAML file (`spec.yaml`\n\n) that configures your sandbox environment automatically at creation time. One file. Reproducible. Shareable. No more \"works on my machine.\"\n\nThink of it like **dotfiles** + **Infrastructure as Code**, but specifically designed for AI agent sandboxes.\n\nWhat a kit actually does\n\n| Capability | Example |\n|---|---|\n| Installs tools |\n`pip install mlflow` , CLIs, binaries |\n| Injects environment variables | `MLFLOW_TRACKING_URI=http://localhost:5000` |\n| Manages secrets securely | Tokens never enter the sandbox VM |\n| Controls network access | Only allows `pypi.org` , blocks everything else |\n| Runs startup scripts | Launches MLflow server automatically |\n\nTwo types of kits\n\n`kind: agent`\n\n: Defines a completely new agent from scratch. Has its own base image and entrypoint.\n\n`kind: mixin`\n\n: Extends an existing agent (like Claude Code) by layering new capabilities on top. Think of it as a plugin.\n\nPut heavy, stable dependencies in the Docker image. Put everything that changes (credentials, network rules, startup commands) in the kit YAML.The golden rule:\n\n**Okay, Now Let's Build One**\n\n**Enough theory**. Let me show you what I built and how.\n\n**The goal:** A mixin kit that automatically starts an MLflow tracking server when the sandbox is created, so Claude Code can log ML experiments, track metrics, and version models right out of the box.\n\n**Step 1:** Plan the architecture\n\nBefore writing any YAML, I sketched the architecture:\n\n```\nSANDBOX (sbx)\n\nClaude Code Agent\n(orchestrates experiments via prompts)\n\nMLflow Tracking Server \nhttp://localhost:5000\n\nSQLite backend + artifact store\n~/.mlflow/mlflow.db\n```\n\nClaude Code talks to MLflow. MLflow persists everything to SQLite. Simple.\n\n**Step 2:** Set up the folder structure\n\n```\nmlops-experiment-agent/\n├── spec.yaml - The heart of the kit\n├── Dockerfile - Base image with heavy dependencies\n├── CLAUDE.md - Instructions for the Claude Code agent\n├── scripts/\n│   └── start-mlflow.sh\n└── README.md\n```\n\n**Step 3:** Write the Dockerfile\n\nHeavy dependencies go in the image not in the install hook. This way, creating a sandbox just pulls a layer instead of downloading gigabytes every time.\n\n```\nFROM --platform=linux/amd64 docker/sandbox-templates:shell-docker\n\nUSER root\n\nRUN apt-get update && apt-get install -y \\\n    software-properties-common \\\n    curl \\\n    build-essential \\\n    pkg-config \\\n    && add-apt-repository ppa:deadsnakes/ppa -y \\\n    && apt-get update && apt-get install -y \\\n    python3.11 \\\n    python3.11-venv \\\n    python3.11-dev \\\n    && rm -rf /var/lib/apt/lists/*\n\nUSER 1000\nENV PATH=/home/agent/.venv/bin:/home/agent/.local/bin:${PATH}\n\nRUN python3.11 -m venv /home/agent/.venv && \\\n    /home/agent/.venv/bin/pip install --no-cache-dir --upgrade pip && \\\n    /home/agent/.venv/bin/pip install --no-cache-dir \\\n    \"numpy==1.26.4\" \\\n    \"pandas==2.2.2\" \\\n    \"scikit-learn==1.4.2\" \\\n    \"mlflow==2.13.0\" \\\n    \"boto3\"\n```\n\n**Step 4:** Write CLAUDE.md\n\nThis file tells Claude Code what tools are available inside the sandbox:\n\n```\n# MLOps Experiment Agent\n\nYou are an ML experiment orchestrator. MLflow is running at http://localhost:5000.\n\n## Your capabilities\n- Log experiments: `mlflow.start_run()`, `mlflow.log_param()`, `mlflow.log_metric()`\n- Register models: `mlflow.sklearn.log_model()`\n- Compare runs via the MLflow UI or Python client\n- Version datasets using MLflow's dataset tracking\n\n## Common tasks you can do\n- \"Run a baseline experiment with this dataset\"\n- \"Compare the last 3 runs by accuracy\"\n- \"Register the best model to the Model Registry\"\n- \"Show me all experiments logged today\"\n\n## MLflow UI\nAvailable at: http://localhost:5000\n```\n\n**Step 5:** Write the spec.yaml\n\nThis is the final kit manifest after all the debugging (more on that below):\n\n```\nschemaVersion: \"1\"\nkind: mixin\nname: mlops-mixin\ndisplayName: MLOps Experiment Agent\ndescription: >\n  Orchestrates ML experiments inside a Docker Sandbox. Tracks runs,\n  logs metrics and parameters, versions models using MLflow.\n  Ideal for classification and CV pipelines.\n\nenvironment:\n  variables:\n    MLFLOW_TRACKING_URI: \"http://localhost:5000\"\n    MLFLOW_EXPERIMENT_NAME: \"sandbox-experiments\"\n\nnetwork:\n  allowedDomains:\n    - \"pypi.org:443\"\n    - \"files.pythonhosted.org:443\"\n\ncommands:\n  install:\n    - command: \"pip install --break-system-packages --upgrade pip && pip install --break-system-packages 'numpy>=2.0' 'pandas>=2.0' 'scikit-learn>=1.5' 'mlflow>=2.13' boto3 && sed -i 's/from importlib.abc import Traversable/from importlib.resources.abc import Traversable/' /home/agent/.local/lib/python3.14/site-packages/mlflow/assistant/skill_installer.py\"\n      user: \"1000\"\n      description: \"Install MLflow and ML dependencies and patch Python 3.14 compatibility\"\n  startup:\n    - command: [\"sh\", \"-c\", \"mkdir -p /home/agent/.mlflow/artifacts && setsid /home/agent/.local/bin/mlflow server --backend-store-uri sqlite:////home/agent/.mlflow/mlflow.db --default-artifact-root /home/agent/.mlflow/artifacts --host 0.0.0.0 --port 5000 > /home/agent/.mlflow/server.log 2>&1 &\"]\n      user: \"1000\"\n      description: \"Start MLflow tracking server\"\n```\n\n**Step 6:** Build and publish the Docker image\n\nOn Apple Silicon (M1/M2/M3) you need to build for multiple architectures:\n\n```\n# Create a multi-arch builder\ndocker buildx create --name multiarch-builder --use\n\n# Build and push to Docker Hub for both platforms\ndocker buildx build \\\n  --platform linux/amd64,linux/arm64 \\\n  -t yharyarias/mlops-experiment-agent:latest \\\n  --push .\n```\n\n**Step 7:** Stage and publish the kit\n\n```\n# Stage a clean copy, sbx kit push ignores .gitignore!\nmkdir -p /tmp/kit-stage/mlops-experiment-agent\nrsync -a \\\n  --exclude '.git' --exclude '.venv' --exclude '.sbx' \\\n  --exclude '*.tar' --exclude '.DS_Store' --exclude '__pycache__' \\\n  ./mlops-experiment-agent/ /tmp/kit-stage/mlops-experiment-agent/\n\n# Publish as an OCI artifact\nsbx kit push /tmp/kit-stage/mlops-experiment-agent \\\n  docker.io/yharyarias/mlops-experiment-agent-kit:latest\n```\n\nImportant:sbx kit push packages the directory exactly as it is, it does NOT respect .gitignore. Always stage a clean copy first. If you have a .venv or .sbx/.env with real secrets, they will be published.\n\nStep 8: Run it!\n\n```\nsbx run \\\n  --kit docker.io/yharyarias/mlops-experiment-agent-kit:latest \\\n  --name mlops-sandbox \\\n  claude\n```\n\nThen verify from a second terminal:\n\n```\n# Is MLflow running?\nsbx exec mlops-sandbox -- curl -s http://localhost:5000/health\n\n# Check the startup log\nsbx exec mlops-sandbox -- cat /var/log/sbx-kit-startup.log\n\n# Check the MLflow server log\nsbx exec mlops-sandbox -- cat /home/agent/.mlflow/server.log\n```\n\nThe Errors (This Is the Good Part)\n\nLet me be honest with you. Nothing worked on the first try. Here's every error I hit and how I fixed it.\n\n**Error 1**: PEP 668: pip won't install system packages\n\nnote: If you believe this is a mistake, please contact your Python installation or OS\n\ndistribution provider. You can override this, at the risk of breaking your Python\n\ninstallation or OS, by passing --break-system-packages.\n\n**What happened:** Modern Ubuntu protects the system Python from pip installs.\n\nFix:\n\n`pip install --break-system-packages mlflow scikit-learn pandas numpy boto3`\n\n**Error 2**: NumPy has no wheel for Python 3.14\n\nCannot compile `Python.h`\n\n. Perhaps you need to install python-dev|python-devel\n\nProject name: NumPy\n\nProject version: 1.26.4\n\nRun-time dependency python found: YES 3.14\n\nHas header \"Python.h\" with dependency python: NO\n\n**What happened:** The sandbox base image uses Python 3.14. `NumPy 1.26.4`\n\nhas no prebuilt wheel for `Python 3.14`\n\n, it tries to compile from source and fails.\n\nFix: Use `numpy>=2.0`\n\nwhich has prebuilt wheels for Python 3.14:\n\n`pip install --break-system-packages 'numpy>=2.0' 'pandas>=2.0' 'scikit-learn>=1.5' 'mlflow>=2.13'`\n\n**Error 3**: Apple Silicon platform mismatch\n\nno match for platform in manifest sha256:dd0618b...: not found\n\n**What happened:** Building on Mac M1/M2/M3 produces an aarch64 image. The sbx runtime expected a multi-arch manifest.\n\nFix: Use docker buildx to build for both platforms:\n\n```\ndocker buildx build \\\n  --platform linux/amd64,linux/arm64 \\\n  -t yharyarias/mlops-experiment-agent:latest \\\n  --push .\n```\n\n**Error 4**: MLflow ImportError on Python 3.14\n\n*ImportError: cannot import name 'Traversable' from 'importlib.abc'\nFile \".../mlflow/assistant/skill_installer.py\", line 11, in\nfrom importlib.abc import Traversable*\n\n**What happened:** importlib.abc.Traversable was removed in Python 3.14 and moved to importlib.resources.abc. MLflow hadn't been updated yet.\n\nThe cool part: Claude Code actually found and fixed this bug by itself inside the sandbox, it patched `skill_installer.py`\n\nautomatically.\n\nFix: Apply a `sed`\n\npatch in the install hook:\n\n``` python\nsed -i 's/from importlib.abc import Traversable/from importlib.resources.abc import Traversable/' \\\n  /home/agent/.local/lib/python3.14/site-packages/mlflow/assistant/skill_installer.py\n```\n\n**Error 5:** Background process dies during startup\n\nsetsid: failed to execute mlflow: No such file or directory\n\n**What happened:** Two issues at once:\n\nThe `PATH`\n\nis not set during startup hooks, so `mlflow`\n\ncan't be found by name\n\nBackground processes started without `setsid`\n\ndie when the startup session ends\n\nFix: Use the absolute path and always prefix with setsid:\n\n```\n# Wrong\nnohup mlflow server ... &\n\n# Correct\nsetsid /home/agent/.local/bin/mlflow server ... > /home/agent/.mlflow/server.log 2>&1 &\n```\n\n**Error 6:** MLflow directory doesn't exist at startup\n\n**What happened:** MLflow tried to create its database before the artifact directory existed, and failed silently.\n\nFix: Create the directory in the same startup command, before MLflow runs:\n\n```\nmkdir -p /home/agent/.mlflow/artifacts && setsid /home/agent/.local/bin/mlflow server ...\n```\n\n**Key Takeaways**\n\n```\n   sbx exec <sandbox> -- cat /var/log/sbx-kit-startup.log\n   sbx exec <sandbox> -- cat /home/agent/.mlflow/server.log\ndocker run --rm -u '1000' 'docker/sandbox-templates:claude-code-docker' \\\n     sh -c 'your install command here'\n```\n\n**Try It Yourself**\n\nThe kit is live on Docker Hub. Just run:\n\n```\nsbx run \\\n  --kit docker.io/yharyarias/mlops-experiment-agent-kit:latest \\\n  --name mlops-sandbox \\\n  claude\n```\n\nMLflow will start automatically on port 5000. Then you can prompt Claude Code with things like:\n\n*Run a baseline logistic regression on the iris dataset and log the results to MLflow\nCompare the last 3 MLflow runs by accuracy\nRegister the best model to the Model Registry*\n\nLinks", "url": "https://wpnews.pro/news/i-set-up-my-first-docker-sbx-kit-and-here-s-how-i-did-it", "canonical_source": "https://dev.to/yhary_arias/i-set-up-my-first-docker-sbx-kit-and-heres-how-i-did-it-b9a", "published_at": "2026-09-03 20:22:23+00:00", "updated_at": "2026-09-03 20:54:25.085161+00:00", "lang": "en", "topics": ["developer-tools", "mlops", "ai-agents"], "entities": ["Yhary", "Docker", "MLflow", "Claude Code", "Docker Captains"], "alternates": {"html": "https://wpnews.pro/news/i-set-up-my-first-docker-sbx-kit-and-here-s-how-i-did-it", "markdown": "https://wpnews.pro/news/i-set-up-my-first-docker-sbx-kit-and-here-s-how-i-did-it.md", "text": "https://wpnews.pro/news/i-set-up-my-first-docker-sbx-kit-and-here-s-how-i-did-it.txt", "jsonld": "https://wpnews.pro/news/i-set-up-my-first-docker-sbx-kit-and-here-s-how-i-did-it.jsonld"}}