<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Koti Vellanki</title>
    <description>The latest articles on DEV Community by Koti Vellanki (@vellanki).</description>
    <link>https://dev.to/vellanki</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2512044%2F06940f5c-98c6-471d-850e-cc946bb0a0d2.jpg</url>
      <title>DEV Community: Koti Vellanki</title>
      <link>https://dev.to/vellanki</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vellanki"/>
    <language>en</language>
    <item>
      <title>Migration, Production Adoption, Hardened Packages &amp; Real-World Patterns</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Thu, 20 Aug 2026 12:28:08 +0000</pubDate>
      <link>https://dev.to/vellanki/migration-production-adoption-hardened-packages-real-world-patterns-hk1</link>
      <guid>https://dev.to/vellanki/migration-production-adoption-hardened-packages-real-world-patterns-hk1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Docker Hardened Images Series — Blog 5 (Final)&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;This final blog turns everything you have learned into a practical adoption plan.&lt;/p&gt;

&lt;p&gt;You will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Walk through a realistic migration of an existing Dockerfile&lt;/li&gt;
&lt;li&gt;Use a clear migration checklist so nothing is missed&lt;/li&gt;
&lt;li&gt;Understand when Community is enough and when Select/Enterprise becomes useful&lt;/li&gt;
&lt;li&gt;Get a short overview of hardened system packages&lt;/li&gt;
&lt;li&gt;Leave with a production checklist and honest limitations&lt;/li&gt;
&lt;li&gt;See how Docker Hardened Images and Docker Sandboxes fit together&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Pulling a DHI and building a new service is easy.&lt;br&gt;&lt;br&gt;
Migrating an existing production service without breaking it is the real work.&lt;/p&gt;

&lt;p&gt;Most failures happen because of the same few issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing shell or package manager in the runtime stage&lt;/li&gt;
&lt;li&gt;Application cannot run as non-root&lt;/li&gt;
&lt;li&gt;Trying to bind to a privileged port&lt;/li&gt;
&lt;li&gt;Forgetting that secrets and tools do not belong in the final image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog gives you the checklist and the judgement to avoid those problems.&lt;/p&gt;
&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Full Hardened Images series (Blogs 1–4) completed&lt;/li&gt;
&lt;li&gt;At least one existing Dockerfile you can migrate (we will use a typical example)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Migration Checklist (Print This)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;th&gt;Why it fails if ignored&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Base image&lt;/td&gt;
&lt;td&gt;Change &lt;code&gt;FROM&lt;/code&gt; to a matching &lt;code&gt;dhi.io/...&lt;/code&gt; variant&lt;/td&gt;
&lt;td&gt;Still using the old attack surface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Package management&lt;/td&gt;
&lt;td&gt;Install only in &lt;code&gt;*-dev&lt;/code&gt; stage, copy artefacts&lt;/td&gt;
&lt;td&gt;Runtime image has no package manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell commands&lt;/td&gt;
&lt;td&gt;Move all &lt;code&gt;RUN&lt;/code&gt; that need a shell into the build stage&lt;/td&gt;
&lt;td&gt;Runtime image has no shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-root user&lt;/td&gt;
&lt;td&gt;Ensure files are readable/executable by the non-root user (usually UID 65532)&lt;/td&gt;
&lt;td&gt;Permission denied at runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ports&lt;/td&gt;
&lt;td&gt;Listen on ≥ 1025 inside the container&lt;/td&gt;
&lt;td&gt;Non-root cannot bind to privileged ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLS certificates&lt;/td&gt;
&lt;td&gt;Remove manual &lt;code&gt;ca-certificates&lt;/code&gt; install steps&lt;/td&gt;
&lt;td&gt;Already present in DHI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ENTRYPOINT / CMD&lt;/td&gt;
&lt;td&gt;Inspect the DHI and adjust if needed&lt;/td&gt;
&lt;td&gt;Unexpected default entrypoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-stage&lt;/td&gt;
&lt;td&gt;Always use &lt;code&gt;-dev&lt;/code&gt; for build, pure runtime for final&lt;/td&gt;
&lt;td&gt;Final image becomes large and less secure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secrets&lt;/td&gt;
&lt;td&gt;Never bake secrets into layers&lt;/td&gt;
&lt;td&gt;They will be visible in the image history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy check&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;docker scout policy ... --policy-bundle dhi/policies:latest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Image may not meet the hardened standard&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 1 — Realistic Migration Example
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before (typical official image)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.13-slim&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (proper DHI multi-stage)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;dhi.io/python:3.13-dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv /app/venv
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/app/venv/bin:$PATH"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; dhi.io/python:3.13&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/app/venv/bin:$PATH"&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/venv /app/venv&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build tools only in the first stage&lt;/li&gt;
&lt;li&gt;Final image is minimal and non-root&lt;/li&gt;
&lt;li&gt;Port moved above 1024&lt;/li&gt;
&lt;li&gt;No unnecessary packages left behind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build and test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; migrated-app &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 migrated-app
docker scout policy migrated-app &lt;span class="nt"&gt;--policy-bundle&lt;/span&gt; dhi/policies:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Community vs Select vs Enterprise (Honest View)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Community (free)&lt;/th&gt;
&lt;th&gt;Select&lt;/th&gt;
&lt;th&gt;Enterprise&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Minimal, near-zero CVE images&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signed SBOM + SLSA provenance&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FIPS / STIG variants&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7-day SLA for critical/high CVEs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customisations&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Limited (5)&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardened system packages repository&lt;/td&gt;
&lt;td&gt;Public only&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Full access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extended lifecycle (post-EOL)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (add-on)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Practical recommendation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Start with Community for almost every service.&lt;br&gt;&lt;br&gt;
Move to Select/Enterprise only when you have a concrete compliance (FIPS/STIG) or SLA requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Hardened System Packages (Short Overview)
&lt;/h2&gt;

&lt;p&gt;Docker builds many system packages from source and signs them.&lt;br&gt;&lt;br&gt;
These packages are already inside the DHI base images.&lt;/p&gt;

&lt;p&gt;Community users can also configure the public hardened package repositories in their own Dockerfiles if they need extra packages during a build stage.&lt;br&gt;&lt;br&gt;
Enterprise customers get a private repository with broader access and longer support.&lt;/p&gt;

&lt;p&gt;For most applications you will never need to touch this — the base image already contains the hardened packages you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Production Adoption Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Every new service starts from a DHI base&lt;/li&gt;
&lt;li&gt;[ ] Existing services have a migration ticket and a deadline&lt;/li&gt;
&lt;li&gt;[ ] Multi-stage pattern is the team standard&lt;/li&gt;
&lt;li&gt;[ ] CI always authenticates to &lt;code&gt;dhi.io&lt;/code&gt; and runs the DHI policy bundle&lt;/li&gt;
&lt;li&gt;[ ] Policy failures block the merge&lt;/li&gt;
&lt;li&gt;[ ] Images are scanned and attested in the pipeline&lt;/li&gt;
&lt;li&gt;[ ] Non-root and high ports are verified in staging&lt;/li&gt;
&lt;li&gt;[ ] Team knows how to use &lt;code&gt;docker scout attest&lt;/code&gt; and &lt;code&gt;docker scout policy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Old official base images are removed from the allowed list&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5 — Combining with Docker Sandboxes
&lt;/h2&gt;

&lt;p&gt;The two series fit together cleanly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers use &lt;strong&gt;Docker Sandboxes&lt;/strong&gt; so AI agents can work safely&lt;/li&gt;
&lt;li&gt;Those same agents (or the CI) build application images &lt;strong&gt;from Docker Hardened Images&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The final artefact that reaches production is both built in an isolated environment and based on a hardened, attested base&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you strong isolation during development and strong supply-chain guarantees at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Realistic Limitations (Be Honest)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Runtime images have no shell → debugging needs &lt;code&gt;docker debug&lt;/code&gt;, a sidecar, or a temporary &lt;code&gt;-dev&lt;/code&gt; container&lt;/li&gt;
&lt;li&gt;Some legacy applications still expect to run as root or bind to port 80 → you must change the application or use a workaround&lt;/li&gt;
&lt;li&gt;Community tier has no SLA — patch timing follows upstream + Docker’s cadence&lt;/li&gt;
&lt;li&gt;Very exotic native dependencies may not yet exist as hardened packages&lt;/li&gt;
&lt;li&gt;Organisation governance (for Sandboxes) and DHI Select/Enterprise are separate paid offerings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Migration Pitfalls
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Leaving a &lt;code&gt;USER root&lt;/code&gt; statement “just in case”&lt;/li&gt;
&lt;li&gt;Installing packages in the final stage&lt;/li&gt;
&lt;li&gt;Forgetting to update health-check or readiness probes that assumed port 80&lt;/li&gt;
&lt;li&gt;Assuming every tag has a &lt;code&gt;-dev&lt;/code&gt; variant (check the catalog)&lt;/li&gt;
&lt;li&gt;Migrating only the base image and leaving the rest of the Dockerfile unchanged&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Most common cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exec: "bash": executable not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runtime image used for a shell command&lt;/td&gt;
&lt;td&gt;Move the command to a &lt;code&gt;-dev&lt;/code&gt; stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission denied&lt;/td&gt;
&lt;td&gt;Non-root user cannot write&lt;/td&gt;
&lt;td&gt;Fix ownership with &lt;code&gt;COPY --chown&lt;/code&gt; or in builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address already in use / bind error&lt;/td&gt;
&lt;td&gt;Trying to use port &amp;lt; 1024&lt;/td&gt;
&lt;td&gt;Change application to listen on ≥ 1025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy fails on “shell present”&lt;/td&gt;
&lt;td&gt;Final stage still has a shell&lt;/td&gt;
&lt;td&gt;Use pure runtime variant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cannot pull in CI&lt;/td&gt;
&lt;td&gt;Missing &lt;code&gt;docker login dhi.io&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add the login step shown in Blog 4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi migrated-app 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Learned in the Whole Hardened Images Series
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why typical images need hardening and how to pull the first DHI
&lt;/li&gt;
&lt;li&gt;Anatomy of runtime vs dev variants and how to build real applications
&lt;/li&gt;
&lt;li&gt;SBOMs, provenance, signatures and how to enforce the same policies
&lt;/li&gt;
&lt;li&gt;Production multi-stage Dockerfiles + CI gates
&lt;/li&gt;
&lt;li&gt;Migration checklist, tier decisions, limitations and how to adopt safely&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You now have a complete, practical path from “I have heard of hardened images” to “I can migrate and run them in production with confidence”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Combined Recommendation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Docker Sandboxes&lt;/strong&gt; for any AI coding agent work
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Docker Hardened Images&lt;/strong&gt; as the base for every application image
&lt;/li&gt;
&lt;li&gt;Enforce both with clear team defaults and automated checks
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That combination gives you strong isolation during development and a minimal, attested, continuously maintained runtime in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/migration/checklist/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/migration/checklist/&lt;/a&gt; — official migration checklist
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/&lt;/a&gt; — tiers and feature comparison
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/hardened-packages/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/hardened-packages/&lt;/a&gt; — system packages
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/use/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/use/&lt;/a&gt; — adoption considerations
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/migration/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/migration/&lt;/a&gt; — language-specific migration guides
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All technical claims verified against current official documentation (August 2026).&lt;/p&gt;




</description>
      <category>ai</category>
      <category>containers</category>
      <category>docker</category>
      <category>security</category>
    </item>
    <item>
      <title>Multi-Stage Best Practices + CI/CD Integration</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Thu, 20 Aug 2026 12:23:32 +0000</pubDate>
      <link>https://dev.to/vellanki/multi-stage-best-practices-cicd-integration-2b76</link>
      <guid>https://dev.to/vellanki/multi-stage-best-practices-cicd-integration-2b76</guid>
      <description>&lt;p&gt;&lt;strong&gt;Docker Hardened Images Series — Blog 4&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A clean, production-ready multi-stage Dockerfile that uses Docker Hardened Images correctly&lt;/li&gt;
&lt;li&gt;A GitHub Actions workflow that:

&lt;ul&gt;
&lt;li&gt;Authenticates to &lt;code&gt;dhi.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Builds the image&lt;/li&gt;
&lt;li&gt;Runs the official DHI policy checks&lt;/li&gt;
&lt;li&gt;Fails the pipeline if the image does not meet the hardened standard&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This turns everything from the previous blogs into a repeatable, automated practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;A good local Dockerfile is not enough. In real teams the image must be built the same way every time, and security checks must be automatic.&lt;/p&gt;

&lt;p&gt;If the policy check only runs on a developer laptop, it will be skipped under pressure.&lt;br&gt;&lt;br&gt;
If the CI cannot pull from &lt;code&gt;dhi.io&lt;/code&gt;, the build will fail randomly.&lt;br&gt;&lt;br&gt;
If the Dockerfile still uses a fat base image or runs as root, the hardened base loses most of its value.&lt;/p&gt;

&lt;p&gt;This blog closes those gaps.&lt;/p&gt;
&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blogs 1–3 completed&lt;/li&gt;
&lt;li&gt;Basic GitHub Actions knowledge (or any CI that can run Docker)&lt;/li&gt;
&lt;li&gt;A GitHub repository (public or private) where you can add secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 1 — Production Multi-Stage Dockerfile (Best Practices)
&lt;/h2&gt;

&lt;p&gt;Here is the pattern I recommend for most language frameworks (Python example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Build stage ----------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;dhi.io/python:3.13-dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 \&lt;/span&gt;
    PYTHONUNBUFFERED=1 \
    PATH="/app/venv/bin:$PATH"

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Create isolated environment&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv /app/venv

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/root/.cache/pip &lt;span class="se"&gt;\
&lt;/span&gt;    pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# ---------- Runtime stage ----------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; dhi.io/python:3.13&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1 \&lt;/span&gt;
    PYTHONUNBUFFERED=1 \
    PATH="/app/venv/bin:$PATH"

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy only what is required&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/venv /app/venv&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;

&lt;span class="c"&gt;# Runtime image already defaults to non-root&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key rules this Dockerfile follows
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-dev&lt;/code&gt; only in build stage&lt;/td&gt;
&lt;td&gt;Tools exist only where they are needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pure runtime variant in final stage&lt;/td&gt;
&lt;td&gt;Minimal attack surface, non-root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Virtual environment (or equivalent)&lt;/td&gt;
&lt;td&gt;Keeps runtime clean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache mount for pip/npm&lt;/td&gt;
&lt;td&gt;Faster CI builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No secrets in layers&lt;/td&gt;
&lt;td&gt;Secrets must come from the runtime environment or a secret store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explicit &lt;code&gt;CMD&lt;/code&gt; in exec form&lt;/td&gt;
&lt;td&gt;No shell required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Multi-stage Flow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ioreid7bhdnr9h3ckm2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ioreid7bhdnr9h3ckm2.png" alt="multi stage flow" width="634" height="1300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Everything that needs a package manager or compiler stays in the first stage.&lt;br&gt;&lt;br&gt;
Only the final artefacts are copied into the hardened runtime image.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 — Local Verification Before CI
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-dhi-app:local &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 my-dhi-app:local

&lt;span class="c"&gt;# Policy check&lt;/span&gt;
docker scout policy my-dhi-app:local &lt;span class="nt"&gt;--policy-bundle&lt;/span&gt; dhi/policies:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Fix any failures before you push.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 — GitHub Actions Workflow (Complete Example)
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/dhi-build.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and check with Docker Hardened Images&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-dhi-app:${{ github.sha }}&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Log in to Docker Hub (for Scout + policy bundle)&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_PAT }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Log in to dhi.io&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dhi.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_PAT }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build image&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Evaluate against DHI policies&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/scout-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.IMAGE_NAME }}&lt;/span&gt;
          &lt;span class="na"&gt;policy-bundle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dhi/policies:latest&lt;/span&gt;
          &lt;span class="na"&gt;exit-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;          &lt;span class="c1"&gt;# fail the job if any policy is violated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Required GitHub Secrets
&lt;/h3&gt;

&lt;p&gt;In the repository settings → Secrets and variables → Actions, add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_USER&lt;/code&gt; — your Docker Hub username
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_PAT&lt;/code&gt; — a personal access token with read access (and write if you later push images)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What the pipeline does
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Checks out the code
&lt;/li&gt;
&lt;li&gt;Logs into both Docker Hub and &lt;code&gt;dhi.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Builds the multi-stage image (with cache)
&lt;/li&gt;
&lt;li&gt;Runs the official DHI policy bundle
&lt;/li&gt;
&lt;li&gt;Fails the pull request or push if the image does not meet the hardened standard&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4 — Optional Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Push the image only on &lt;code&gt;main&lt;/code&gt; after the policy check passes
&lt;/li&gt;
&lt;li&gt;Pin the policy-bundle digest instead of &lt;code&gt;:latest&lt;/code&gt; for reproducibility
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;docker scout cves&lt;/code&gt; as an extra informational step
&lt;/li&gt;
&lt;li&gt;Use OIDC login (available for Docker organisations) instead of long-lived PATs when possible&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let’s Break It
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Temporarily change the final stage back to a normal &lt;code&gt;python:3.13&lt;/code&gt; image.
&lt;/li&gt;
&lt;li&gt;Push the change.
&lt;/li&gt;
&lt;li&gt;Watch the policy step fail (root user, extra packages, missing attestations, etc.).
&lt;/li&gt;
&lt;li&gt;Revert to the DHI runtime stage and see the pipeline go green again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In a real team I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make this workflow the default for every service repository
&lt;/li&gt;
&lt;li&gt;Treat a failing DHI policy check the same way as a failing unit test
&lt;/li&gt;
&lt;li&gt;Keep the multi-stage Dockerfile pattern in a shared template or cookiecutter
&lt;/li&gt;
&lt;li&gt;Review any exception that needs a &lt;code&gt;-dev&lt;/code&gt; image in the final stage (almost never justified)
&lt;/li&gt;
&lt;li&gt;Combine this with the Sandboxes series so agents themselves build on hardened bases&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The CI runner must be able to authenticate to &lt;code&gt;dhi.io&lt;/code&gt;; otherwise builds become flaky
&lt;/li&gt;
&lt;li&gt;Long-lived PATs should be rotated and scoped as tightly as possible
&lt;/li&gt;
&lt;li&gt;Policy evaluation happens on the final image, not just the base — that is what matters
&lt;/li&gt;
&lt;li&gt;Caching is fine; it does not weaken the policy check&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Logging into Docker Hub but forgetting &lt;code&gt;dhi.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Using a runtime image in the build stage (missing tools)
&lt;/li&gt;
&lt;li&gt;Leaving &lt;code&gt;exit-code: false&lt;/code&gt; so the policy check never fails the build
&lt;/li&gt;
&lt;li&gt;Baking secrets into the image layers
&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;:latest&lt;/code&gt; tags for base images in production Dockerfiles&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unauthorized: dhi.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing or wrong login to dhi.io&lt;/td&gt;
&lt;td&gt;Add the second &lt;code&gt;docker/login-action&lt;/code&gt; step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy step always green&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;exit-code&lt;/code&gt; not set to true&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;exit-code: true&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build fails on &lt;code&gt;pip&lt;/code&gt; / &lt;code&gt;npm&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Runtime image used for build&lt;/td&gt;
&lt;td&gt;Switch build stage to &lt;code&gt;*-dev&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache not helping&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;cache-from&lt;/code&gt; / &lt;code&gt;cache-to&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add the GHA cache lines shown above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy bundle not found&lt;/td&gt;
&lt;td&gt;Not logged into Docker Hub&lt;/td&gt;
&lt;td&gt;Ensure Hub login happens before the scout step&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;No permanent resources are created on your machine.&lt;br&gt;&lt;br&gt;
In GitHub you can delete the workflow file or the test branch when finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multi-stage with a &lt;code&gt;-dev&lt;/code&gt; build stage + pure runtime stage is the standard pattern
&lt;/li&gt;
&lt;li&gt;CI must authenticate to both Docker Hub and &lt;code&gt;dhi.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The official DHI policy bundle can be enforced automatically on every build
&lt;/li&gt;
&lt;li&gt;A failing policy check should block the merge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have a complete, automated path from code to a hardened production image.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In the final blog (Blog 5) we cover migration of existing applications, production adoption checklist, hardened system packages overview, known limitations, and how to combine Docker Hardened Images with the Sandboxes series for a full secure development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/use/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/use/&lt;/a&gt; — multi-stage patterns and CI notes
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/policies/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/policies/&lt;/a&gt; — policy bundle and GitHub Actions example
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/build/cache/backends/gha/" rel="noopener noreferrer"&gt;https://docs.docker.com/build/cache/backends/gha/&lt;/a&gt; — GitHub Actions cache
&lt;/li&gt;
&lt;li&gt;Official Scout GitHub Action documentation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and workflow patterns verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cicd</category>
      <category>docker</category>
      <category>containers</category>
    </item>
    <item>
      <title>SBOM, Provenance, Signatures, Scanning &amp; DHI Policies</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Tue, 18 Aug 2026 02:59:45 +0000</pubDate>
      <link>https://dev.to/vellanki/sbom-provenance-signatures-scanning-dhi-policies-3mjk</link>
      <guid>https://dev.to/vellanki/sbom-provenance-signatures-scanning-dhi-policies-3mjk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Docker Hardened Images Series — Blog 3&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inspect the signed attestations that every Docker Hardened Image carries&lt;/li&gt;
&lt;li&gt;Understand SBOM, provenance, signatures and VEX in practical terms&lt;/li&gt;
&lt;li&gt;Verify that an image really comes from Docker and has not been tampered with&lt;/li&gt;
&lt;li&gt;Scan your own application image&lt;/li&gt;
&lt;li&gt;Evaluate any image against the same policy bar that DHI images meet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the supply-chain transparency part of hardened images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;A minimal image with few CVEs is good.&lt;br&gt;&lt;br&gt;
Knowing &lt;strong&gt;exactly what is inside it&lt;/strong&gt;, &lt;strong&gt;how it was built&lt;/strong&gt;, and being able to &lt;strong&gt;prove&lt;/strong&gt; those claims is better.&lt;/p&gt;

&lt;p&gt;Docker Hardened Images ship with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A signed Software Bill of Materials (SBOM)&lt;/li&gt;
&lt;li&gt;SLSA Build Level 3 provenance&lt;/li&gt;
&lt;li&gt;Cryptographic signatures&lt;/li&gt;
&lt;li&gt;VEX statements (which vulnerabilities are actually exploitable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can inspect and verify all of this yourself. You can also hold your own images to the same standard using the official DHI policy bundle.&lt;/p&gt;
&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blogs 1 and 2 completed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker login dhi.io&lt;/code&gt; already done&lt;/li&gt;
&lt;li&gt;Docker Scout available (included with Docker Desktop)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Core Concepts (Simple Explanations)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What it answers&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SBOM&lt;/td&gt;
&lt;td&gt;What packages and versions are inside the image?&lt;/td&gt;
&lt;td&gt;Transparency &amp;amp; vulnerability matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provenance&lt;/td&gt;
&lt;td&gt;Who built it, from which source, with which steps?&lt;/td&gt;
&lt;td&gt;Tamper resistance (SLSA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature&lt;/td&gt;
&lt;td&gt;Has this attestation been altered since Docker signed it?&lt;/td&gt;
&lt;td&gt;Trust&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VEX&lt;/td&gt;
&lt;td&gt;Is this CVE actually exploitable in this image?&lt;/td&gt;
&lt;td&gt;Reduce noise in scans&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Supply-chain Metadata Flow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg36xijregcfobbaln1cj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg36xijregcfobbaln1cj.png" alt="supply chain flow" width="799" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Docker builds the image and attaches signed attestations.&lt;br&gt;&lt;br&gt;
You (or your CI) can later pull those attestations and verify the signatures.&lt;br&gt;&lt;br&gt;
If the signature is valid, you know the metadata has not been changed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1 — List Attestations on a DHI
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout attest list dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi-lab docker scout attest list dhi.io/python:3.13
    i New version 1.24.0 available &lt;span class="o"&gt;(&lt;/span&gt;installed version is 1.23.1&lt;span class="o"&gt;)&lt;/span&gt; at https://github.com/docker/scout-cli
    ✓ SBOM obtained from attestation, 100 packages found
    ✓ Provenance obtained from attestation


&lt;span class="c"&gt;## Overview&lt;/span&gt;

              │                    Analyzed Image                     
──────────────┼───────────────────────────────────────────────────────
 Subject      │  dhi.io/python:3.13                                   
   digest     │  24a8822f76af                                         
   platform   │ linux/arm64                                           
   provenance │ https://github.com/docker-hardened-images/definitions 
              │  5fb32dbb43253700792f258345f151035442841c             
   size       │ 26 MB                                                 
   packages   │ 100                                                   


&lt;span class="c"&gt;## Attestations&lt;/span&gt;

https://cyclonedx.org/bom/v1.6  CycloneDX SBOM  
  sha256:cd84b3cc8ea0476ce46f0332d80c32559fb027a75c4e54c0e53d9e68d9c6335f

 Name                        │  dhi.io/python                             
   digest                    │  cd84b3cc8ea0                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://cyclonedx.org/bom/v1.6             


https://docker.com/dhi/changelog/v0.1  Changelog  
  sha256:8d5f5d332cd3f4849aee647a6e3cd3656273364c8330b73a3204dd15ec2fb615

 Name                        │  dhi.io/python                             
   digest                    │  8d5f5d332cd3                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://docker.com/dhi/changelog/v0.1      


https://docker.com/dhi/source/v0.1
  sha256:ff88fffcb43f8b1f1c097f03b3b7d0625456ee2b6c94c43609442ab77cdc07fa

 Name                        │  dhi.io/python                             
   digest                    │  ff88fffcb43f                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://docker.com/dhi/source/v0.1         


https://in-toto.io/attestation/vulns/v0.2  &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt; vulnerabilities  
  sha256:37889af6f7e43d2c7990b75801624071f451bc62037faf64f2268fb00853cdea

 Name                        │  dhi.io/python                             
   digest                    │  37889af6f7e4                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://in-toto.io/attestation/vulns/v0.2  


https://openvex.dev/ns/v0.2.0  OpenVEX  
  sha256:613cdd6ab043d43ca77ffcf90aba336899a03d51a84c93f0bd5c620d0f7ca37a

 Name                          │  dhi.io/python                             
   digest                      │  613cdd6ab043                              
                               │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                    │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;                 │ application/vnd.in-toto+json               
 Annotations                   │                                            
   com.docker.scout.vex.source │ exceptions                                 
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type   │ https://openvex.dev/ns/v0.2.0              


https://scout.docker.com/provenance/v0.1  Scout provenance  
  sha256:6a19c4b87c703e42ae53005562088c95e1a94d6c72dd07d47e064935a3b9bfea

 Name                        │  dhi.io/python                             
   digest                    │  6a19c4b87c70                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://scout.docker.com/provenance/v0.1   


https://scout.docker.com/sbom/v0.1  Scout SBOM  
  sha256:586bc87d9734f37b0733973179241ec2f955fbed732dc38522e421f1f4c9b0d1

 Name                        │  dhi.io/python                             
   digest                    │  586bc87d9734                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://scout.docker.com/sbom/v0.1         


https://scout.docker.com/secrets/v0.1  Scout secret scan  
  sha256:d20496aab3d5e4b3a50e091bac7c0e7ddb1f6909c23e9ea0c0c3ae8e192abd31

 Name                        │  dhi.io/python                             
   digest                    │  d20496aab3d5                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://scout.docker.com/secrets/v0.1      


https://scout.docker.com/tests/v0.1  Scout &lt;span class="nb"&gt;test &lt;/span&gt;report  
  sha256:7a2f5e93a4be1d0eb3c5ed966104c6fb193b87de3a20b7cb536bd10e4418daf2

 Name                        │  dhi.io/python                             
   digest                    │  7a2f5e93a4be                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://scout.docker.com/tests/v0.1        


https://scout.docker.com/virus/v0.1  Scout virus/malware report  
  sha256:55aa6a3cd931cfe60c4abff38787668580a1c425e6f385b6a8e9b04dd3a47931

 Name                        │  dhi.io/python                             
   digest                    │  55aa6a3cd931                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://scout.docker.com/virus/v0.1        


https://scout.docker.com/vulnerabilities/v0.1  Scout vulnerabilities  
  sha256:85ea5c8e021775fcd6933bfc712dd45b0ba63275e07b4bc44b1be440bf194a38

 Name                        │  dhi.io/python                                
   digest                    │  85ea5c8e0217                                 
                             │                                               
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json    
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json                  
 Annotations                 │                                               
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://scout.docker.com/vulnerabilities/v0.1 


https://slsa.dev/provenance/v0.2  SLSA provenance  
  sha256:43283e5c83d2625542ee63797cf9cd3d0ee94e6012c6b5b8ed8c0a482e698156

 Name                        │  dhi.io/python                             
   digest                    │  43283e5c83d2                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://slsa.dev/provenance/v0.2           


https://slsa.dev/provenance/v1  SLSA provenance  
  sha256:22870de00ae3918dd426c22dff0628620586e50d97f14df52c846f266a232bb2

 Name                        │  dhi.io/python                             
   digest                    │  22870de00ae3                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://slsa.dev/provenance/v1             


https://slsa.dev/verification_summary/v1  SLSA verification summary  
  sha256:63485f84f89c1534b60e9a24b9bef9d4c10bba652bd9e351bb7861742b5a9c3c

 Name                        │  dhi.io/python                             
   digest                    │  63485f84f89c                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://slsa.dev/verification_summary/v1   


https://spdx.dev/Document  SPDX SBOM  
  sha256:91c071b3faf69247626618fcf7cc758415553ab6edb394b50131cd50f3d36180

 Name                        │  dhi.io/python                             
   digest                    │  91c071b3faf6                              
                             │                                            
 Media &lt;span class="nb"&gt;type&lt;/span&gt;                  │ application/vnd.oci.image.manifest.v1+json 
 Artifact &lt;span class="nb"&gt;type&lt;/span&gt;               │ application/vnd.in-toto+json               
 Annotations                 │                                            
   &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nt"&gt;-toto&lt;/span&gt;.io/predicate-type │ https://spdx.dev/Document                  

➜  dhi-lab 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see several attestations (SBOM, provenance, vulnerability reports, etc.).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If the image is already present locally, prefix the name with &lt;code&gt;registry://&lt;/code&gt;:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout attest list registry://dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2 — Retrieve and Verify an SBOM
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout attest get &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--predicate-type&lt;/span&gt; https://scout.docker.com/sbom/v0.1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--verify&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If verification succeeds you will see confirmation that the signature is valid, plus the equivalent &lt;code&gt;cosign&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You can also ask for a CycloneDX SBOM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout attest get &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--predicate-type&lt;/span&gt; https://cyclonedx.org/bom/v1.6 &lt;span class="se"&gt;\&lt;/span&gt;
  dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout sbom dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Scan Your Own Application Image
&lt;/h2&gt;

&lt;p&gt;Rebuild the small Flask app from Blog 2 (or any image you have):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
&lt;span class="c"&gt;#navigate to dhi-lab&lt;/span&gt;
dhi-lab   &lt;span class="c"&gt;# or recreate the simple app&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-dhi-app:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi-lab docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-dhi-app:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] Building 5.4s &lt;span class="o"&gt;(&lt;/span&gt;19/19&lt;span class="o"&gt;)&lt;/span&gt; FINISHED                                                                                                                                                        docker:desktop-linux
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile                                                                                                                                                      0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 728B                                                                                                                                                                      0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; resolve image config &lt;span class="k"&gt;for &lt;/span&gt;docker-image://docker.io/docker/dockerfile:1                                                                                                                                 1.7s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;auth] docker/dockerfile:pull token &lt;span class="k"&gt;for &lt;/span&gt;registry-1.docker.io                                                                                                                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED docker-image://docker.io/docker/dockerfile:1@sha256:ecfaec9ed6d810b56388c508f4121597bfbba70d41a6dfeee4d8cad5f295fc32                                                                           0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; resolve docker.io/docker/dockerfile:1@sha256:ecfaec9ed6d810b56388c508f4121597bfbba70d41a6dfeee4d8cad5f295fc32                                                                                      0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;dhi.io/python:3.13                                                                                                                                                       2.2s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;dhi.io/python:3.13-dev                                                                                                                                                   3.4s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;auth] python:pull token &lt;span class="k"&gt;for &lt;/span&gt;dhi.io                                                                                                                                                                   0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                                                                                                                                                         0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 2B                                                                                                                                                                           0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;builder 1/5] FROM dhi.io/python:3.13-dev@sha256:b31351cce7a96e52c272b009d9167f62e8ffba0f0531bce7c55b74d580c4363a                                                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; resolve dhi.io/python:3.13-dev@sha256:b31351cce7a96e52c272b009d9167f62e8ffba0f0531bce7c55b74d580c4363a                                                                                             0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build context                                                                                                                                                                         0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 63B                                                                                                                                                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;stage-1 1/4] FROM dhi.io/python:3.13@sha256:5669b3baeccdcd7b189df90aab1616a92bee6a766dc537b90a169268c0ab962c                                                                                         0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; resolve dhi.io/python:3.13@sha256:5669b3baeccdcd7b189df90aab1616a92bee6a766dc537b90a169268c0ab962c                                                                                                 0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-1 2/4] WORKDIR /app                                                                                                                                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;builder 2/5] WORKDIR /app                                                                                                                                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;builder 3/5] RUN python &lt;span class="nt"&gt;-m&lt;/span&gt; venv /app/venv                                                                                                                                                     0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;builder 4/5] COPY requirements.txt &lt;span class="nb"&gt;.&lt;/span&gt;                                                                                                                                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;builder 5/5] RUN pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt                                                                                                                               0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-1 3/4] COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder /app/venv /app/venv                                                                                                                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;stage-1 4/4] COPY app.py &lt;span class="nb"&gt;.&lt;/span&gt;                                                                                                                                                                    0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting to image                                                                                                                                                                                    0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                                                                                                                                                   0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting manifest sha256:2d981cdcc89cc5122fae8386ac9291917ec2a6e1c3b0352025185161a8ab6e68                                                                                                         0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting config sha256:e9b998ce0fe58f26568f8371b1acfadeb88b6fdb621ef737cc3860e2ab98c1d2                                                                                                           0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting attestation manifest sha256:31ed97bd0834de4b09ba5d46c4245e340119ad852f6a4ee702c537cbfdc87ff6                                                                                             0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting manifest list sha256:e8c9377134bb24982fd0fb771881c290c9a3545ba0177b0fb5041db927991aa3                                                                                                    0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/my-dhi-app:v1                                                                                                                                                          0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; unpacking to docker.io/library/my-dhi-app:v1                                                                                                                                                       0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/pu3qzidtva9nrroqyhv7mlsm5
➜  dhi-lab 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scan it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout cves my-dhi-app:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi-lab docker scout cves my-dhi-app:v1
    i New version 1.24.0 available &lt;span class="o"&gt;(&lt;/span&gt;installed version is 1.23.1&lt;span class="o"&gt;)&lt;/span&gt; at https://github.com/docker/scout-cli
    ✓ Image stored &lt;span class="k"&gt;for &lt;/span&gt;indexing
    ✓ Indexed 107 packages
    ✓ Provenance obtained from attestation
    ✗ Detected 8 vulnerable packages with a total of 17 vulnerabilities


&lt;span class="c"&gt;## Overview&lt;/span&gt;

                   │       Analyzed Image        
───────────────────┼─────────────────────────────
 Target            │  my-dhi-app:v1              
   digest          │  e8c9377134bb               
   platform        │ linux/arm64                 
   vulnerabilities │    0C     3H     2M    12L  
   size            │ 31 MB                       
   packages        │ 107                         


&lt;span class="c"&gt;## Packages and Vulnerabilities&lt;/span&gt;

   0C     2H     0M     0L  msgpack 1.1.2
pkg:pypi/msgpack@1.1.2

    ✗ HIGH GHSA-6v7p-g79w-8964 &lt;span class="o"&gt;[&lt;/span&gt;Use After Free]
      https://scout.docker.com/v/GHSA-6v7p-g79w-8964
      Affected range : &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt;1.2.0                                      
      Fixed version  : 1.2.1                                        
      CVSS Score     : 7.5                                          
      CVSS Vector    : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H 

    ✗ HIGH CVE-2026-57585
      https://scout.docker.com/v/CVE-2026-57585
      Affected range : &amp;lt;1.2.1                                       
      Fixed version  : 1.2.1                                        
      CVSS Score     : 7.5                                          
      CVSS Vector    : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H 


   0C     1H     1M     0L  setuptools 70.3.0
pkg:pypi/setuptools@70.3.0

    ✗ HIGH CVE-2025-47273 &lt;span class="o"&gt;[&lt;/span&gt;Improper Limitation of a Pathname to a Restricted Directory &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Path Traversal'&lt;/span&gt;&lt;span class="o"&gt;)]&lt;/span&gt;
      https://scout.docker.com/v/CVE-2025-47273
      Affected range : &amp;lt;78.1.1                                                             
      Fixed version  : 78.1.1                                                              
      CVSS Score     : 7.7                                                                 
      CVSS Vector    : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P 

    ✗ MEDIUM CVE-2026-59890 &lt;span class="o"&gt;[&lt;/span&gt;Improper Handling of Unicode Encoding]
      https://scout.docker.com/v/CVE-2026-59890
      Affected range : &amp;lt;83.0.0                                      
      Fixed version  : 83.0.0                                       
      CVSS Score     : 6.1                                          
      CVSS Vector    : CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N 


   0C     0H     1M     0L  expat 2.8.2-1~deb13u1+dhi0
pkg:deb/debian/expat@2.8.2-1~deb13u1%2Bdhi0?os_distro&lt;span class="o"&gt;=&lt;/span&gt;trixie&amp;amp;os_name&lt;span class="o"&gt;=&lt;/span&gt;debian&amp;amp;os_version&lt;span class="o"&gt;=&lt;/span&gt;13

    ✗ MEDIUM CVE-2026-72522
      https://scout.docker.com/v/CVE-2026-72522
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 


   0C     0H     0M     7L  glibc 2.41-12+deb13u3+dhi1
pkg:deb/debian/glibc@2.41-12%2Bdeb13u3%2Bdhi1?os_distro&lt;span class="o"&gt;=&lt;/span&gt;trixie&amp;amp;os_name&lt;span class="o"&gt;=&lt;/span&gt;debian&amp;amp;os_version&lt;span class="o"&gt;=&lt;/span&gt;13

    ✗ LOW CVE-2019-9192
      https://scout.docker.com/v/CVE-2019-9192
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2019-1010025
      https://scout.docker.com/v/CVE-2019-1010025
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2019-1010024
      https://scout.docker.com/v/CVE-2019-1010024
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2019-1010023
      https://scout.docker.com/v/CVE-2019-1010023
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2019-1010022
      https://scout.docker.com/v/CVE-2019-1010022
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2018-20796
      https://scout.docker.com/v/CVE-2018-20796
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2010-4756
      https://scout.docker.com/v/CVE-2010-4756
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 


   0C     0H     0M     2L  sqlite3 3.46.1-7+deb13u1+dhi2
pkg:deb/debian/sqlite3@3.46.1-7%2Bdeb13u1%2Bdhi2?os_distro&lt;span class="o"&gt;=&lt;/span&gt;trixie&amp;amp;os_name&lt;span class="o"&gt;=&lt;/span&gt;debian&amp;amp;os_version&lt;span class="o"&gt;=&lt;/span&gt;13

    ✗ LOW CVE-2025-70873
      https://scout.docker.com/v/CVE-2025-70873
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 

    ✗ LOW CVE-2021-45346
      https://scout.docker.com/v/CVE-2021-45346
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 


   0C     0H     0M     1L  openssl 3.5.6-1~deb13u2+dhi1
pkg:deb/debian/openssl@3.5.6-1~deb13u2%2Bdhi1?os_distro&lt;span class="o"&gt;=&lt;/span&gt;trixie&amp;amp;os_name&lt;span class="o"&gt;=&lt;/span&gt;debian&amp;amp;os_version&lt;span class="o"&gt;=&lt;/span&gt;13

    ✗ LOW CVE-2010-0928
      https://scout.docker.com/v/CVE-2010-0928
      Affected range : &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;3.2.1-3 
      Fixed version  : not fixed 


   0C     0H     0M     1L  flask 3.0.3
pkg:pypi/flask@3.0.3

    ✗ LOW CVE-2026-27205 &lt;span class="o"&gt;[&lt;/span&gt;Use of Cache Containing Sensitive Information]
      https://scout.docker.com/v/CVE-2026-27205
      Affected range : &amp;lt;3.1.3                                                          
      Fixed version  : 3.1.3                                                           
      CVSS Score     : 2.3                                                             
      CVSS Vector    : CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N 


   0C     0H     0M     1L  util-linux 2.41.5-0+deb13u1+dhi2
pkg:deb/debian/util-linux@2.41.5-0%2Bdeb13u1%2Bdhi2?os_distro&lt;span class="o"&gt;=&lt;/span&gt;trixie&amp;amp;os_name&lt;span class="o"&gt;=&lt;/span&gt;debian&amp;amp;os_version&lt;span class="o"&gt;=&lt;/span&gt;13

    ✗ LOW CVE-2022-0563
      https://scout.docker.com/v/CVE-2022-0563
      Affected range : &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;0        
      Fixed version  : not fixed 



17 vulnerabilities found &lt;span class="k"&gt;in &lt;/span&gt;8 packages
  CRITICAL  0  
  HIGH      3  
  MEDIUM    2  
  LOW       12 


What&lt;span class="s1"&gt;'s next:
    View base image update recommendations → docker scout recommendations my-dhi-app:v1

➜  dhi-lab 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare with the pure base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout compare my-dhi-app:v1 &lt;span class="nt"&gt;--to&lt;/span&gt; dhi.io/python:3.13 &lt;span class="nt"&gt;--ignore-unchanged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi-lab docker scout compare my-dhi-app:v1 &lt;span class="nt"&gt;--to&lt;/span&gt; dhi.io/python:3.13 &lt;span class="nt"&gt;--ignore-unchanged&lt;/span&gt;
    i New version 1.24.0 available &lt;span class="o"&gt;(&lt;/span&gt;installed version is 1.23.1&lt;span class="o"&gt;)&lt;/span&gt; at https://github.com/docker/scout-cli
    &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s1"&gt;'docker scout compare'&lt;/span&gt; is experimental and its behaviour might change &lt;span class="k"&gt;in &lt;/span&gt;the future
    ✓ Image stored &lt;span class="k"&gt;for &lt;/span&gt;indexing
    ✓ Indexed 107 packages
    ✓ Provenance obtained from attestation
    ✓ SBOM obtained from attestation, 100 packages found
    ✓ Provenance obtained from attestation
    ✓ Policy evaluation completed
    ✓ VEX statements obtained from attestation
    ✓ Policy evaluation completed
    ✓ VEX statements obtained from attestation


  &lt;span class="c"&gt;## Overview&lt;/span&gt;

                     │       Analyzed Image        │                   Comparison Image                    
  ───────────────────┼─────────────────────────────┼───────────────────────────────────────────────────────
   Target            │  my-dhi-app:v1              │  dhi.io/python:3.13                                   
     digest          │  e8c9377134bb               │  24a8822f76af                                         
     tag             │  v1                         │  3.13                                                 
     platform        │ linux/arm64                 │ linux/arm64                                           
     provenance      │                             │ https://github.com/docker-hardened-images/definitions 
                     │                             │  5fb32dbb43253700792f258345f151035442841c             
     vulnerabilities │    0C     3H     2M    12L  │    0C     0H     1M     0L                            
                     │           +3     +1    +12  │                                                       
     size            │ 31 MB &lt;span class="o"&gt;(&lt;/span&gt;+5.4 MB&lt;span class="o"&gt;)&lt;/span&gt;             │ 26 MB                                                 
     packages        │ 107 &lt;span class="o"&gt;(&lt;/span&gt;+7&lt;span class="o"&gt;)&lt;/span&gt;                    │ 100                                                   
                     │                             │                                                       


  &lt;span class="c"&gt;## Environment Variables&lt;/span&gt;


    - &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    + &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/app/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    + &lt;span class="nv"&gt;PYTHONDONTWRITEBYTECODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
    + &lt;span class="nv"&gt;PYTHONUNBUFFERED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
      &lt;span class="nv"&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.13.15
      &lt;span class="nv"&gt;SSL_CERT_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/ssl/certs/ca-certificates.crt



  &lt;span class="c"&gt;## Labels&lt;/span&gt;


      com.docker.dhi.chain-id&lt;span class="o"&gt;=&lt;/span&gt;sha256:a12342a94f7cf5f1d5dc0c6bc57007908b47f8e8681d261c97ec9aa60c9cdcc0
      com.docker.dhi.compliance&lt;span class="o"&gt;=&lt;/span&gt;cis
      com.docker.dhi.created&lt;span class="o"&gt;=&lt;/span&gt;2026-08-12T13:14:57Z
      com.docker.dhi.date.end-of-life&lt;span class="o"&gt;=&lt;/span&gt;2029-10-31
      com.docker.dhi.date.release&lt;span class="o"&gt;=&lt;/span&gt;2024-10-07
      com.docker.dhi.definition&lt;span class="o"&gt;=&lt;/span&gt;image/python/debian-13/3.13
      com.docker.dhi.distro&lt;span class="o"&gt;=&lt;/span&gt;debian-13
      com.docker.dhi.entitlement&lt;span class="o"&gt;=&lt;/span&gt;public
      com.docker.dhi.flavor&lt;span class="o"&gt;=&lt;/span&gt;
      com.docker.dhi.name&lt;span class="o"&gt;=&lt;/span&gt;dhi/python
      com.docker.dhi.package-manager&lt;span class="o"&gt;=&lt;/span&gt;
      com.docker.dhi.shell&lt;span class="o"&gt;=&lt;/span&gt;
      com.docker.dhi.title&lt;span class="o"&gt;=&lt;/span&gt;Python 3.13.x
      com.docker.dhi.url&lt;span class="o"&gt;=&lt;/span&gt;https://dhi.io/catalog/python
      com.docker.dhi.variant&lt;span class="o"&gt;=&lt;/span&gt;runtime
      com.docker.dhi.version&lt;span class="o"&gt;=&lt;/span&gt;3.13.15-debian13



  &lt;span class="c"&gt;## Config&lt;/span&gt;


    + 8000/tcp&lt;span class="o"&gt;=&lt;/span&gt;
    - &lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python3
    + &lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python app.py
      &lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;65532
    - &lt;span class="nv"&gt;workdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/
    + &lt;span class="nv"&gt;workdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/app



  &lt;span class="c"&gt;## Policies&lt;/span&gt;


  0 improved, 2 worsened

    Policy                                       Analyzed  Comparison  Change             

    Default non-root user                        ✓         ✓                   No Change  
    No copyleft licenses                         &lt;span class="o"&gt;!&lt;/span&gt; 119     &lt;span class="o"&gt;!&lt;/span&gt; 119               No Change  
    No fixable critical or high vulnerabilities  &lt;span class="o"&gt;!&lt;/span&gt; 3       ✓           +3      Worsened   
    No high-profile vulnerabilities              ✓         ✓                   No Change  
    No outdated base images                      ✓         ✓                   No Change  
    No unapproved base images                    ✓         ✓                   No Change  
    Supply chain attestations                    &lt;span class="o"&gt;!&lt;/span&gt; 1       ✓           +1      Worsened   

      View policy details → docker scout policy my-dhi-app:v1


  &lt;span class="c"&gt;## Packages and Vulnerabilities&lt;/span&gt;


    +    7 packages added  
        95 packages unchanged


    + 16 vulnerabilities added


     Package       Type  Version  Compared Version  

  +  blinker       pypi  1.9.0                      
  +  click         pypi  8.4.2                      
  +  flask         pypi  3.0.3                      
     └─  +  LOW          CVE-2026-27205  &lt;span class="o"&gt;[&lt;/span&gt;https://scout.docker.com/v/CVE-2026-27205]  
                         2.3  Use of Cache Containing Sensitive Information  
                         ✓ fixed &lt;span class="k"&gt;in  &lt;/span&gt;3.1.3   

  +  itsdangerous  pypi  2.2.0                      
  +  jinja2        pypi  3.1.6                      
  +  markupsafe    pypi  3.0.3                      
  +  werkzeug      pypi  3.1.8                      


➜  dhi-lab 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 — Evaluate Against Official DHI Policies
&lt;/h2&gt;

&lt;p&gt;Docker publishes the exact policy set that hardened images are held to. You can apply the same policies to any image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout policy my-dhi-app:v1 &lt;span class="nt"&gt;--policy-bundle&lt;/span&gt; dhi/policies:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi-lab docker scout policy my-dhi-app:v1 &lt;span class="nt"&gt;--policy-bundle&lt;/span&gt; dhi/policies:latest
    i New version 1.24.0 available &lt;span class="o"&gt;(&lt;/span&gt;installed version is 1.23.1&lt;span class="o"&gt;)&lt;/span&gt; at https://github.com/docker/scout-cli
    &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s1"&gt;'docker scout policy'&lt;/span&gt; is experimental and its behaviour might change &lt;span class="k"&gt;in &lt;/span&gt;the future
    ✓ Image stored &lt;span class="k"&gt;for &lt;/span&gt;indexing
    ✓ Indexed 107 packages
    ✓ Provenance obtained from attestation
    ✓ Policy evaluation completed


&lt;span class="c"&gt;## Overview&lt;/span&gt;

            │ Analyzed Image  
────────────┼─────────────────
 Target     │  my-dhi-app:v1  
   digest   │  e8c9377134bb   
   platform │ linux/arm64     


&lt;span class="c"&gt;## Policies&lt;/span&gt;

Policy status  FAILED  &lt;span class="o"&gt;(&lt;/span&gt;4/9 policies met&lt;span class="o"&gt;)&lt;/span&gt;

 Status │                    Policy                     │           Results           
────────┼───────────────────────────────────────────────┼─────────────────────────────
 ✓      │ No default root user &lt;span class="k"&gt;for &lt;/span&gt;non-dev images       │                             
 &lt;span class="o"&gt;!&lt;/span&gt;      │ Image contains embedded malware               │    2 deviations             
 &lt;span class="o"&gt;!&lt;/span&gt;      │ Image contains embedded secrets               │    2 deviations             
 &lt;span class="o"&gt;!&lt;/span&gt;      │ Image has failing tests                       │    2 deviations             
 &lt;span class="o"&gt;!&lt;/span&gt;      │ Fixable vulnerabilities past remediation SLA  │    0C     3H     0M     1L  
 ✓      │ No high-profile vulnerabilities               │    0C     0H     0M     0L  
 ✓      │ STIG scan                                     │    0 deviations             
 &lt;span class="o"&gt;!&lt;/span&gt;      │ Missing or unsigned supply chain attestations │    6 deviations             
 ✓      │ Unintentional shell or package manager        │    0 deviations             


&lt;span class="c"&gt;## "No embedded malware" policy evaluation results&lt;/span&gt;
This policy checks that no malware is embedded &lt;span class="k"&gt;in &lt;/span&gt;the image.

              Predicate              │                                            Reason                                             
─────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────
 https://scout.docker.com/virus/v0.1 │ doesn&lt;span class="s1"&gt;'t match subject sha256:e8c9377134bb24982fd0fb771881c290c9a3545ba0177b0fb5041db927991aa3 
 https://scout.docker.com/virus/v0.1 │ is missing                                                                                    


## "No embedded secrets" policy evaluation results
This policy checks that no secrets are embedded in the image.

               Predicate               │                                            Reason                                             
───────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────
 https://scout.docker.com/secrets/v0.1 │ doesn'&lt;/span&gt;t match subject sha256:e8c9377134bb24982fd0fb771881c290c9a3545ba0177b0fb5041db927991aa3 
 https://scout.docker.com/secrets/v0.1 │ is missing                                                                                    


&lt;span class="c"&gt;## "No failing tests" policy evaluation results&lt;/span&gt;
This policy checks that the image has tests and none are failing.

              Predicate              │                                            Reason                                             
─────────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────
 https://scout.docker.com/tests/v0.1 │ doesn&lt;span class="s1"&gt;'t match subject sha256:e8c9377134bb24982fd0fb771881c290c9a3545ba0177b0fb5041db927991aa3 
 https://scout.docker.com/tests/v0.1 │ is missing                                                                                    


## "No fixable vulnerabilities past their remediation SLA" policy evaluation results
Flags packages with a fixable CVE whose remediation SLA has elapsed. SLAs vary by severity, with a shorter SLA for CISA KEV-listed vulnerabilities. Only vulnerabilities that have a fix available are considered.

    Vulnerability    │  Severity  │  Current package version   │ Fix version 
─────────────────────┼────────────┼────────────────────────────┼─────────────
 CVE-2026-57585      │    HIGH    │ pkg:pypi/msgpack@1.1.2     │ 1.2.1       
 GHSA-6v7p-g79w-8964 │    HIGH    │ pkg:pypi/msgpack@1.1.2     │ 1.2.1       
 CVE-2025-47273      │    HIGH    │ pkg:pypi/setuptools@70.3.0 │ 78.1.1      
 CVE-2026-27205      │    LOW     │ pkg:pypi/flask@3.0.3       │ 3.1.3       


## "Signed supply chain attestations" policy evaluation results
This policy checks that all required SSC attestations are available and signed by a trusted party.

            Predicate             │                                            Reason                                             
──────────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────
 https://slsa.dev/provenance/v0.2 │ doesn'&lt;/span&gt;t match subject sha256:e8c9377134bb24982fd0fb771881c290c9a3545ba0177b0fb5041db927991aa3 
 https://slsa.dev/provenance/v0.2 │ doesn&lt;span class="s1"&gt;'t resolve to image ref                                                                  
 https://slsa.dev/provenance/v0.2 │ is missing                                                                                    
 https://spdx.dev/Document        │ doesn'&lt;/span&gt;t match subject sha256:e8c9377134bb24982fd0fb771881c290c9a3545ba0177b0fb5041db927991aa3 
 https://spdx.dev/Document        │ doesn&lt;span class="s1"&gt;'t resolve to image ref                                                                  
 https://spdx.dev/Document        │ is missing                                                                                    


What'&lt;/span&gt;s next:
    View vulnerabilities → docker scout cves my-dhi-app:v1
    View base image update recommendations → docker scout recommendations my-dhi-app:v1
    Compare with the latest &lt;span class="k"&gt;in &lt;/span&gt;the registry → docker scout compare &lt;span class="nt"&gt;--to-latest&lt;/span&gt; my-dhi-app:v1

➜  dhi-lab 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
The command pulls the policy bundle, generates an SBOM for your image, enriches it with CVE/VEX data, and checks every policy.&lt;/p&gt;

&lt;p&gt;Typical policies in the bundle include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs as non-root (for non-dev images)&lt;/li&gt;
&lt;li&gt;No fixable critical/high CVEs past the remediation window&lt;/li&gt;
&lt;li&gt;No high-profile / known-exploited vulnerabilities&lt;/li&gt;
&lt;li&gt;No embedded malware or secrets&lt;/li&gt;
&lt;li&gt;Signed supply-chain attestations present&lt;/li&gt;
&lt;li&gt;No unintentional shell or package manager&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Policy Evaluation Flow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhlfqi85znoxchl6n117.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flhlfqi85znoxchl6n117.png" alt="policy evaluation flow" width="783" height="1180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Scout does not just look at the base image. It analyses the full contents of &lt;em&gt;your&lt;/em&gt; image and tells you whether it meets the same bar that Docker Hardened Images are required to meet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Fix a Failing Check (Hands-on)
&lt;/h2&gt;

&lt;p&gt;If the policy run reports that your image runs as root or contains an unexpected shell, go back to the multi-stage Dockerfile from Blog 2 and ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Final stage uses a pure runtime variant (not &lt;code&gt;-dev&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You did not accidentally add a shell or switch to &lt;code&gt;USER root&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rebuild and re-run the policy command until it is clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Break It
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Build a naïve image that starts from a normal &lt;code&gt;python:3.13&lt;/code&gt; and runs as root.
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;docker scout policy ... --policy-bundle dhi/policies:latest&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Observe the failures (root user, extra packages, missing attestations, etc.).
&lt;/li&gt;
&lt;li&gt;Switch the base to a DHI runtime image and rebuild — most failures disappear.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In a real pipeline I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always verify DHI attestations when pulling base images in CI&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;docker scout policy --policy-bundle dhi/policies:latest&lt;/code&gt; as a required gate&lt;/li&gt;
&lt;li&gt;Fail the build if critical policies are not met&lt;/li&gt;
&lt;li&gt;Keep the policy config in the repository so the whole team uses the same rules&lt;/li&gt;
&lt;li&gt;Prefer images that already carry signed SBOMs and provenance&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A signed SBOM tells you what is inside; it does not magically remove vulnerabilities&lt;/li&gt;
&lt;li&gt;VEX helps you ignore CVEs that are not exploitable in this particular image&lt;/li&gt;
&lt;li&gt;Provenance proves the build process; it does not prove the application code is correct&lt;/li&gt;
&lt;li&gt;Policy evaluation is only as good as the policies you enable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting the &lt;code&gt;registry://&lt;/code&gt; prefix when the image is already local&lt;/li&gt;
&lt;li&gt;Expecting every attestation type on every variant (some are variant-specific)&lt;/li&gt;
&lt;li&gt;Treating a green Scout report as “the application is secure”&lt;/li&gt;
&lt;li&gt;Not pinning the policy-bundle digest in CI (using &lt;code&gt;:latest&lt;/code&gt; is convenient but less reproducible)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Check / Command&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;unauthorized&lt;/code&gt; on attest list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker login dhi.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-authenticate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attestation not found&lt;/td&gt;
&lt;td&gt;Image name / tag / platform&lt;/td&gt;
&lt;td&gt;Use exact tag and &lt;code&gt;--platform&lt;/code&gt; if needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature verification fails&lt;/td&gt;
&lt;td&gt;Network / key&lt;/td&gt;
&lt;td&gt;Try &lt;code&gt;--skip-tlog&lt;/code&gt; if transparency log issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy bundle pull fails&lt;/td&gt;
&lt;td&gt;Docker Hub login&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker login&lt;/code&gt; (normal Hub credentials)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Too many policy failures&lt;/td&gt;
&lt;td&gt;Base image still official&lt;/td&gt;
&lt;td&gt;Switch final stage to a DHI runtime variant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi my-dhi-app:v1 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every DHI carries signed SBOMs, provenance and other attestations&lt;/li&gt;
&lt;li&gt;You can list, retrieve and cryptographically verify them with Docker Scout&lt;/li&gt;
&lt;li&gt;The same policy bar that Docker applies to hardened images can be applied to your own images&lt;/li&gt;
&lt;li&gt;This gives real supply-chain transparency, not just marketing claims&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now prove what is inside an image and enforce the same standards yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In Blog 4 we put everything into practice for CI/CD: proper multi-stage Dockerfiles, caching, authenticating to &lt;code&gt;dhi.io&lt;/code&gt; in pipelines, and making the DHI policy check a required gate so every build stays clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/verify/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/verify/&lt;/a&gt; — listing and verifying attestations
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/policies/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/policies/&lt;/a&gt; — DHI policy bundle
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/core-concepts/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/core-concepts/&lt;/a&gt; — SBOM, SLSA, provenance, signatures
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/core-concepts/sbom/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/core-concepts/sbom/&lt;/a&gt; — SBOM details
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/core-concepts/signatures/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/core-concepts/signatures/&lt;/a&gt; — code signing
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>containers</category>
      <category>security</category>
    </item>
    <item>
      <title>Anatomy of a Hardened Image + Building Real Applications</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Mon, 17 Aug 2026 16:37:12 +0000</pubDate>
      <link>https://dev.to/vellanki/anatomy-of-a-hardened-image-building-real-applications-5a8a</link>
      <guid>https://dev.to/vellanki/anatomy-of-a-hardened-image-building-real-applications-5a8a</guid>
      <description>&lt;p&gt;&lt;strong&gt;Docker Hardened Images Series — Blog 2&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inspect what is actually inside a Docker Hardened Image&lt;/li&gt;
&lt;li&gt;Clearly understand runtime vs dev variants&lt;/li&gt;
&lt;li&gt;See why many runtime images have no shell&lt;/li&gt;
&lt;li&gt;Build a real small application (Python Flask) using a proper multi-stage Dockerfile on DHI bases&lt;/li&gt;
&lt;li&gt;Run the final minimal image successfully as non-root&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In Blog 1 we pulled a DHI and compared size/CVEs. That showed the benefit, but it did not explain &lt;strong&gt;how&lt;/strong&gt; the image is different or how to build real applications on top of it.&lt;/p&gt;

&lt;p&gt;If you simply change &lt;code&gt;FROM python:3.13&lt;/code&gt; to &lt;code&gt;FROM dhi.io/python:3.13&lt;/code&gt; many Dockerfiles will break because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is often no shell&lt;/li&gt;
&lt;li&gt;There is no package manager in the runtime variant&lt;/li&gt;
&lt;li&gt;The container runs as a non-root user by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog shows the correct, production-ready pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blog 1 completed (&lt;code&gt;docker login dhi.io&lt;/code&gt; already done)&lt;/li&gt;
&lt;li&gt;Basic Dockerfile knowledge&lt;/li&gt;
&lt;li&gt;A working Docker environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1 — Inspect a Hardened Image
&lt;/h2&gt;

&lt;p&gt;Pull both a runtime and a dev variant (example with Python):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull dhi.io/python:3.13
docker pull dhi.io/python:3.13-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fskikcik8b668oreto7qd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fskikcik8b668oreto7qd.png" alt="docker pull" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the default user
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect dhi.io/python:3.13 &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.User}}'&lt;/span&gt;
docker inspect dhi.io/python:3.13-dev &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.User}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9h6d2xlwy1k6sh3xg8yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9h6d2xlwy1k6sh3xg8yy.png" alt="docker inspect" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Runtime images usually show a non-root user (often &lt;code&gt;nonroot&lt;/code&gt; or a numeric UID such as 65532).&lt;br&gt;&lt;br&gt;
Dev images often run as root so you can install packages during build.&lt;/p&gt;
&lt;h3&gt;
  
  
  Try to get a shell
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; dhi.io/python:3.13 bash
&lt;span class="c"&gt;# Usually fails — no shell&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh66jwj4g4pmzavcsqzjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh66jwj4g4pmzavcsqzjt.png" alt="shell failed" width="798" height="92"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; dhi.io/python:3.13-dev bash
&lt;span class="c"&gt;# Works — shell is present&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fit556fg9ryyqgsb3blpe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fit556fg9ryyqgsb3blpe.png" alt="shell working" width="800" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Create a Simple Application
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; dhi-lab
&lt;span class="nb"&gt;cd &lt;/span&gt;dhi-lab

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; app.py &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from a Docker Hardened Image!"

@app.route("/health")
def health():
    return {"status": "ok"}

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
flask==3.0.3
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 — Write a Correct Multi-Stage Dockerfile
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax=docker/dockerfile:1&lt;/span&gt;

&lt;span class="c"&gt;# ---------- Build stage ----------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;dhi.io/python:3.13-dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/app/venv/bin:$PATH"&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Create a virtual environment and install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv /app/venv
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# ---------- Runtime stage ----------&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; dhi.io/python:3.13&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="/app/venv/bin:$PATH"&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy only what is needed&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/venv /app/venv&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py .&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="c"&gt;# Runtime image already runs as non-root&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this pattern works&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build stage uses the &lt;code&gt;-dev&lt;/code&gt; image → has shell + pip&lt;/li&gt;
&lt;li&gt;Runtime stage uses the minimal image → no shell, non-root, tiny attack surface&lt;/li&gt;
&lt;li&gt;Only the virtual environment and application code are copied&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4 — Build and Run
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; dhi-flask-app &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8000:8000 dhi-flask-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0952eknrbwgxhak2dnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0952eknrbwgxhak2dnl.png" alt="docker build run" width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8000
curl http://localhost:8000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal 2 Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fda475enmali7daso1cc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fda475enmali7daso1cc0.png" alt="health check" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see the expected responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify the final image is minimal and non-root
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images dhi-flask-app
docker inspect dhi-flask-app &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'{{.Config.User}}'&lt;/span&gt;
docker &lt;span class="nb"&gt;history &lt;/span&gt;dhi-flask-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F10r8avk4euqbrskl8li4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F10r8avk4euqbrskl8li4.png" alt="verifying images" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compare size with a naïve single-stage build if you want — the multi-stage DHI version is significantly smaller and cleaner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Just Happened Internally
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The builder stage installed dependencies inside a virtual environment using tools that only exist in the dev variant.
&lt;/li&gt;
&lt;li&gt;The runtime stage started from a minimal hardened base that already runs as non-root.
&lt;/li&gt;
&lt;li&gt;Only the necessary files were copied across.
&lt;/li&gt;
&lt;li&gt;The final container has no package manager and no shell, so an attacker who gets code execution has far fewer tools available.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Let’s Break It
&lt;/h2&gt;

&lt;p&gt;Try a single-stage Dockerfile that uses only the runtime image and tries to run &lt;code&gt;pip install&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; dhi.io/python:3.13&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt   &lt;span class="c"&gt;# This will fail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fails because there is no pip (and often no shell). That is intentional. Multi-stage is the correct solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In real projects I always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;-dev&lt;/code&gt; (or &lt;code&gt;-sdk&lt;/code&gt;) only in build stages&lt;/li&gt;
&lt;li&gt;Keep the final stage as the pure runtime variant&lt;/li&gt;
&lt;li&gt;Prefer virtual environments (Python) or equivalent so the runtime image stays clean&lt;/li&gt;
&lt;li&gt;Explicitly set &lt;code&gt;USER&lt;/code&gt; only if I need to override the default non-root user&lt;/li&gt;
&lt;li&gt;Test that the application works when running as non-root (file permissions, ports &amp;gt; 1024)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Non-root by default removes an entire class of privilege-escalation attacks&lt;/li&gt;
&lt;li&gt;No shell and no package manager make post-exploitation much harder&lt;/li&gt;
&lt;li&gt;The image is still only as secure as the application code and the dependencies you copy in&lt;/li&gt;
&lt;li&gt;Always scan the &lt;strong&gt;final&lt;/strong&gt; image, not just the base&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Using a runtime image for the build stage → missing tools&lt;/li&gt;
&lt;li&gt;Forgetting to copy the virtual environment or node_modules correctly&lt;/li&gt;
&lt;li&gt;Assuming the container can bind to ports below 1024&lt;/li&gt;
&lt;li&gt;Adding a shell “just in case” back into the runtime image&lt;/li&gt;
&lt;li&gt;Running as root again with &lt;code&gt;USER root&lt;/code&gt; without a strong reason&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Why it happens&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pip: command not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Using runtime image for build&lt;/td&gt;
&lt;td&gt;Switch build stage to &lt;code&gt;*-dev&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission denied writing files&lt;/td&gt;
&lt;td&gt;Non-root user&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --chown=...&lt;/code&gt; or fix ownership in builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cannot bind to port 80&lt;/td&gt;
&lt;td&gt;Non-root + older Docker/K8s&lt;/td&gt;
&lt;td&gt;Use port ≥ 1024 inside the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image still large&lt;/td&gt;
&lt;td&gt;Copied too much from builder&lt;/td&gt;
&lt;td&gt;Copy only venv / binary / needed files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exec format error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Platform mismatch&lt;/td&gt;
&lt;td&gt;Build with &lt;code&gt;--platform&lt;/code&gt; or matching arch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi dhi-flask-app dhi.io/python:3.13 dhi.io/python:3.13-dev 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/dhi-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Runtime variants are intentionally minimal (no shell, non-root)&lt;/li&gt;
&lt;li&gt;Dev variants exist precisely so you can still build software&lt;/li&gt;
&lt;li&gt;Multi-stage Dockerfiles are the standard and recommended pattern with DHI&lt;/li&gt;
&lt;li&gt;You can build real applications that stay small and secure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now know how to look inside a hardened image and how to build on top of it correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In Blog 3 we inspect the supply-chain metadata that every DHI carries: signed SBOMs, SLSA Build Level 3 provenance, signatures and VEX data. We will also learn how to verify them and how to enforce the same standards on our own images with Docker Scout policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/use/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/use/&lt;/a&gt; — runtime vs dev, multi-stage patterns
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/explore/what/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/explore/what/&lt;/a&gt; — distroless approach and design goals
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hub.docker.com/hardened-images/catalog" rel="noopener noreferrer"&gt;https://hub.docker.com/hardened-images/catalog&lt;/a&gt; — variant details and examples
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/migration/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/migration/&lt;/a&gt; — common migration considerations
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>containers</category>
      <category>security</category>
    </item>
    <item>
      <title>Why Images Need Hardening + Getting Started with Docker Hardened Images</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Sat, 15 Aug 2026 11:44:35 +0000</pubDate>
      <link>https://dev.to/vellanki/why-images-need-hardening-getting-started-with-docker-hardened-images-5b4g</link>
      <guid>https://dev.to/vellanki/why-images-need-hardening-getting-started-with-docker-hardened-images-5b4g</guid>
      <description>&lt;p&gt;&lt;strong&gt;Docker Hardened Images Series — Blog 1&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will understand the real problem with typical container images, know what a Docker Hardened Image (DHI) actually is, authenticate to &lt;code&gt;dhi.io&lt;/code&gt;, pull your first hardened image, run it, and compare it side-by-side with a normal official image.&lt;/p&gt;

&lt;p&gt;You will leave with a clear “why” and a working first image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Most teams still start with official images like &lt;code&gt;python:3.13&lt;/code&gt; or &lt;code&gt;node:22&lt;/code&gt;. These images are convenient, but they usually contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many packages you never use&lt;/li&gt;
&lt;li&gt;Shells, package managers and debugging tools&lt;/li&gt;
&lt;li&gt;Known CVEs that keep appearing in scans&lt;/li&gt;
&lt;li&gt;A larger attack surface than your application needs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a vulnerability is disclosed, you spend time triaging, rebuilding and redeploying — often for packages that should never have been in the runtime image in the first place.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;hardened image&lt;/strong&gt; is deliberately minimised and continuously maintained so that the number of known vulnerabilities stays near zero and the attack surface is dramatically smaller.&lt;/p&gt;

&lt;p&gt;Docker Hardened Images (DHI) are Docker’s official implementation of this idea. The Community tier is free and open under Apache 2.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Basic Docker knowledge (&lt;code&gt;docker pull&lt;/code&gt;, &lt;code&gt;docker run&lt;/code&gt;, &lt;code&gt;docker images&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A free Docker account&lt;/li&gt;
&lt;li&gt;Docker Desktop or Docker Engine with the Scout CLI plugin (comes with Docker Desktop)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Important Distinctions (Do Not Confuse These)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;Automatically secure?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Small / Slim&lt;/td&gt;
&lt;td&gt;Fewer packages, smaller size&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;td&gt;Only what is needed to run&lt;/td&gt;
&lt;td&gt;Better, still not enough&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distroless&lt;/td&gt;
&lt;td&gt;No shell, no package manager&lt;/td&gt;
&lt;td&gt;Good reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardened (DHI)&lt;/td&gt;
&lt;td&gt;Minimal + continuous CVE patching + signed SBOM + SLSA provenance + non-root by default&lt;/td&gt;
&lt;td&gt;Closest to “secure by default”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Smaller does &lt;strong&gt;not&lt;/strong&gt; automatically mean secure. Hardening is about continuous maintenance, provenance and reduced attack surface together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before vs After — Attack Surface
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc9po2y7w65ax0p1gmmj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc9po2y7w65ax0p1gmmj3.png" alt="before vs after" width="800" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Left side shows a typical official image full of extra tools and packages.&lt;br&gt;&lt;br&gt;
Right side shows a hardened image that keeps only what is required to run the application. Fewer components = fewer places for vulnerabilities.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1 — See the Problem Yourself
&lt;/h2&gt;

&lt;p&gt;Pull a normal official image and check its size and vulnerabilities (example with Python):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull python:3.13
docker images python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi docker pull python:3.13
3.13: Pulling from library/python
018e5aeb5455: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;fcff83b0426f: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;6e8333d59c37: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;50fe3346a36b: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;cecdaccf3d35: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;7c92d7371fba: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;4d3df26e3af2: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;ae85bf126208: Download &lt;span class="nb"&gt;complete 
&lt;/span&gt;1635e0a2f722: Download &lt;span class="nb"&gt;complete 
&lt;/span&gt;Digest: sha256:e16ab55c341bfd0e7da665bc2d48939cff890b43a41867fe6e1f0690638ceb7c
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;python:3.13
docker.io/library/python:3.13

What&lt;span class="s1"&gt;'s next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview python:3.13
➜  dhi 
➜  dhi docker images python:3.13
                                                                                                                                                                                           i Info →   U  In Use
IMAGE         ID             DISK USAGE   CONTENT SIZE   EXTRA
python:3.13   e16ab55c341b       1.62GB          420MB        
➜  dhi 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have Docker Scout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout cves python:3.13 &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Docker scout Output&lt;/strong&gt; &lt;br&gt;
(It's too lengthy output added the main summary for getting context)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;313 vulnerabilities found &lt;span class="k"&gt;in &lt;/span&gt;52 packages
  CRITICAL     3   
  HIGH         42  
  MEDIUM       22  
  LOW          199 
  UNSPECIFIED  47  


What&lt;span class="s1"&gt;'s next:
    View base image update recommendations → docker scout recommendations python:3.13

➜  dhi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will usually see a long list of CVEs and a relatively large image size. Note the numbers — we will compare them soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Browse the Hardened Images Catalog
&lt;/h2&gt;

&lt;p&gt;Open the official catalog:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hub.docker.com/hardened-images/catalog" rel="noopener noreferrer"&gt;https://hub.docker.com/hardened-images/catalog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5wyvzeyxgscshbasj8s4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5wyvzeyxgscshbasj8s4.png" alt="official catalogue" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Search for &lt;code&gt;python&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;golang&lt;/code&gt;, &lt;code&gt;nginx&lt;/code&gt;, etc.&lt;br&gt;&lt;br&gt;
You will see variants (Debian/Alpine, runtime/dev, etc.) and security information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybcp7zgjlf335dyhisxi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybcp7zgjlf335dyhisxi.png" alt="python hardened image" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 — Authenticate to dhi.io
&lt;/h2&gt;

&lt;p&gt;Community images live on the &lt;code&gt;dhi.io&lt;/code&gt; registry and require authentication (same Docker account you already have).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login dhi.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi docker login dhi.io
Authenticating with existing credentials... &lt;span class="o"&gt;[&lt;/span&gt;Username: vellankikoti]

i Info → To login with a different account, run &lt;span class="s1"&gt;'docker logout'&lt;/span&gt; followed by &lt;span class="s1"&gt;'docker login'&lt;/span&gt;


Login Succeeded
➜  dhi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use your Docker Hub username and password (or a personal access token).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Pull and Run Your First DHI
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi docker pull dhi.io/python:3.13
3.13: Pulling from python
3969c08c9319: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;a3577369b942: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;267158a60674: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;26a837766441: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;61947a7c3e2f: Pull &lt;span class="nb"&gt;complete 
&lt;/span&gt;Digest: sha256:45c7b8b9a4edeb7b41ee8dfb1e902dd37782e9d78bef53ae31d01bcb6259b7bf
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;dhi.io/python:3.13
dhi.io/python:3.13

What&lt;span class="s1"&gt;'s next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview dhi.io/python:3.13
➜  dhi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; dhi.io/python:3.13 python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"print('Hello from Docker Hardened Image')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; dhi.io/python:3.13 python &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"print('Hello from Docker Hardened Image')"&lt;/span&gt;
Hello from Docker Hardened Image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that many DHI runtime images are intentionally minimal — they may not contain a full shell or package manager. That is by design.&lt;/p&gt;

&lt;p&gt;Check the size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images dhi.io/python:3.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi docker images dhi.io/python:3.13         
                                                                                                                                                                                           i Info →   U  In Use
IMAGE                ID             DISK USAGE   CONTENT SIZE   EXTRA
dhi.io/python:3.13   45c7b8b9a4ed        148MB           26MB        
➜  dhi docker images python:3.13                                                           
                                                                                                                                                                                           i Info →   U  In Use
IMAGE         ID             DISK USAGE   CONTENT SIZE   EXTRA
python:3.13   e16ab55c341b       1.62GB          420MB        
➜  dhi 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5 — Compare Side-by-Side with Docker Scout
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker scout compare dhi.io/python:3.13 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--to&lt;/span&gt; python:3.13 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ignore-unchanged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the &lt;strong&gt;Overview&lt;/strong&gt; section. You will typically see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Large reduction in image size&lt;/li&gt;
&lt;li&gt;Many (sometimes almost all) CVEs removed&lt;/li&gt;
&lt;li&gt;Clear difference in the number of packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your exact numbers will vary by tag and date, but the improvement is usually dramatic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison Flow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmllgrs2s8ov80wntuymn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmllgrs2s8ov80wntuymn.png" alt="dhi comparison" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Scout analyses both images and shows the concrete differences in size, packages and known vulnerabilities. This is the fastest way to see the value of a hardened image.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Just Happened Internally
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You authenticated to the dedicated DHI registry (&lt;code&gt;dhi.io&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You pulled a continuously maintained, minimal image built by Docker&lt;/li&gt;
&lt;li&gt;The image is designed to run as non-root by default&lt;/li&gt;
&lt;li&gt;It comes with signed SBOMs, SLSA Build Level 3 provenance and VEX data (we will inspect these in later blogs)&lt;/li&gt;
&lt;li&gt;The comparison proves the reduction in attack surface with real numbers&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Let’s Break It (Gently)
&lt;/h2&gt;

&lt;p&gt;Try to run an interactive shell on a minimal runtime image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; dhi.io/python:3.13 bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  dhi docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; dhi.io/python:3.13 bash

What&lt;span class="s1"&gt;'s next:
    Debug this container error with Gordon → docker ai "help me fix this container error"
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "bash": executable file not found in $PATH

Run '&lt;/span&gt;docker run &lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="s1"&gt;' for more information
➜  dhi 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will often fail because many runtime variants intentionally do not include a shell.&lt;br&gt;&lt;br&gt;
That is expected. Use a &lt;code&gt;-dev&lt;/code&gt; variant when you need a shell for building, and keep the runtime image minimal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In a real project I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start every new service with a DHI base image from day one&lt;/li&gt;
&lt;li&gt;Use multi-stage builds (dev variant for build, runtime variant for final image)&lt;/li&gt;
&lt;li&gt;Treat the Scout comparison as part of the PR checklist&lt;/li&gt;
&lt;li&gt;Prefer Community tier first; move to Select/Enterprise only when I need FIPS, SLA or customisation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Near-zero known CVEs is a strong starting point, but it is not a complete security programme&lt;/li&gt;
&lt;li&gt;A hardened base image does not make insecure application code safe&lt;/li&gt;
&lt;li&gt;Always still scan your final application image&lt;/li&gt;
&lt;li&gt;Non-root by default removes a whole class of privilege-escalation issues&lt;/li&gt;
&lt;li&gt;Smaller attack surface means fewer packages that can be exploited&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expecting every DHI to have a shell and package manager&lt;/li&gt;
&lt;li&gt;Forgetting &lt;code&gt;docker login dhi.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Comparing images without specifying &lt;code&gt;--platform&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Thinking “I pulled a small image, so I am secure”&lt;/li&gt;
&lt;li&gt;Putting secrets or unnecessary tools into the final runtime image&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;unauthorized&lt;/code&gt; when pulling&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker login dhi.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log in again with Docker Hub credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image has no shell&lt;/td&gt;
&lt;td&gt;You pulled a runtime variant&lt;/td&gt;
&lt;td&gt;Use a &lt;code&gt;-dev&lt;/code&gt; variant for build stages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scout compare fails&lt;/td&gt;
&lt;td&gt;Scout plugin / login&lt;/td&gt;
&lt;td&gt;Ensure Docker Desktop + logged in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Numbers look different&lt;/td&gt;
&lt;td&gt;Tag or platform&lt;/td&gt;
&lt;td&gt;Always pin platform and compare same tags&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker rmi dhi.io/python:3.13 python:3.13 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Typical official images carry unnecessary packages and CVEs&lt;/li&gt;
&lt;li&gt;Docker Hardened Images are minimal, continuously patched, non-root by default, and come with strong supply-chain metadata&lt;/li&gt;
&lt;li&gt;Community tier is free; you only need &lt;code&gt;docker login dhi.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A simple Scout comparison immediately shows the practical benefit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now pull and run hardened images with confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In Blog 2 we look inside a hardened image (layers, distroless nature, non-root user) and start building real application images on top of DHI bases using multi-stage Dockerfiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/&lt;/a&gt; — official overview and tiers
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/get-started/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/get-started/&lt;/a&gt; — quickstart
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/explore/what/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/explore/what/&lt;/a&gt; — what hardened images are
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/dhi/how-to/use/" rel="noopener noreferrer"&gt;https://docs.docker.com/dhi/how-to/use/&lt;/a&gt; — authentication and basic usage
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hub.docker.com/hardened-images/catalog" rel="noopener noreferrer"&gt;https://hub.docker.com/hardened-images/catalog&lt;/a&gt; — public catalog
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>dhi</category>
      <category>containers</category>
    </item>
    <item>
      <title>Security Model, Troubleshooting, Limitations &amp; Production Best Practices</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Fri, 14 Aug 2026 11:50:37 +0000</pubDate>
      <link>https://dev.to/vellanki/security-model-troubleshooting-limitations-production-best-practices-5dlc</link>
      <guid>https://dev.to/vellanki/security-model-troubleshooting-limitations-production-best-practices-5dlc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Blog 5 — Docker Sandboxes Series (Final)&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;This final blog brings everything together.&lt;/p&gt;

&lt;p&gt;You will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clearly understand the five isolation layers and the real trust boundary&lt;/li&gt;
&lt;li&gt;Know exactly what is protected and what is still shared&lt;/li&gt;
&lt;li&gt;Diagnose real problems with &lt;code&gt;sbx diagnose&lt;/code&gt; and policy logs&lt;/li&gt;
&lt;li&gt;See realistic limitations so you do not over-promise&lt;/li&gt;
&lt;li&gt;Leave with a practical production checklist and team runbook&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this blog you should be able to introduce Docker Sandboxes to a real team with confidence and honest expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;You now know how to launch agents, control files, network and secrets, build containers inside the sandbox, use templates, kits, ports and SSH.&lt;/p&gt;

&lt;p&gt;The last missing piece is judgement.&lt;/p&gt;

&lt;p&gt;An experienced engineer needs to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What can still go wrong?&lt;/li&gt;
&lt;li&gt;Where does the protection stop?&lt;/li&gt;
&lt;li&gt;How do I debug when something fails?&lt;/li&gt;
&lt;li&gt;When should I &lt;em&gt;not&lt;/em&gt; use a sandbox?&lt;/li&gt;
&lt;li&gt;How do I run this safely with a team?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This blog answers those questions with practical detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blogs 1–4 completed&lt;/li&gt;
&lt;li&gt;At least one working sandbox from previous labs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Five Isolation Layers (Security Model)
&lt;/h2&gt;

&lt;p&gt;Docker Sandboxes protect the host with five layers. The primary trust boundary is the &lt;strong&gt;microVM&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-level Trust Boundary
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcn8n72388ut0iu2nzwau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcn8n72388ut0iu2nzwau.png" alt="trust boundary" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Everything inside the microVM is fully controlled by the agent (including sudo).&lt;br&gt;&lt;br&gt;
The hypervisor prevents the agent from reaching anything on the host except what you deliberately share (workspace, allowed network via proxy, injected credentials).&lt;/p&gt;
&lt;h3&gt;
  
  
  Layer-by-layer summary
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hypervisor isolation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Separate kernel. No shared processes or memory with the host. Removing the sandbox deletes the entire VM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Network isolation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
All HTTP/HTTPS goes through a host-side proxy that enforces deny-by-default policy.&lt;br&gt;&lt;br&gt;
Raw TCP, UDP and ICMP are blocked at the network layer.&lt;br&gt;&lt;br&gt;
Sandboxes cannot talk to each other or to host localhost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Engine isolation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Each sandbox has its own Docker daemon. There is no path to the host Docker socket.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Workspace isolation&lt;/strong&gt; (optional but important)  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default = direct read-write mount (agent can change your real files)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--clone&lt;/code&gt; = private clone inside the VM + read-only mount of your repo (much safer)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Credential isolation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Secrets stay in the OS keychain on the host. The proxy injects them into HTTP headers. Raw values never enter the microVM.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  What the agent &lt;em&gt;can&lt;/em&gt; do inside the microVM
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install any package with sudo
&lt;/li&gt;
&lt;li&gt;Build and run any containers with its own Docker
&lt;/li&gt;
&lt;li&gt;Read/write the workspace (or the private clone)
&lt;/li&gt;
&lt;li&gt;Make network calls to domains you have allowed&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What is protected
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Host filesystem outside the workspace / shared skills store
&lt;/li&gt;
&lt;li&gt;Host Docker daemon
&lt;/li&gt;
&lt;li&gt;Host network and localhost
&lt;/li&gt;
&lt;li&gt;Any domain not explicitly allowed
&lt;/li&gt;
&lt;li&gt;Direct communication between sandboxes
&lt;/li&gt;
&lt;li&gt;Raw secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Hands-on: Controlled Breakage &amp;amp; Recovery
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Confirm the isolation layers yourself
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start a clean sandbox&lt;/span&gt;
sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; sec-test &lt;span class="nt"&gt;--clone&lt;/span&gt; shell

&lt;span class="c"&gt;# Inside the sandbox try things that should fail&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /home          &lt;span class="c"&gt;# only the agent user&lt;/span&gt;
docker info       &lt;span class="c"&gt;# works — but it is the sandbox daemon&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://example.com   &lt;span class="c"&gt;# may be blocked depending on policy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;On the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; alpine &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Host Docker is clean"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Run diagnostics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx diagnose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks CLI, daemon, storage, authentication and gives suggested fixes.&lt;/p&gt;

&lt;p&gt;Also useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy &lt;span class="nb"&gt;ls
&lt;/span&gt;sbx policy log
sbx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Simulate a common failure
&lt;/h3&gt;

&lt;p&gt;Intentionally block a domain the agent needs, watch it fail, then fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy deny network registry.npmjs.org
&lt;span class="c"&gt;# try npm install inside the sandbox → fails&lt;/span&gt;
sbx policy allow network &lt;span class="s2"&gt;"*.npmjs.org"&lt;/span&gt;
&lt;span class="c"&gt;# try again → works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Realistic Limitations
&lt;/h2&gt;

&lt;p&gt;Be honest with your team about these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource overhead&lt;/strong&gt; — each sandbox is a microVM + its own Docker daemon. Disk and memory usage grows with images and packages. Clean up regularly with &lt;code&gt;sbx rm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct mount risk&lt;/strong&gt; — without &lt;code&gt;--clone&lt;/code&gt; the agent can modify Git hooks, CI files, &lt;code&gt;.env&lt;/code&gt;, IDE configs, etc. Always review changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clone mode still reads your files&lt;/strong&gt; — including untracked &lt;code&gt;.env&lt;/code&gt; files. Secrets should never live in the workspace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-HTTP protocols&lt;/strong&gt; — SSH, raw TCP, UDP are blocked by default. Limited IP:port exceptions exist but are not free-for-all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organisation governance&lt;/strong&gt; (paid) completely replaces local &lt;code&gt;sbx policy&lt;/code&gt; rules. Local allow rules become inactive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem performance&lt;/strong&gt; — large repositories in direct mode can feel slower. Virtiofs caching helps; you can disable it if needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No shared Docker layers&lt;/strong&gt; between sandboxes — each keeps its own cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers&lt;/strong&gt; that run on the host are outside the microVM boundary. Treat them as trusted host integrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production Best Practices &amp;amp; Team Runbook
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Recommended defaults for a team
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Always prefer clone mode for agent work&lt;/span&gt;
sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; feature-xyz &lt;span class="nt"&gt;--clone&lt;/span&gt; claude

&lt;span class="c"&gt;# Keep network tight&lt;/span&gt;
sbx policy set-default balanced   &lt;span class="c"&gt;# or stricter&lt;/span&gt;
&lt;span class="c"&gt;# then add only what is needed&lt;/span&gt;

&lt;span class="c"&gt;# Secrets only via sbx secret&lt;/span&gt;
sbx secret &lt;span class="nb"&gt;set &lt;/span&gt;anthropic
sbx secret &lt;span class="nb"&gt;set &lt;/span&gt;github &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh auth token&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Named sandboxes + clean up&lt;/span&gt;
sbx &lt;span class="nb"&gt;rm &lt;/span&gt;feature-xyz   &lt;span class="c"&gt;# when the task is finished&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Simple team runbook (copy-paste ready)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;code&gt;sbx&lt;/code&gt; and run &lt;code&gt;sbx login&lt;/code&gt; + &lt;code&gt;sbx setup ssh&lt;/code&gt; once.
&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;--clone&lt;/code&gt; for any non-trivial agent session.
&lt;/li&gt;
&lt;li&gt;Use named sandboxes (&lt;code&gt;--name&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;Store all secrets with &lt;code&gt;sbx secret&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Start from Balanced (or Locked) network policy and open only required domains.
&lt;/li&gt;
&lt;li&gt;Connect VS Code / Cursor via &lt;code&gt;*.sbx&lt;/code&gt; when you need a full editor.
&lt;/li&gt;
&lt;li&gt;Publish ports only when you need to open a service in the browser.
&lt;/li&gt;
&lt;li&gt;When the task is done: &lt;code&gt;sbx stop&lt;/code&gt; → &lt;code&gt;sbx rm&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Weekly: &lt;code&gt;sbx ls&lt;/code&gt; and remove anything older than a few days.
&lt;/li&gt;
&lt;li&gt;If something is broken: &lt;code&gt;sbx diagnose&lt;/code&gt; first.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Combining with Docker Hardened Images
&lt;/h3&gt;

&lt;p&gt;Inside the sandbox you can (and should) build your application images from Docker Hardened Images (&lt;code&gt;dhi.io/...&lt;/code&gt;). The sandbox’s own Docker daemon will pull them after you have authenticated to &lt;code&gt;dhi.io&lt;/code&gt; on the host. This gives you both strong agent isolation &lt;strong&gt;and&lt;/strong&gt; secure base images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you haven't heard or tried Docker Hardened Images (DHI) yet, don't worry similar to Sandboxes I'll be publishing short blog series on DHI as well.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes (Final List)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Running agents in direct mode on important repositories without review
&lt;/li&gt;
&lt;li&gt;Leaving secrets in &lt;code&gt;.env&lt;/code&gt; files that the agent can read
&lt;/li&gt;
&lt;li&gt;Opening network too widely (“just allow **”)
&lt;/li&gt;
&lt;li&gt;Forgetting to remove old sandboxes → disk fills up
&lt;/li&gt;
&lt;li&gt;Expecting local policy rules to work when organisation governance is active
&lt;/li&gt;
&lt;li&gt;Treating the sandbox as a full replacement for a proper CI environment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;First command&lt;/th&gt;
&lt;th&gt;Typical fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Anything strange&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx diagnose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow the suggestions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network blocked&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx policy log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx policy allow network &amp;lt;domain&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cannot connect editor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx setup ssh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk full&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sbx rm&lt;/code&gt; old sandboxes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent cannot authenticate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx secret ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-set the secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow file operations&lt;/td&gt;
&lt;td&gt;Check if direct mount&lt;/td&gt;
&lt;td&gt;Try &lt;code&gt;--clone&lt;/code&gt; or disable virtiofs cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy changes ignored&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx policy ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check for “Managed by ”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completely broken state&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sbx reset&lt;/code&gt; (destructive, keeps secrets optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx stop sec-test 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx &lt;span class="nb"&gt;rm &lt;/span&gt;sec-test 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="c"&gt;# Review remaining sandboxes&lt;/span&gt;
sbx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Learned in the Whole Series
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why microVM isolation exists and how to install/use &lt;code&gt;sbx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Full control over workspace, persistence, network and secrets
&lt;/li&gt;
&lt;li&gt;Running real coding agents + isolated Docker safely
&lt;/li&gt;
&lt;li&gt;Templates, kits, ports, SSH and daily workflows
&lt;/li&gt;
&lt;li&gt;The exact security model, real limitations and how to run this in production&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You now have a complete, practical Zero-to-Hero path for Docker Sandboxes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Production Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Everyone on the team has &lt;code&gt;sbx&lt;/code&gt; installed and logged in
&lt;/li&gt;
&lt;li&gt;[ ] Default is &lt;code&gt;--clone&lt;/code&gt; for agent sessions
&lt;/li&gt;
&lt;li&gt;[ ] Secrets only via &lt;code&gt;sbx secret&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Network policy starts restrictive
&lt;/li&gt;
&lt;li&gt;[ ] Named sandboxes + regular cleanup
&lt;/li&gt;
&lt;li&gt;[ ] VS Code / Cursor connected via SSH when needed
&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;sbx diagnose&lt;/code&gt; is the first troubleshooting step
&lt;/li&gt;
&lt;li&gt;[ ] Team knows the limitations (especially direct mount and resource use)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;You have finished the Docker Sandboxes series.&lt;br&gt;
I'll see you again with another interesting series.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/security/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/security/&lt;/a&gt; — security model overview
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/security/isolation/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/security/isolation/&lt;/a&gt; — detailed isolation layers
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/troubleshooting/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/troubleshooting/&lt;/a&gt; — diagnostics and common fixes
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/governance/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/governance/&lt;/a&gt; — organisation governance
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/faq/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/faq/&lt;/a&gt; — practical limitations and tips
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/architecture/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/architecture/&lt;/a&gt; — comparison with other approaches
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All technical claims and commands verified against current official documentation (August 2026).&lt;/p&gt;




</description>
      <category>ai</category>
      <category>docker</category>
      <category>agents</category>
      <category>containers</category>
    </item>
    <item>
      <title>Templates, Kits, Ports, SSH Integrations &amp; Daily Workflows</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Thu, 13 Aug 2026 05:14:44 +0000</pubDate>
      <link>https://dev.to/vellanki/templates-kits-ports-ssh-integrations-daily-workflows-18nh</link>
      <guid>https://dev.to/vellanki/templates-kits-ports-ssh-integrations-daily-workflows-18nh</guid>
      <description>&lt;p&gt;&lt;strong&gt;Blog 4 — Docker Sandboxes Series&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will turn a one-off sandbox into a reusable, daily development environment.&lt;/p&gt;

&lt;p&gt;You will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand and use custom templates&lt;/li&gt;
&lt;li&gt;Apply kits for extra tools, network rules and startup behaviour&lt;/li&gt;
&lt;li&gt;Publish ports so you can open services from your browser&lt;/li&gt;
&lt;li&gt;Connect VS Code or Cursor to the sandbox over SSH&lt;/li&gt;
&lt;li&gt;Use the interactive TUI and practical day-to-day patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this blog a sandbox should feel like a normal remote development machine — just safer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In Blog 3 we successfully ran agents and used Docker inside the sandbox. That is powerful, but repeating the same setup every time is tiring.&lt;/p&gt;

&lt;p&gt;You want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same tools pre-installed for every team member&lt;/li&gt;
&lt;li&gt;Easy access to the web app the agent just started&lt;/li&gt;
&lt;li&gt;Your favourite editor (VS Code / Cursor) connected to the isolated environment&lt;/li&gt;
&lt;li&gt;A clean way to start, stop and manage multiple sandboxes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Templates, kits, ports and SSH give you exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blogs 1–3 completed&lt;/li&gt;
&lt;li&gt;A working project (we will reuse &lt;code&gt;agent-lab&lt;/code&gt; or create a fresh one)&lt;/li&gt;
&lt;li&gt;VS Code or Cursor installed (optional but recommended for the SSH part)&lt;/li&gt;
&lt;li&gt;Docker Desktop is required &lt;strong&gt;only&lt;/strong&gt; if you want to build your own template image&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Clean previous test sandboxes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx &lt;span class="nb"&gt;ls
&lt;/span&gt;sbx stop &lt;span class="si"&gt;$(&lt;/span&gt;sbx &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;sbx &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx &lt;span class="nb"&gt;ls
&lt;/span&gt;No sandboxes found.
Launch one: sbx run claude
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go back to the project (or recreate it quickly):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;agent-lab &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; agent-lab &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;agent-lab&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm on agent-lab directory&lt;br&gt;
&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ &lt;span class="nb"&gt;pwd&lt;/span&gt;
/Users/koti/myFuture/writings/docker/sandboxes/agent-lab
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1 — Custom Templates (Reusable Base Environments)
&lt;/h2&gt;

&lt;p&gt;Official templates live under &lt;code&gt;docker/sandbox-templates:&amp;lt;variant&amp;gt;&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Most agents already use the &lt;code&gt;-docker&lt;/code&gt; variant by default (full Docker Engine inside the microVM).&lt;/p&gt;

&lt;p&gt;You can point to any template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; templated &lt;span class="nt"&gt;--template&lt;/span&gt; docker.io/docker/sandbox-templates:shell-docker shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9j39cw97q17445lht18p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9j39cw97q17445lht18p.png" alt="sbx run template" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building your own template (optional)
&lt;/h3&gt;

&lt;p&gt;If you have Docker Desktop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; docker/sandbox-templates:shell-docker&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    tree &lt;span class="se"&gt;\
&lt;/span&gt;    jq &lt;span class="se"&gt;\
&lt;/span&gt;    httpie &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# build docker image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-dev-template:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3l0khsljv5o754o78c8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3l0khsljv5o754o78c8r.png" alt="sbx template docker build" width="800" height="438"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#run the container&lt;/span&gt;
sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; custom-template &lt;span class="nt"&gt;--template&lt;/span&gt; my-dev-template:v1 shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl4l753ezgksy5jp5x8m3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl4l753ezgksy5jp5x8m3.png" alt="sbx run" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This error is expected&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker Sandboxes cannot see images that exist only in your local Docker Desktop / Docker Engine.The sbx runtime has its own separate image store. When you run the following command it tries to pull the image from a registry → gets 403 Forbidden because the image only exists locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; custom-template &lt;span class="nt"&gt;--template&lt;/span&gt; my-dev-template:v1 shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Correct way to use a locally built custom template
&lt;/h3&gt;

&lt;p&gt;Follow these exact steps:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Save your local image as a tar file
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image save my-dev-template:v1 &lt;span class="nt"&gt;-o&lt;/span&gt; my-dev-template.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Load it into the Sandbox runtime
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx template load my-dev-template.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Now run the sandbox using the template
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; custom-template &lt;span class="nt"&gt;--template&lt;/span&gt; my-dev-template:v1 shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7pahjirmazttju7hpc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7pahjirmazttju7hpc5.png" alt="sbx run commands description" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Useful commands after loading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See templates available to sbx in host terminal&lt;/span&gt;
sbx template &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx template &lt;span class="nb"&gt;ls
&lt;/span&gt;REPOSITORY                           TAG                  IMAGE ID       FLAVOR               CREATED
docker.io/docker/sandbox-templates   claude-code-docker   ae8a46a10575   claude-code-docker   3 days ago
docker.io/docker/sandbox-templates   shell-docker         d86a6cdc105a   shell-docker         3 days ago
docker.io/library/my-dev-template    v1                   b403131ff1e6   shell-docker         2 minutes ago
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove a template if needed&lt;/span&gt;
sbx template &lt;span class="nb"&gt;rm &lt;/span&gt;my-dev-template:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Inside the new sandbox you already have &lt;code&gt;tree&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt; and &lt;code&gt;httpie&lt;/code&gt; without installing them again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agent@custom-template:agent-lab&lt;span class="nv"&gt;$ &lt;/span&gt;tree
&lt;span class="nb"&gt;.&lt;/span&gt;
|-- Dockerfile
&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;--&lt;/span&gt; my-dev-template.tar

1 directory, 2 files
agent@custom-template:agent-lab&lt;span class="nv"&gt;$ &lt;/span&gt;jq
jq - commandline JSON processor &lt;span class="o"&gt;[&lt;/span&gt;version 1.8.1]

Usage:  jq &lt;span class="o"&gt;[&lt;/span&gt;options] &amp;lt;jq filter&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;file...]
    jq &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="nt"&gt;--args&lt;/span&gt; &amp;lt;jq filter&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;strings...]
    jq &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="nt"&gt;--jsonargs&lt;/span&gt; &amp;lt;jq filter&amp;gt; &lt;span class="o"&gt;[&lt;/span&gt;JSON_TEXTS...]

jq is a tool &lt;span class="k"&gt;for &lt;/span&gt;processing JSON inputs, applying the given filter to
its JSON text inputs and producing the filter&lt;span class="s1"&gt;'s results as JSON on
standard output.

The simplest filter is ., which copies jq'&lt;/span&gt;s input to its output
unmodified except &lt;span class="k"&gt;for &lt;/span&gt;formatting. For more advanced filters see
the jq&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt; manpage &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"man jq"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; and/or https://jqlang.org/.

Example:

    &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"foo": 0}'&lt;/span&gt; | jq &lt;span class="nb"&gt;.&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"foo"&lt;/span&gt;: 0
    &lt;span class="o"&gt;}&lt;/span&gt;

For listing the &lt;span class="nb"&gt;command &lt;/span&gt;options, use jq &lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
agent@custom-template:agent-lab&lt;span class="nv"&gt;$ &lt;/span&gt;httpie
usage: httpie &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-h&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--debug&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--traceback&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;cli,plugins&lt;span class="o"&gt;}&lt;/span&gt; ...
httpie: error: Please specify one of these: &lt;span class="s1"&gt;'cli'&lt;/span&gt;, &lt;span class="s1"&gt;'plugins'&lt;/span&gt;

This &lt;span class="nb"&gt;command &lt;/span&gt;is only &lt;span class="k"&gt;for &lt;/span&gt;managing HTTPie plugins.
To send a request, please use the http/https commands:

  &lt;span class="nv"&gt;$ &lt;/span&gt;http POST pie.dev/post &lt;span class="nv"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;world

  &lt;span class="nv"&gt;$ &lt;/span&gt;https POST pie.dev/post &lt;span class="nv"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;world

agent@custom-template:agent-lab&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Template vs Kit Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frjic3edxks14lgem6efv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frjic3edxks14lgem6efv.png" alt="template vs kit" width="800" height="793"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A &lt;strong&gt;template&lt;/strong&gt; is a Docker image — heavy things that rarely change (language runtimes, system packages, Docker itself).&lt;br&gt;&lt;br&gt;
A &lt;strong&gt;kit&lt;/strong&gt; is applied at runtime — lighter, declarative additions (extra tools, network allow-list, startup scripts, files).&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 — Kits (Declarative Extensions)
&lt;/h2&gt;

&lt;p&gt;Kits are YAML files (&lt;code&gt;spec.yaml&lt;/code&gt;). They can be local directories, Git URLs or OCI artifacts.&lt;/p&gt;

&lt;p&gt;Simple example — create a small kit that adds a useful tool and allows a domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; my-kit

&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-kit/spec.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
schemaVersion: "1"
kind: mixin
name: handy-tools
description: Adds a few everyday tools and allows example.com

commands:
  startup:
    - command: ["bash", "-c", "sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y tree jq || true"]
      user: "root"

network:
  allowedDomains:
    - "example.com:443"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run with the kit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; with-kit &lt;span class="nt"&gt;--kit&lt;/span&gt; my-kit shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the sandbox &lt;code&gt;jq&lt;/code&gt; is available and &lt;code&gt;example.com&lt;/code&gt; is reachable.&lt;/p&gt;

&lt;p&gt;You can stack multiple kits with repeated &lt;code&gt;--kit&lt;/code&gt; flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Publishing Ports
&lt;/h2&gt;

&lt;p&gt;*&lt;em&gt;This we have covered in previous blog but for the simplicity and to get familiarised I'm adding detailed section for ports here. *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sandboxes are network-isolated by default. To open a service from your host browser you must publish a port.&lt;/p&gt;

&lt;p&gt;Start a simple server inside a sandbox (or let the agent do it):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; port-demo &lt;span class="nt"&gt;--clone&lt;/span&gt; shell
&lt;span class="c"&gt;# inside the sandbox&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; http.server 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the &lt;strong&gt;host&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx ports port-demo &lt;span class="nt"&gt;--publish&lt;/span&gt; 8000
&lt;span class="c"&gt;# or pin a host port&lt;/span&gt;
sbx ports port-demo &lt;span class="nt"&gt;--publish&lt;/span&gt; 8080:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx ports port-demo
sbx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx ports port-demo &lt;span class="nt"&gt;--publish&lt;/span&gt; 8080:8000
Published 127.0.0.1:8080 -&amp;gt; 8000/tcp
Published &lt;span class="o"&gt;[&lt;/span&gt;::1]:8080 -&amp;gt; 8000/tcp
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx ports port-demo
HOST IP     HOST PORT   SANDBOX PORT   PROTOCOL
127.0.0.1   8080        8000           tcp
::1         8080        8000           tcp
127.0.0.1   49156       9418           tcp
127.0.0.1   49157       8000           tcp
::1         49157       8000           tcp
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx &lt;span class="nb"&gt;ls
&lt;/span&gt;SANDBOX     AGENT   STATUS    PORTS                                                                                                                     WORKSPACE
port-demo   shell   running   127.0.0.1:8080-&amp;gt;8000/tcp, ::1:8080-&amp;gt;8000/tcp, 127.0.0.1:49157-&amp;gt;8000/tcp, ::1:49157-&amp;gt;8000/tcp, 127.0.0.1:49156-&amp;gt;9418/tcp   /Users/koti/myFuture/writings/docker/sandboxes/agent-lab
with-kit    shell   stopped                                                                                                                             /Users/koti/myFuture/writings/docker/sandboxes
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt; (or the ephemeral port shown).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0x2pajqqehpm9t72jtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0x2pajqqehpm9t72jtu.png" alt="sbx hello" width="794" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unpublish when finished:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx ports port-demo &lt;span class="nt"&gt;--unpublish&lt;/span&gt; 8080:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Port Publishing Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2jjxacj5yo1sz6iymcx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2jjxacj5yo1sz6iymcx8.png" alt="port publishing" width="440" height="972"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Traffic from your browser hits a port on the host. &lt;code&gt;sbx ports&lt;/code&gt; forwards it into the microVM. The app never needs to bind to the host network directly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4 — SSH Integration (VS Code / Cursor)
&lt;/h2&gt;

&lt;p&gt;One-time setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx setup ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx setup ssh
SSH ──────────────────────────────────────────────────────
Status       ● enabled · signed &lt;span class="k"&gt;in &lt;/span&gt;as vellankikoti
Access       &lt;span class="k"&gt;*&lt;/span&gt;.sbx -&amp;gt; sandboxd.sock &lt;span class="o"&gt;(&lt;/span&gt;no port · login-gated&lt;span class="o"&gt;)&lt;/span&gt;

Connect ──────────────────────────────────────────────────────
Terminal     ssh &amp;lt;sandboxname&amp;gt;.sbx
File access  sftp &amp;lt;sandboxname&amp;gt;.sbx
Copy files   scp myfile.txt &amp;lt;sandboxname&amp;gt;.sbx:/path/to/workspace/

You can safely re-run sbx setup ssh to reconfigure
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This writes a managed block into your SSH config so any &lt;code&gt;*.sbx&lt;/code&gt; host is handled by the sbx daemon.&lt;/p&gt;

&lt;p&gt;Create or use a named sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx create &lt;span class="nt"&gt;--name&lt;/span&gt; editor-demo shell &lt;span class="nb"&gt;.&lt;/span&gt;
sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; editor-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpkzdq2elsh7qzu1yo82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbpkzdq2elsh7qzu1yo82.png" alt="sbx create run" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh editor-demo.sbx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx &lt;span class="nb"&gt;ls
&lt;/span&gt;SANDBOX       AGENT   STATUS    PORTS   WORKSPACE
editor-demo   shell   running           /Users/koti/myFuture/writings/docker/sandboxes/agent-lab
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ 
➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; editor-demo


Starting shell agent &lt;span class="k"&gt;in &lt;/span&gt;sandbox &lt;span class="s1"&gt;'editor-demo'&lt;/span&gt;...
Workspace: /Users/koti/myFuture/writings/docker/sandboxes/agent-lab

agent@editor-demo:agent-lab&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;multi_line.txt
agent@editor-demo:agent-lab&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Try SSH to sbx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  agent-lab git:&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ ssh editor-demo.sbx       

Connecting to sandbox &lt;span class="s2"&gt;"editor-demo"&lt;/span&gt;…
agent@editor-demo:workspace&lt;span class="nv"&gt;$ &lt;/span&gt;
agent@editor-demo:workspace&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
/home/agent/workspace
agent@editor-demo:workspace&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /Users/koti/myFuture/writings/docker/sandboxes/agent-lab
agent@editor-demo:agent-lab&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls
&lt;/span&gt;multi_line.txt
agent@editor-demo:agent-lab&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSH Connection Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmg5yo2rku6d1krhg0kf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmg5yo2rku6d1krhg0kf4.png" alt="ssh connection" width="762" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The editor talks to a local proxy provided by &lt;code&gt;sbx&lt;/code&gt;. No inbound port is opened on the microVM. Authentication is tied to your Docker login. The sandbox starts automatically if it was stopped.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5 — Daily Workflow Patterns &amp;amp; TUI
&lt;/h2&gt;

&lt;p&gt;Launch the interactive dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a live view of all sandboxes (CPU, memory, status). From here you can create, start/stop, attach, open a shell or remove sandboxes. There is also a network panel.&lt;/p&gt;

&lt;p&gt;Practical patterns I use every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Named sandboxes per feature: &lt;code&gt;--name feature-login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Always prefer &lt;code&gt;--clone&lt;/code&gt; for agent work&lt;/li&gt;
&lt;li&gt;Publish only the ports you need&lt;/li&gt;
&lt;li&gt;Connect the editor once and leave it attached&lt;/li&gt;
&lt;li&gt;Clean up with &lt;code&gt;sbx rm&lt;/code&gt; when the task is done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiple workspaces example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; multi claude ~/main-project ~/shared-libs:ro ~/docs:ro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let’s Break It
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Publish a port, open the service, then unpublish and confirm the browser can no longer reach it.
&lt;/li&gt;
&lt;li&gt;Connect VS Code, make a change, and confirm it appears both in the editor and on the host (or via the clone remote).
&lt;/li&gt;
&lt;li&gt;Apply a kit that denies a domain and watch the agent fail to reach it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In a real team I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publish a small set of internal templates to a private registry&lt;/li&gt;
&lt;li&gt;Keep common kits in the project repository (&lt;code&gt;./kits/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Document the exact &lt;code&gt;sbx run ... --kit ...&lt;/code&gt; command in the README&lt;/li&gt;
&lt;li&gt;Prefer SSH + editor over pure terminal for longer sessions&lt;/li&gt;
&lt;li&gt;Use the TUI for quick overview of resource usage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Templates and kits can install packages with root inside the microVM — treat them like any other code you run&lt;/li&gt;
&lt;li&gt;Port publishing intentionally punches a controlled hole from host → sandbox&lt;/li&gt;
&lt;li&gt;SSH access is still subject to the same network policy and workspace rules&lt;/li&gt;
&lt;li&gt;Kits can declare their own network allow-lists; those rules are enforced&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting &lt;code&gt;sbx setup ssh&lt;/code&gt; before trying to connect an editor&lt;/li&gt;
&lt;li&gt;Publishing ports on a stopped sandbox&lt;/li&gt;
&lt;li&gt;Building templates without Docker Desktop&lt;/li&gt;
&lt;li&gt;Putting secrets inside a template image instead of using &lt;code&gt;sbx secret&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Leaving many sandboxes with large Docker image caches&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;*.sbx&lt;/code&gt; not resolving&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx setup ssh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run the setup command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port not reachable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx ports &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish again or check the mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editor cannot find folder&lt;/td&gt;
&lt;td&gt;Absolute path inside sandbox&lt;/td&gt;
&lt;td&gt;Use the host path that was mounted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kit not applying&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sbx ls&lt;/code&gt; / logs&lt;/td&gt;
&lt;td&gt;Check kit path and &lt;code&gt;spec.yaml&lt;/code&gt; syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template pull fails&lt;/td&gt;
&lt;td&gt;Image name&lt;/td&gt;
&lt;td&gt;Use full &lt;code&gt;docker.io/...&lt;/code&gt; reference&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Useful commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx ports &amp;lt;name&amp;gt;
sbx setup ssh
ssh &amp;lt;name&amp;gt;.sbx
sbx
sbx &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;name&amp;gt; bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx stop port-demo editor-demo with-kit custom-template 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx &lt;span class="nb"&gt;rm &lt;/span&gt;port-demo editor-demo with-kit custom-template 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Templates give you reusable base images with tools pre-installed
&lt;/li&gt;
&lt;li&gt;Kits add lightweight, declarative capabilities at runtime
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sbx ports&lt;/code&gt; makes services inside the sandbox reachable from the host
&lt;/li&gt;
&lt;li&gt;SSH turns the sandbox into a first-class remote development target for VS Code and Cursor
&lt;/li&gt;
&lt;li&gt;The combination makes daily agent work feel natural and safe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have everything needed for comfortable, repeatable development inside sandboxes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In the final blog (Blog 5) we go deep into the security model, realistic troubleshooting, known limitations, organisation governance concepts, and production best practices so you can introduce Docker Sandboxes to a real team with confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/customize/templates/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/customize/templates/&lt;/a&gt; — templates
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/customize/kits/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/customize/kits/&lt;/a&gt; — kits overview and examples
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/integrations/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/integrations/&lt;/a&gt; — SSH, VS Code, Cursor
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/usage/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/usage/&lt;/a&gt; — ports, TUI, workflows
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/reference/cli/sbx/ports/" rel="noopener noreferrer"&gt;https://docs.docker.com/reference/cli/sbx/ports/&lt;/a&gt; — port command reference
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/customize/kit-reference/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/customize/kit-reference/&lt;/a&gt; — full kit spec
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Running Coding Agents + Docker Inside the Sandbox</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Tue, 11 Aug 2026 13:12:37 +0000</pubDate>
      <link>https://dev.to/vellanki/running-coding-agents-docker-inside-the-sandbox-eae</link>
      <guid>https://dev.to/vellanki/running-coding-agents-docker-inside-the-sandbox-eae</guid>
      <description>&lt;p&gt;&lt;strong&gt;Blog 3 — Docker Sandboxes Series&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launch real coding agents (Claude, Gemini, Codex, Copilot, OpenCode, Kiro, etc.) inside a sandbox&lt;/li&gt;
&lt;li&gt;Give an agent an actual development task and watch it work safely&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;isolated Docker daemon&lt;/strong&gt; that lives inside every sandbox to build and run containers&lt;/li&gt;
&lt;li&gt;Prove that nothing touches your host Docker or host system packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the point where sandboxes stop being a demo and become a daily tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In the first two blogs we built a safe environment and learned how to control files, network and secrets.  &lt;/p&gt;

&lt;p&gt;Now we put a real agent inside that environment. The agent can install packages, edit code, run tests &lt;strong&gt;and&lt;/strong&gt; build Docker images — all without risk to your laptop.&lt;/p&gt;

&lt;p&gt;The key design point is this: every sandbox comes with its &lt;strong&gt;own Docker daemon&lt;/strong&gt;. The agent can run &lt;code&gt;docker build&lt;/code&gt;, &lt;code&gt;docker run&lt;/code&gt; and even &lt;code&gt;docker compose&lt;/code&gt; freely. Those containers live only inside the microVM. Your host Docker stays completely untouched.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blogs 1 and 2 completed&lt;/li&gt;
&lt;li&gt;A free Docker account (already signed in)&lt;/li&gt;
&lt;li&gt;At least one of the following (optional but recommended):

&lt;ul&gt;
&lt;li&gt;Anthropic API key or Claude subscription (for &lt;code&gt;claude&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;OpenAI API key (for &lt;code&gt;codex&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Google API key (for &lt;code&gt;gemini&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Or just use the &lt;code&gt;shell&lt;/code&gt; agent if you have no keys yet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will show both paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Environment Setup
&lt;/h2&gt;

&lt;p&gt;Clean any old sandboxes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx &lt;span class="nb"&gt;ls
&lt;/span&gt;sbx stop &lt;span class="si"&gt;$(&lt;/span&gt;sbx &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;sbx &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2dzowgyb19oevdmif9l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy2dzowgyb19oevdmif9l.png" alt="sbx ls" width="524" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a realistic small project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; agent-lab
&lt;span class="nb"&gt;cd &lt;/span&gt;agent-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store secrets if you have them (examples):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Anthropic (Claude)&lt;/span&gt;
sbx secret &lt;span class="nb"&gt;set &lt;/span&gt;anthropic

&lt;span class="c"&gt;# or OpenAI (Codex)&lt;/span&gt;
&lt;span class="c"&gt;# sbx secret set openai&lt;/span&gt;

&lt;span class="c"&gt;# GitHub (useful for most agents)&lt;/span&gt;
sbx secret &lt;span class="nb"&gt;set &lt;/span&gt;github &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh auth token&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Recommended Method (Claude Subscription – Pro / Max / Team / Enterprise)
&lt;/h3&gt;

&lt;p&gt;You do &lt;strong&gt;not&lt;/strong&gt; need to run &lt;code&gt;sbx secret set anthropic&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exact steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start Claude Code Inside the Sandbox:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; agent-lab &lt;span class="nt"&gt;--clone&lt;/span&gt; claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(or whatever name/workspace you are using)&lt;br&gt;
&lt;strong&gt;What happens:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new microVM starts&lt;/li&gt;
&lt;li&gt;Claude Code launches inside it&lt;/li&gt;
&lt;li&gt;You will see the Claude Code interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are now &lt;strong&gt;inside Claude Code&lt;/strong&gt; (running inside the sandbox).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8twiwej1hl2kdrnwu0z1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8twiwej1hl2kdrnwu0z1.png" alt="start claude sbx" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Once Claude Code starts inside the sandbox, type this command:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   /login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0m9ix3mktepf8f5611sw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0m9ix3mktepf8f5611sw.png" alt="claude login" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Claude will open a browser window (or give you a link) for OAuth login.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sign in with your Claude subscription account (the same one you use on claude.ai).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After successful login you will see something like:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Login successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhaoakp09ecic4pp88gym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhaoakp09ecic4pp88gym.png" alt="login success browser" width="798" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpichegmvgwxymvg3ntv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpichegmvgwxymvg3ntv1.png" alt="login success local" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can now start chatting with Claude. The session token stays on your &lt;strong&gt;host&lt;/strong&gt; and is never stored inside the sandbox.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Step 2 — Ask Claude to Create the Application
&lt;/h3&gt;

&lt;p&gt;Still inside the &lt;strong&gt;Claude Code&lt;/strong&gt; session, paste this prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a simple Flask application with these files:

1. app.py – A Flask app that has two routes:
   - / → returns "Hello from Docker Sandbox!"
   - /health → returns {"status": "ok"}

2. requirements.txt – contains flask

3. Dockerfile – Use python:3.12-slim, install requirements, expose port 5000, and run the app.

Make the code clean and production-ready.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude will create the files for you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhj7utzcoe51cjvkkn9bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhj7utzcoe51cjvkkn9bn.png" alt="creating app with claude" width="798" height="186"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3 — Use Docker Inside the Sandbox
&lt;/h3&gt;

&lt;p&gt;You now have two clean ways to work with Docker.&lt;/p&gt;

&lt;h4&gt;
  
  
  Method A: Let Claude do everything (Recommended)
&lt;/h4&gt;

&lt;p&gt;Still inside Claude Code, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build the Docker image, run the container on port 5000, and test both endpoints using curl.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude will run all the Docker commands itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8qtatur88t65g3wde5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg8qtatur88t65g3wde5k.png" alt="build image run container" width="798" height="186"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  Method B: Run Docker commands yourself (Manual control)
&lt;/h4&gt;

&lt;p&gt;If you want to type the Docker commands yourself, follow these exact steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep the Claude Code terminal open&lt;/strong&gt; (do not close it).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open a &lt;strong&gt;new terminal&lt;/strong&gt; on your host machine and run:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; agent-lab bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are now inside a normal Linux shell &lt;strong&gt;inside the same sandbox&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run these commands one by one:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; agent-lab-app &lt;span class="nb"&gt;.&lt;/span&gt;
docker images
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 5000:5000 &lt;span class="nt"&gt;--name&lt;/span&gt; demo agent-lab-app
docker ps
curl http://localhost:5000
curl http://localhost:5000/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;When finished, type &lt;code&gt;exit&lt;/code&gt; to leave this shell.
The Claude Code session in the other terminal remains running.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Important Notes (Read Carefully)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You pressed &lt;code&gt;Ctrl + C&lt;/code&gt; or &lt;code&gt;Ctrl + X&lt;/code&gt; and Claude stopped&lt;/td&gt;
&lt;td&gt;Just run &lt;code&gt;sbx run --name agent-lab&lt;/code&gt; again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want a normal shell&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;sbx exec -it agent-lab bash&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want Claude to do the work&lt;/td&gt;
&lt;td&gt;Stay in the Claude Code terminal and give instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want to check running containers&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;sbx exec -it agent-lab bash&lt;/code&gt; then &lt;code&gt;docker ps&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1ji5rkjilv22d2616qm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1ji5rkjilv22d2616qm.png" alt="sbx exec" width="797" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 — Publish the Port (Optional)
&lt;/h3&gt;

&lt;p&gt;If you want to access the app from your host browser:&lt;/p&gt;

&lt;p&gt;On your &lt;strong&gt;sandbox&lt;/strong&gt; run app:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhs920ylo95mvao30i78k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhs920ylo95mvao30i78k.png" alt="running app in sbx" width="800" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On your &lt;strong&gt;host machine&lt;/strong&gt; run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx ports agent-lab &lt;span class="nt"&gt;--publish&lt;/span&gt; 8080:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open: &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimhaa8qmla9r4tw9c1yw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fimhaa8qmla9r4tw9c1yw.png" alt="sbx port publish" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 5 — Clean Up
&lt;/h3&gt;

&lt;p&gt;When you are finished:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On host machine&lt;/span&gt;
sbx stop agent-lab
sbx &lt;span class="nb"&gt;rm &lt;/span&gt;agent-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fef9zjryrha06ds2sllwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fef9zjryrha06ds2sllwy.png" alt="sbx cleanup" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/agents/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/agents/&lt;/a&gt; — supported agents list
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/usage/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/usage/&lt;/a&gt; — running, attaching, reconnecting
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/architecture/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/architecture/&lt;/a&gt; — isolated Docker daemon
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/get-started/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/get-started/&lt;/a&gt; — first agent session examples
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/reference/cli/sbx/run/" rel="noopener noreferrer"&gt;https://docs.docker.com/reference/cli/sbx/run/&lt;/a&gt; — exact CLI options
&lt;/li&gt;
&lt;li&gt;Individual agent pages under &lt;a href="https://docs.docker.com/ai/sandboxes/agents/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/agents/&lt;/a&gt; (claude, gemini, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>containers</category>
      <category>agents</category>
    </item>
    <item>
      <title>Docker Sandbox Workspace, Network Policies &amp; Secrets — Complete Practical Guide (2026)</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Mon, 10 Aug 2026 12:35:33 +0000</pubDate>
      <link>https://dev.to/vellanki/docker-sandbox-workspace-network-policies-secrets-complete-practical-guide-2026-3hnb</link>
      <guid>https://dev.to/vellanki/docker-sandbox-workspace-network-policies-secrets-complete-practical-guide-2026-3hnb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Blog 2 — Docker Sandboxes Series&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Sandbox Workspace, Network Policies &amp;amp; Secrets — Complete Practical Guide (2026)
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will fully control three critical things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How the agent sees and changes your files (direct mount vs clone mode)
&lt;/li&gt;
&lt;li&gt;What survives when you stop a sandbox and what is deleted when you remove it
&lt;/li&gt;
&lt;li&gt;Exactly which network destinations the agent can reach, and how secrets are injected safely without ever entering the microVM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will break things on purpose, fix them, and leave with a clean, controlled environment ready for real agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In Blog 1 we launched a sandbox and saw basic isolation. That is not enough for real work.&lt;/p&gt;

&lt;p&gt;If the agent can freely rewrite your entire project and can reach any website, one wrong command can still cause pain.&lt;br&gt;&lt;br&gt;
If secrets sit as environment variables inside the sandbox, the agent (or any code it runs) can read them.&lt;br&gt;&lt;br&gt;
If you do not understand what survives a &lt;code&gt;stop&lt;/code&gt;, you will keep losing installed packages or Docker images.&lt;/p&gt;

&lt;p&gt;This blog gives you the practical controls an experienced engineer actually uses every day.&lt;/p&gt;
&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blog 1 completed (sbx installed and working)
&lt;/li&gt;
&lt;li&gt;A project directory (we will reuse or recreate &lt;code&gt;~/sandbox-lab&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Optional but useful: &lt;code&gt;gh&lt;/code&gt; CLI if you want to test GitHub secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;

&lt;p&gt;Make sure you are in a clean state from Blog 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# If any sandbox is still running, stop and remove it&lt;/span&gt;
sbx stop first-sandbox 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx &lt;span class="nb"&gt;rm &lt;/span&gt;first-sandbox 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feodpsk0u1lcx0nbtg3rd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feodpsk0u1lcx0nbtg3rd.png" alt="cleaning up old sbx" width="799" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a fresh project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; sandbox-lab
&lt;span class="nb"&gt;cd &lt;/span&gt;sandbox-lab
git init
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello from host - direct mode"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; note.txt
git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"initial"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffmnej7ku4z9zlui62kuz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffmnej7ku4z9zlui62kuz.png" alt="creating sbx project" width="799" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Direct Mount (Default Behaviour)
&lt;/h2&gt;

&lt;p&gt;Launch a normal sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; direct-sandbox shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4d7wrp645xy3inurh71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4d7wrp645xy3inurh71.png" alt="launching direct sbx" width="800" height="784"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd
cat &lt;/span&gt;note.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"changed by agent"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; note.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"new file from agent"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; agent-file.txt
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46fl1uc3jfgysw5m96en.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F46fl1uc3jfgysw5m96en.png" alt="inside sbx" width="799" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exit the sandbox (Ctrl+D or &lt;code&gt;exit&lt;/code&gt;) or open terminal in the sandbox directory.&lt;/p&gt;

&lt;p&gt;On the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls 
cat &lt;/span&gt;note.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23130yknkf0tge4encnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23130yknkf0tge4encnf.png" alt="ls cat" width="800" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You see the changes immediately. This is the default &lt;strong&gt;direct mount&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Mount Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwfsto16igeavk5n2p6pz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwfsto16igeavk5n2p6pz.png" alt="direct mount" width="732" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The project folder on your host is mounted directly into the microVM at the exact same path. Any change the agent makes appears on your host instantly, and vice versa. There is no copy and no sync delay.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 — Clone Mode (Safer Isolation for Files)
&lt;/h2&gt;

&lt;p&gt;Remove the previous sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx stop direct-sandbox
sbx &lt;span class="nb"&gt;rm &lt;/span&gt;direct-sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc9m0b7k2gt5zqyyfqcrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc9m0b7k2gt5zqyyfqcrq.png" alt="remove sbx" width="798" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now create one with &lt;code&gt;--clone&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; clone-sandbox &lt;span class="nt"&gt;--clone&lt;/span&gt; shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgg8380b9ez8ask3pz606.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgg8380b9ez8ask3pz606.png" alt="clone sandbox" width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd
ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;note.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"this change stays inside the clone"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; note.txt
git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb1lkrk7mpe9b1j5fil5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb1lkrk7mpe9b1j5fil5f.png" alt="inside clone sbx" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;note.txt
&lt;span class="c"&gt;# The host file is unchanged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtpkchx8ngpy5zmghbac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtpkchx8ngpy5zmghbac.png" alt="inside host" width="799" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent is working on a &lt;strong&gt;private Git clone&lt;/strong&gt; inside the microVM. Your original files are mounted read-only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone Mode Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F15deyiybyb96ixzunkhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F15deyiybyb96ixzunkhr.png" alt="clone sbx diagram" width="800" height="1021"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Start from the host repository (mounted read-only). The sandbox creates its own private clone. The agent edits only that clone. When you want the changes, you fetch from the special remote &lt;code&gt;sandbox-&amp;lt;name&amp;gt;&lt;/code&gt; that Docker automatically adds on the host. Removing the sandbox deletes the private clone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: &lt;code&gt;--clone&lt;/code&gt; can only be set at creation time. You cannot switch an existing sandbox later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 — Persistence: What Survives Stop vs Remove
&lt;/h2&gt;

&lt;p&gt;Inside any running sandbox install something:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# still inside clone-sandbox or start a new one&lt;/span&gt;
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; tree

&lt;span class="c"&gt;#if command fails due to root privileges use then use the following command&lt;/span&gt;

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; tree

docker pull alpine:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop it (state is kept):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# from host&lt;/span&gt;
sbx &lt;span class="nb"&gt;ls
&lt;/span&gt;sbx stop clone-sandbox
sbx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fth4zcimsz9rvndj021ke.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fth4zcimsz9rvndj021ke.png" alt="stop current sbx" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; clone-sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tree &lt;span class="nt"&gt;--version&lt;/span&gt;
docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fki3h2f71a52z073pqtri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fki3h2f71a52z073pqtri.png" alt="resume sbx" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything is still there.&lt;/p&gt;

&lt;p&gt;Now remove it completely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx stop clone-sandbox
sbx &lt;span class="nb"&gt;rm &lt;/span&gt;clone-sandbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1thf6s5nxklosi8udy23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1thf6s5nxklosi8udy23.png" alt="remove sbx completely" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you recreate a sandbox with the same name, the packages and images are gone. That is expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lifecycle Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fymesflu5w39tgm6c7vt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fymesflu5w39tgm6c7vt2.png" alt="lifecycle diagram" width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;stop&lt;/code&gt; pauses the microVM but keeps the disk state (packages, images, agent history).&lt;br&gt;&lt;br&gt;
&lt;code&gt;rm&lt;/code&gt; deletes the entire microVM and everything inside it. Host project files are never touched.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4 — Network Policies
&lt;/h2&gt;

&lt;p&gt;List current rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the Balanced preset rules (or whatever you chose earlier).&lt;/p&gt;

&lt;p&gt;Allow a specific host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy allow network docker.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deny something:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy deny network docs.docker.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk1sjwlrb0gwzaihppw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk1sjwlrb0gwzaihppw4.png" alt="host firewall rules" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test from inside a sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; net-test shell
&lt;span class="c"&gt;# inside&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://docker.com
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://docs.docker.com   &lt;span class="c"&gt;# should fail if denied&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgyiko7q0ijs4mbtf1j55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgyiko7q0ijs4mbtf1j55.png" alt="sbx net-test" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmz8xct4dm76sedlv4noe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmz8xct4dm76sedlv4noe.png" alt="test firewall rules" width="799" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rules apply immediately. Deny always wins over allow.&lt;br&gt;&lt;br&gt;
You can scope a rule to one sandbox with &lt;code&gt;--sandbox &amp;lt;name&amp;gt;&lt;/code&gt; or keep it global (default).&lt;/p&gt;
&lt;h3&gt;
  
  
  Network Flow Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh13tp8nv11wde7g7g5nk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh13tp8nv11wde7g7g5nk.png" alt="network flow diagram" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Every outbound HTTP/HTTPS request from the sandbox goes through a proxy that lives on your host. The proxy checks the policy rules. Non-HTTP traffic (raw TCP, UDP, ICMP) is blocked at the network layer and cannot be opened with normal policy rules (except limited IP:port cases).&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5 — Secrets (The Safe Way)
&lt;/h2&gt;

&lt;p&gt;Never put real tokens as normal environment variables inside the sandbox.&lt;/p&gt;

&lt;p&gt;Store a secret on the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example with GitHub (if you have gh CLI)&lt;/span&gt;
sbx secret &lt;span class="nb"&gt;set &lt;/span&gt;github &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh auth token&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="o"&gt;![&lt;/span&gt;sbx github secret]&lt;span class="o"&gt;(&lt;/span&gt;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/95mykhlfukxre589fo7r.png&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# Or interactively&lt;/span&gt;
sbx secret &lt;span class="nb"&gt;set &lt;/span&gt;anthropic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value is stored in your OS keychain. When the agent makes an HTTP request that needs it, the &lt;strong&gt;host-side proxy&lt;/strong&gt; injects the credential. The raw secret never enters the microVM.&lt;/p&gt;

&lt;p&gt;Verify from the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx secret &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgm654bkrc3179kif6pro.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgm654bkrc3179kif6pro.png" alt="sbx secret ls" width="798" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Credential Injection Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47x1f2hxw2dby5521ab4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47x1f2hxw2dby5521ab4.png" alt="credential injection" width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The agent only sees a normal HTTP request. The proxy quietly adds the authentication header using the secret stored on the host. Even if the agent is compromised, it cannot read the real token.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let’s Break It
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Start a sandbox with Locked Down style policy and try to reach a package registry → it fails.
&lt;/li&gt;
&lt;li&gt;Allow the host → it works.
&lt;/li&gt;
&lt;li&gt;Put a fake secret with &lt;code&gt;sbx secret set&lt;/code&gt; and confirm the agent can use the service without the value appearing in &lt;code&gt;env&lt;/code&gt; inside the sandbox.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In a real project I would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer &lt;code&gt;--clone&lt;/code&gt; when the agent is experimental or when multiple agents run on the same repo
&lt;/li&gt;
&lt;li&gt;Keep network policy as restrictive as possible (start from Balanced or stricter)
&lt;/li&gt;
&lt;li&gt;Always use &lt;code&gt;sbx secret&lt;/code&gt; for any token
&lt;/li&gt;
&lt;li&gt;Name sandboxes clearly and remove them when the task is finished
&lt;/li&gt;
&lt;li&gt;Document the exact &lt;code&gt;sbx policy allow&lt;/code&gt; list the team needs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Direct mount = agent can rewrite your working tree
&lt;/li&gt;
&lt;li&gt;Clone mode = safer file isolation, but you must fetch the work later
&lt;/li&gt;
&lt;li&gt;Secrets injected by proxy are the only safe method
&lt;/li&gt;
&lt;li&gt;Network is deny-by-default; anything not explicitly allowed is blocked
&lt;/li&gt;
&lt;li&gt;Organisation policies (if your company enables them) override local rules completely&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting &lt;code&gt;--clone&lt;/code&gt; is create-time only
&lt;/li&gt;
&lt;li&gt;Expecting &lt;code&gt;stop&lt;/code&gt; to free disk space (only &lt;code&gt;rm&lt;/code&gt; does)
&lt;/li&gt;
&lt;li&gt;Putting secrets in &lt;code&gt;.env&lt;/code&gt; files inside the sandbox
&lt;/li&gt;
&lt;li&gt;Adding too many global allow rules and wondering why isolation feels weak
&lt;/li&gt;
&lt;li&gt;Not checking &lt;code&gt;sbx policy ls&lt;/code&gt; after changing rules&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;How to confirm&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Changes not appearing on host&lt;/td&gt;
&lt;td&gt;Are you in clone mode?&lt;/td&gt;
&lt;td&gt;Fetch from &lt;code&gt;sandbox-&amp;lt;name&amp;gt;&lt;/code&gt; remote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network still blocked&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx policy ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add allow rule or check deny precedence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret not working&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx secret ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-set the secret; restart sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk full&lt;/td&gt;
&lt;td&gt;Many old sandboxes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sbx ls&lt;/code&gt; then &lt;code&gt;sbx rm&lt;/code&gt; unused ones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy not applying&lt;/td&gt;
&lt;td&gt;Org governance active?&lt;/td&gt;
&lt;td&gt;Local rules are ignored&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Useful commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy &lt;span class="nb"&gt;ls
&lt;/span&gt;sbx secret &lt;span class="nb"&gt;ls
&lt;/span&gt;sbx diagnose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx stop net-test clone-sandbox 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx &lt;span class="nb"&gt;rm &lt;/span&gt;net-test clone-sandbox 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;sbx policy &lt;span class="nb"&gt;ls&lt;/span&gt;          &lt;span class="c"&gt;# review and clean rules if needed&lt;/span&gt;
&lt;span class="c"&gt;# Optional full reset of policies (careful)&lt;/span&gt;
&lt;span class="c"&gt;# sbx policy reset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbeus06k3d6qpr2l5acw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbeus06k3d6qpr2l5acw7.png" alt="cleanup sbx" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Direct mount is convenient but gives the agent write access to your real files
&lt;/li&gt;
&lt;li&gt;Clone mode keeps your working tree safe and is the better default for experimental work
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stop&lt;/code&gt; keeps state, &lt;code&gt;rm&lt;/code&gt; destroys it
&lt;/li&gt;
&lt;li&gt;Network is fully controllable with &lt;code&gt;sbx policy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Secrets never need to enter the microVM — the proxy injects them
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You now have complete control over the three things that matter most for safe daily use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In Blog 3 we put everything together: launch real coding agents (Claude, Gemini, Codex, etc.), give them actual development tasks, and use the isolated Docker daemon that lives inside every sandbox to build and run containers safely.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/usage/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/usage/&lt;/a&gt; — workspace and clone mode details
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/architecture/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/architecture/&lt;/a&gt; — mounting, persistence, networking
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/security/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/security/&lt;/a&gt; — isolation layers and credential model
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/security/credentials/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/security/credentials/&lt;/a&gt; — sbx secret behaviour
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/governance/access-controls/local/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/governance/access-controls/local/&lt;/a&gt; — policy commands and presets
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/reference/cli/sbx/policy/" rel="noopener noreferrer"&gt;https://docs.docker.com/reference/cli/sbx/policy/&lt;/a&gt; and related pages — exact CLI syntax
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>docker</category>
      <category>ai</category>
      <category>agents</category>
      <category>containers</category>
    </item>
    <item>
      <title>Docker Sandboxes from Zero to Production: The Complete Hands-on Guide (2026)</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Sun, 09 Aug 2026 18:34:54 +0000</pubDate>
      <link>https://dev.to/vellanki/stop-running-ai-agents-on-your-host-master-docker-sandboxes-in-one-guide-49l2</link>
      <guid>https://dev.to/vellanki/stop-running-ai-agents-on-your-host-master-docker-sandboxes-in-one-guide-49l2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Blog 1 — Docker Sandboxes Series&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Sandboxes from Zero — Why They Exist, Setup, First Sandbox &amp;amp; Basic Isolation
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What We Are Building
&lt;/h2&gt;

&lt;p&gt;By the end of this blog you will understand why AI coding agents are risky on a normal laptop, install the &lt;code&gt;sbx&lt;/code&gt; CLI, launch your first isolated sandbox, see the isolation boundary with your own eyes, and clean everything up properly.&lt;/p&gt;

&lt;p&gt;No paid AI subscription is required. We use the &lt;code&gt;shell&lt;/code&gt; agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;AI coding agents are useful because they act on their own. They install packages, edit files, run tests, build Docker images, and sometimes run commands you would never type yourself.&lt;/p&gt;

&lt;p&gt;If you run them directly on your machine they can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change or delete files outside the project&lt;/li&gt;
&lt;li&gt;Install system packages that break your setup&lt;/li&gt;
&lt;li&gt;Talk to your host Docker daemon&lt;/li&gt;
&lt;li&gt;Reach any network destination&lt;/li&gt;
&lt;li&gt;Accidentally expose credentials already present in your environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Normal containers share the host kernel. Mounting the Docker socket gives the agent the same power as you. Full virtual machines are heavy and slow to start.&lt;/p&gt;

&lt;p&gt;Docker Sandboxes solve this by giving each agent its own lightweight microVM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before vs After — The Core Problem
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78lr05gxsrwd6c86942q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F78lr05gxsrwd6c86942q.png" alt="before-aftersandbox" width="800" height="984"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation of the diagram&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Left side shows the dangerous default: the agent runs with the same power as you on the host.&lt;br&gt;&lt;br&gt;
Right side shows the sandbox approach: the agent is locked inside a microVM. Only the project folder is shared. Everything else stays isolated.&lt;/p&gt;
&lt;h2&gt;
  
  
  What You Should Know Before Starting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Comfortable with a terminal&lt;/li&gt;
&lt;li&gt;Free Docker account&lt;/li&gt;
&lt;li&gt;Supported platform:

&lt;ul&gt;
&lt;li&gt;macOS&lt;/li&gt;
&lt;li&gt;Windows 11 with Windows Hypervisor Platform enabled&lt;/li&gt;
&lt;li&gt;Ubuntu 24.04+ with KVM working&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker Desktop is &lt;strong&gt;not&lt;/strong&gt; required. (No blocker for corporates...)&lt;/p&gt;
&lt;h2&gt;
  
  
  Environment Setup
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install the sbx CLI
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew trust docker/tap
brew &lt;span class="nb"&gt;install &lt;/span&gt;docker/tap/sbx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ubuntu / Linux&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://get.docker.com | &lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="nv"&gt;REPO_ONLY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;docker-sbx
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; kvm &lt;span class="nv"&gt;$USER&lt;/span&gt;
newgrp kvm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;winget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-h&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Docker.sbx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjuhqhwt5l69qh8ukuthq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjuhqhwt5l69qh8ukuthq.png" alt="sbx version" width="800" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign in
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fno1t3q0m35u3x18noru6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fno1t3q0m35u3x18noru6.png" alt="sbx login" width="800" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — Create a Simple Project
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/sandbox-lab
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/sandbox-lab
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello from the host"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; note.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft2qcx0l3tshoy2iq2m5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft2qcx0l3tshoy2iq2m5v.png" alt="sbx creating project" width="798" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Launch the First Sandbox
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx run &lt;span class="nt"&gt;--name&lt;/span&gt; first-sandbox shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa2yls016aejfpm6fe7ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa2yls016aejfpm6fe7ks.png" alt="sbx launch first sandbox" width="799" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the first run choose the &lt;strong&gt;Balanced&lt;/strong&gt; network preset (recommended).&lt;/p&gt;

&lt;p&gt;You are now inside a Bash shell that lives inside the microVM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Look Around Inside the Sandbox
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd
ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;note.txt
&lt;span class="nb"&gt;whoami
&lt;/span&gt;docker version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9f6hrd91jdgn9iflwj4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9f6hrd91jdgn9iflwj4e.png" alt="inside sandbox" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open a second terminal on the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6zor6tb1tjwbqkwbmawj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6zor6tb1tjwbqkwbmawj.png" alt="sbx ls" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  High-level Sandbox Architecture (what you just created)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0yvjbss0z374t14jlrgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0yvjbss0z374t14jlrgs.png" alt="sbx architecture" width="694" height="1157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation of the diagram&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You talk to the &lt;code&gt;sbx&lt;/code&gt; CLI. The CLI starts a microVM. Inside that microVM the agent, its Docker daemon and its filesystem live. The only connection back to the host is the workspace folder you chose.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4 — Prove the Isolation
&lt;/h2&gt;

&lt;p&gt;Inside the sandbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"written from inside the sandbox"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; note.txt
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"created inside sandbox"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; from-sandbox.txt
docker pull alpine:latest
docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxtgejx44l64a1930i4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxtgejx44l64a1930i4j.png" alt="inside sbx" width="798" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/sandbox-lab/note.txt
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/sandbox-lab
docker images | &lt;span class="nb"&gt;grep &lt;/span&gt;alpine &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"alpine image is NOT on the host"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6b99pcvgmou46g8z1ab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6b99pcvgmou46g8z1ab.png" alt="on host" width="798" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Ideally you should see message alpine image is NOT on the host as output but as I'm using postgres alpine image for another application it showed up but you can observe this alpine images is not what we pulled in the sandbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Isolation Boundary Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpnbpwr2nb4ql28o4lteo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpnbpwr2nb4ql28o4lteo.png" alt="isolation boundary" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation of the diagram&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Files in the project folder move both ways (live mount).&lt;br&gt;&lt;br&gt;
The sandbox Docker daemon has &lt;strong&gt;no path&lt;/strong&gt; to the host Docker daemon. Images and containers stay inside the microVM.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Just Happened Internally
&lt;/h2&gt;

&lt;p&gt;The microVM has its own kernel. The agent has full power &lt;strong&gt;inside&lt;/strong&gt; the microVM (including sudo). Network traffic goes through a host-side proxy that enforces the policy you chose. Credentials (when we use them later) are injected by the proxy and never stored inside the VM.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let’s Break It (Gently)
&lt;/h2&gt;

&lt;p&gt;Inside the sandbox try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://koti.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fojowv7m22avfpqwf0ei0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fojowv7m22avfpqwf0ei0.png" alt="policy" width="800" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it is blocked under Balanced policy, that is expected.&lt;/p&gt;

&lt;p&gt;On the host allow it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx policy allow network koti.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbtj823rvpsiyr77ag4n6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbtj823rvpsiyr77ag4n6.png" alt="allow policy" width="798" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retry. It should now work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5qbqut2co2p7uyvfh1d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5qbqut2co2p7uyvfh1d.png" alt="policy worked" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Thinking
&lt;/h2&gt;

&lt;p&gt;In real work I always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with Balanced or stricter&lt;/li&gt;
&lt;li&gt;Give every sandbox a clear name&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;--clone&lt;/code&gt; for experimental or untrusted agent work&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;sbx rm&lt;/code&gt; as the normal way to finish a session&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The agent is powerful inside the sandbox. Do not treat it like a normal restricted container.&lt;/li&gt;
&lt;li&gt;The workspace is read-write by default.&lt;/li&gt;
&lt;li&gt;Never put long-lived secrets as normal environment variables inside the sandbox (we cover the proper way in the next blog).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting the &lt;code&gt;kvm&lt;/code&gt; group on Linux → sandbox will not start&lt;/li&gt;
&lt;li&gt;Skipping &lt;code&gt;sbx login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Expecting host Docker to see images built inside the sandbox&lt;/li&gt;
&lt;li&gt;Leaving many sandboxes running → disk fills up&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Quick check&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sbx&lt;/code&gt; not found&lt;/td&gt;
&lt;td&gt;Installation&lt;/td&gt;
&lt;td&gt;Re-run install steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KVM error on Linux&lt;/td&gt;
&lt;td&gt;`lsmod \&lt;/td&gt;
&lt;td&gt;grep kvm`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sandbox stuck&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sbx ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sbx stop&lt;/code&gt; then &lt;code&gt;sbx rm&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need clean state&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sbx reset&lt;/code&gt; (destructive)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Also useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx diagnose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sbx stop first-sandbox
sbx &lt;span class="nb"&gt;rm &lt;/span&gt;first-sandbox
sbx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;~/sandbox-lab&lt;/code&gt; folder remains. Only the microVM is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI agents need strong isolation. MicroVMs give that isolation without the weight of full VMs.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;sbx&lt;/code&gt; CLI is the current free way to use Docker Sandboxes.&lt;/li&gt;
&lt;li&gt;Workspace is shared; almost everything else stays inside the sandbox.&lt;/li&gt;
&lt;li&gt;You can now install, launch, inspect and cleanly remove a sandbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;In Blog 2 we take full control of the environment: how files persist, the difference between direct mount and clone mode, network policies in depth, and the safe way to inject secrets so the agent never sees the real values.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/&lt;/a&gt; — official overview
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/get-started/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/get-started/&lt;/a&gt; — installation and first run
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/ai/sandboxes/architecture/" rel="noopener noreferrer"&gt;https://docs.docker.com/ai/sandboxes/architecture/&lt;/a&gt; — microVM and isolation details
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/reference/cli/sbx/" rel="noopener noreferrer"&gt;https://docs.docker.com/reference/cli/sbx/&lt;/a&gt; — command reference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All commands and behaviour verified against current official documentation (August 2026).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>docker</category>
      <category>agents</category>
      <category>containers</category>
    </item>
    <item>
      <title>Why I Broke Kubernetes Cluster 35 Times? I did it So You Don't Have To</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Tue, 07 Apr 2026 14:51:21 +0000</pubDate>
      <link>https://dev.to/vellanki/why-i-broke-kubernetes-cluster-35-times-i-did-it-so-you-dont-have-to-57cb</link>
      <guid>https://dev.to/vellanki/why-i-broke-kubernetes-cluster-35-times-i-did-it-so-you-dont-have-to-57cb</guid>
      <description>&lt;h2&gt;
  
  
  The Problem With Learning Kubernetes
&lt;/h2&gt;

&lt;p&gt;Everyone tells you to "&lt;strong&gt;learn Kubernetes&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;So you read the docs. You watch YouTube. You follow a tutorial that deploys nginx. You feel great.&lt;/p&gt;

&lt;p&gt;After a cheerful weekend, you logged in Monday morning. A pod is stuck in CrashLoopBackOff. You stare at the terminal. You Google or ask any GPT models. You paste random commands. Thirty minutes later, you're still stuck.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;Here's the thing: &lt;strong&gt;you don't really understand Kubernetes until something breaks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And the best way to learn troubleshooting is to break things on purpose, in a safe environment, where you can take your time and actually understand what went wrong.&lt;/p&gt;

&lt;p&gt;That's exactly why I built this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing: Troubleshoot Kubernetes Like a Pro
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro" rel="noopener noreferrer"&gt;https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a free, open-source collection of &lt;strong&gt;35 real-world Kubernetes failure scenarios&lt;/strong&gt; that you can simulate, investigate, and fix on your own cluster.&lt;/p&gt;

&lt;p&gt;No custom Docker images. No complex setup. No cloud account required. Just a local Kubernetes cluster (Minikube, Kind, or Docker Desktop) and &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Every scenario follows the same simple pattern:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Break it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; issue.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a deliberately broken Kubernetes resource. A pod that crashes. A service that points to nothing. A container that runs out of memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Investigate it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods
kubectl describe pod &amp;lt;pod-name&amp;gt;
kubectl logs &amp;lt;pod-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like you would in production. No hints. No hand-holding. You figure it out.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fix it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; fix.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix resolves the issue. You can compare the two YAML files to see exactly what changed and why.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Understand it
&lt;/h3&gt;

&lt;p&gt;Every scenario includes a &lt;code&gt;description.md&lt;/code&gt; that explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the issue is&lt;/li&gt;
&lt;li&gt;What causes it in the real world&lt;/li&gt;
&lt;li&gt;How to identify it&lt;/li&gt;
&lt;li&gt;How to fix it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 35 Scenarios
&lt;/h2&gt;

&lt;p&gt;Here's what's inside, organized by category:&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduling Failures (Pod stuck in Pending)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Affinity Rules Violation&lt;/td&gt;
&lt;td&gt;Pod requires a node label that doesn't exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node Affinity Issue&lt;/td&gt;
&lt;td&gt;Pod targets a non-existent node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insufficient Resources&lt;/td&gt;
&lt;td&gt;Pod requests more CPU/memory than available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taints and Tolerations Mismatch&lt;/td&gt;
&lt;td&gt;Pod can't schedule due to node selector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster Autoscaler Issues&lt;/td&gt;
&lt;td&gt;Too many replicas for the cluster to handle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Container Crashes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CrashLoopBackOff&lt;/td&gt;
&lt;td&gt;Container exits immediately with error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OOM Killed&lt;/td&gt;
&lt;td&gt;Container exceeds memory limit, killed by cgroup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong Container Command&lt;/td&gt;
&lt;td&gt;Invalid command in container spec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CGroup Issues&lt;/td&gt;
&lt;td&gt;Memory stress exceeds cgroup limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failed Resource Limits&lt;/td&gt;
&lt;td&gt;Workload exceeds restrictive resource limits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Image Problems
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Image Pull BackOff&lt;/td&gt;
&lt;td&gt;Non-existent image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image Pull Error&lt;/td&gt;
&lt;td&gt;Private registry image without credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Probe Failures
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Liveness Probe Failure&lt;/td&gt;
&lt;td&gt;Probe hits wrong endpoint, container keeps restarting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readiness Probe Failure&lt;/td&gt;
&lt;td&gt;Probe fails, pod shows 0/1 Ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Liveness &amp;amp; Readiness Failure&lt;/td&gt;
&lt;td&gt;Both probes misconfigured&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Storage Issues
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Volume Mount Issue&lt;/td&gt;
&lt;td&gt;Pod references a volume that doesn't exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent Volume Claim Issues&lt;/td&gt;
&lt;td&gt;PVC can't bind to any PV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk IO Errors&lt;/td&gt;
&lt;td&gt;HostPath points to non-existent directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File Permissions on Mounted Volumes&lt;/td&gt;
&lt;td&gt;Read-only filesystem blocks writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crash Due to Insufficient Disk Space&lt;/td&gt;
&lt;td&gt;Ephemeral storage limit exceeded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Networking Issues
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DNS Resolution Failure&lt;/td&gt;
&lt;td&gt;Custom DNS config points to invalid nameserver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firewall Restriction&lt;/td&gt;
&lt;td&gt;NetworkPolicy blocks all egress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Connectivity Issues&lt;/td&gt;
&lt;td&gt;NetworkPolicy blocks pod traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service Port Mismatch&lt;/td&gt;
&lt;td&gt;Service port doesn't match container port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ingress Configuration Issue&lt;/td&gt;
&lt;td&gt;Ingress points to wrong host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LoadBalancer Misconfiguration&lt;/td&gt;
&lt;td&gt;Service selector doesn't match any pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port Binding Issues&lt;/td&gt;
&lt;td&gt;Container port conflict&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Security &amp;amp; RBAC
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Service Account Permissions&lt;/td&gt;
&lt;td&gt;Pod references non-existent ServiceAccount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security Context Issues&lt;/td&gt;
&lt;td&gt;Running as root vs non-root (best practice)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELinux/AppArmor Policy Violation&lt;/td&gt;
&lt;td&gt;Security policy configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PID Namespace Collision&lt;/td&gt;
&lt;td&gt;Host PID namespace shared (security risk)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Other
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container Runtime (CRI) Errors&lt;/td&gt;
&lt;td&gt;RuntimeClass with non-existent handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource Requests &amp;amp; Limits Mismatch&lt;/td&gt;
&lt;td&gt;CPU limit less than request (API rejection)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pod Disruption Budget Violations&lt;/td&gt;
&lt;td&gt;PDB blocks voluntary disruptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outdated Kubernetes Version&lt;/td&gt;
&lt;td&gt;Educational scenario about version management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Quick Start (5 Minutes)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Use the interactive script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro.git
&lt;span class="nb"&gt;cd &lt;/span&gt;troubleshoot-kubernetes-like-a-pro
./manage-scenarios.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick a scenario number. The script handles everything - creates the issue, lets you investigate, then applies the fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Run scenarios manually
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;scenarios/crashloopbackoff

&lt;span class="c"&gt;# Create the problem&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; issue.yaml

&lt;span class="c"&gt;# Investigate&lt;/span&gt;
kubectl get pods
kubectl describe pod crashloopbackoff-pod
kubectl logs crashloopbackoff-pod

&lt;span class="c"&gt;# Fix it&lt;/span&gt;
kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; issue.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; fix.yaml

&lt;span class="c"&gt;# Verify&lt;/span&gt;
kubectl get pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What You'll Actually Learn
&lt;/h2&gt;

&lt;p&gt;After working through these scenarios, you'll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read pod status and know exactly what's wrong&lt;/strong&gt; - Pending means scheduling, CrashLoopBackOff means the app is failing, ImagePullBackOff means the image is wrong.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use kubectl describe like a pro&lt;/strong&gt; - The Events section at the bottom tells you everything. Failed scheduling, failed mounts, failed pulls, probe failures - it's all there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Understand resource management&lt;/strong&gt; - Requests vs limits, ephemeral storage, cgroup OOM kills, and why your pod got evicted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debug networking issues&lt;/strong&gt; - DNS resolution, NetworkPolicy, service selectors, port mismatches, and ingress configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handle security configurations&lt;/strong&gt; - ServiceAccounts, security contexts, PID namespaces, and runtime classes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Beginners&lt;/strong&gt; who just finished a Kubernetes tutorial and want real practice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers&lt;/strong&gt; who deploy to Kubernetes but panic when something breaks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DevOps engineers&lt;/strong&gt; preparing for CKA/CKAD certification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SREs&lt;/strong&gt; who want to sharpen their troubleshooting instincts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teams&lt;/strong&gt; who want to run Kubernetes troubleshooting workshops&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Note on Two Scenario Types
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;30 scenarios produce hard failures&lt;/strong&gt; - you'll see Pending, CrashLoopBackOff, OOMKilled, Error, or ImagePullBackOff. These are obvious and satisfying to fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 scenarios are educational&lt;/strong&gt; - Security Context, SELinux, and Outdated K8s Version. Both the issue and fix pods run successfully. The learning is in understanding the security implications of the configuration difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 scenarios require a CNI with NetworkPolicy support&lt;/strong&gt; (like Calico or Cilium) to fully demonstrate blocked traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Today
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro.git
&lt;span class="nb"&gt;cd &lt;/span&gt;troubleshoot-kubernetes-like-a-pro
./manage-scenarios.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Star the repo if it helps you: &lt;a href="https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro" rel="noopener noreferrer"&gt;https://github.com/vellankikoti/troubleshoot-kubernetes-like-a-pro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Share it with someone who's learning Kubernetes. The best way to learn is to break things safely.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built and maintained by &lt;a href="https://www.linkedin.com/in/vellankikoti/" rel="noopener noreferrer"&gt;Koti Vellanki&lt;/a&gt;. Contributions welcome!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>ai</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I Automated GitHub Repos, Branches &amp; PRs Using Claude AI and Docker Desktop MCP</title>
      <dc:creator>Koti Vellanki</dc:creator>
      <pubDate>Sat, 17 May 2025 22:07:19 +0000</pubDate>
      <link>https://dev.to/vellanki/how-i-automated-github-repos-branches-prs-using-claude-ai-and-docker-desktop-mcp-1929</link>
      <guid>https://dev.to/vellanki/how-i-automated-github-repos-branches-prs-using-claude-ai-and-docker-desktop-mcp-1929</guid>
      <description>&lt;h2&gt;
  
  
  🚀 Introduction
&lt;/h2&gt;

&lt;p&gt;In this guide, I’ll walk you through how I used &lt;strong&gt;Docker Desktop&lt;/strong&gt;, the &lt;strong&gt;MCP Toolkit extension&lt;/strong&gt;, and &lt;strong&gt;Claude Desktop&lt;/strong&gt; to connect to GitHub and automate tasks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a repository&lt;/li&gt;
&lt;li&gt;Adding multiple branches&lt;/li&gt;
&lt;li&gt;Opening a pull request — all via simple prompts to Claude&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll set up everything step-by-step so you can &lt;strong&gt;follow this like a playbook&lt;/strong&gt; and get the same results.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have the following installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/index/claude" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;GitHub Personal Access Token (PAT)&lt;/a&gt; with &lt;code&gt;repo&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; access&lt;/li&gt;
&lt;li&gt;Internet connectivity&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Step-by-Step Guide
&lt;/h2&gt;




&lt;h3&gt;
  
  
  🔹 Step 1: Open Docker Desktop and Navigate to Extensions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Launch Docker Desktop&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;"Add Extensions"&lt;/strong&gt; tab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4c9hyigil2179gcys5oy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4c9hyigil2179gcys5oy.png" alt=" " width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🔹 Step 2: Install MCP Toolkit Extension
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Search for &lt;strong&gt;“MCP Toolkit”&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Install&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Wait until installation is complete and launch the extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw8thyd8miq59crpfpovh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw8thyd8miq59crpfpovh.png" alt=" " width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🔹 Step 3: Enable GitHub as MCP Server
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Inside MCP Toolkit, Search for &lt;strong&gt;GitHub&lt;/strong&gt; under MCP Servers and select the official MCP Server&lt;/li&gt;
&lt;li&gt;Paste your &lt;strong&gt;GitHub Personal Access Token&lt;/strong&gt;
If you don't have the GitHub PAT ready go to Account Settings --&amp;gt; Developer Settings - Create Token and paste it in the github.personal_access_token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1egh5gb7woy7sxi08lnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1egh5gb7woy7sxi08lnt.png" alt=" " width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This enables Docker to act as an MCP Server that communicates securely with GitHub.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🔹 Step 4: Launch Claude Desktop and Configure MCP Client
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open Claude Desktop settings or config file&lt;/li&gt;
&lt;li&gt;Copy the MCP Client config instructions shown in Docker MCP extension&lt;/li&gt;
&lt;li&gt;Paste them into Claude’s config&lt;/li&gt;
&lt;li&gt;Restart Claude Desktop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs41se2u5ee7sk0nn419y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs41se2u5ee7sk0nn419y.png" alt=" " width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Now Claude is linked to the GitHub MCP server and ready to take commands.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🔹 Step 5: Create a GitHub Repository Using Claude
&lt;/h3&gt;

&lt;p&gt;Open Claude’s interface and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create a repo called yt-demo-github-mcp in my github account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ You’ll see a confirmation from Claude that the repository is created.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔹 Step 6: Create Multiple Branches
&lt;/h3&gt;

&lt;p&gt;Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;can you create 3 branches dev, qe, prod and list all the available branches?

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Claude will confirm branch creation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1up3xdmn9gsguhx5u4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1up3xdmn9gsguhx5u4u.png" alt=" " width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🔹 Step 7: Open a Pull Request
&lt;/h3&gt;

&lt;p&gt;Prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;can you create a file called tes.py with hello worls code in Python and raise Pull request from Dev to QE, QE to Prod and Prod to main?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Claude opens the PR instantly, no GitHub UI involved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdq4pcxzrnr91shn5tciu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdq4pcxzrnr91shn5tciu.png" alt=" " width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 What Else Can You Do?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;GitHub MCP Server&lt;/strong&gt; supports over &lt;strong&gt;48 different GitHub features&lt;/strong&gt; — from repository settings to contributor roles, issues, PR reviews, and more.&lt;/p&gt;

&lt;p&gt;👉 You can explore all supported features here:&lt;br&gt;
📖 &lt;a href="https://docs.docker.com/desktop/extensions/mcp/github" rel="noopener noreferrer"&gt;GitHub MCP Official Documentation&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🐳 &lt;a href="https://www.docker.com/products/docker-desktop" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔌 &lt;a href="https://marketplace.docker.com/extensions/docker-mcp-toolkit" rel="noopener noreferrer"&gt;Docker MCP Toolkit Extension&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://docs.docker.com/desktop/extensions/mcp/github" rel="noopener noreferrer"&gt;GitHub MCP Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🤖 &lt;a href="https://www.anthropic.com/index/claude" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔐 &lt;a href="https://github.com/settings/tokens" rel="noopener noreferrer"&gt;Create a GitHub PAT&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;You just saw how easy it is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up Docker MCP and Claude Desktop&lt;/li&gt;
&lt;li&gt;Connect to GitHub&lt;/li&gt;
&lt;li&gt;Automate key GitHub tasks with natural language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide is part of my &lt;strong&gt;Docker Series&lt;/strong&gt;, where I’ll continue sharing hands-on, real-world use cases to help you get the most out of Docker tools and extensions.&lt;/p&gt;




</description>
      <category>github</category>
      <category>devops</category>
      <category>fai</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
