<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Gonzalo Gasca Meza on Medium]]></title>
        <description><![CDATA[Stories by Gonzalo Gasca Meza on Medium]]></description>
        <link>https://medium.com/@gogasca_?source=rss-53e6ac1fe11e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*T_TByaVuMVm_9PKfke1-Ww.png</url>
            <title>Stories by Gonzalo Gasca Meza on Medium</title>
            <link>https://medium.com/@gogasca_?source=rss-53e6ac1fe11e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 10:27:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@gogasca_/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Notebooks in the AI era]]></title>
            <link>https://medium.com/@gogasca_/notebooks-in-the-ai-era-ac0e43d8f1f7?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/ac0e43d8f1f7</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Wed, 16 Sep 2026 06:29:27 GMT</pubDate>
            <atom:updated>2026-09-16T06:29:27.371Z</atom:updated>
            <content:encoded><![CDATA[<h3>Chat Is the Scratchpad, the Notebook Is the Commit</h3><p><em>What AI agents change about Jupyter notebooks, and what the IDE vs. CLI debate misses about data work.</em></p><p>Here is a Jupyter session:</p><pre>In [1]: import pandas as pd<br>In [2]: sales = pd.read_csv(&quot;sales.csv&quot;)<br>In [3]: sales.groupby(&quot;region&quot;)[&quot;revenue&quot;].sum().plot.bar()<br>Out[3]: &lt;Axes: xlabel=&#39;region&#39;&gt;</pre><p>And here is the same work with an AI agent:</p><pre>You:   Load sales.csv and chart revenue by region.<br>Agent: Done. West leads, but 14% of rows have no region.<br>       Should I drop them or group them as &quot;Unknown&quot;?</pre><p>The rhythm is identical: a prompt, a response, and state that carries into the next turn. The difference shows up the next morning. You can re-run the first. You can only re-read the second.</p><p>The resemblance is no accident. Notebooks have always been conversations with a computer. Mathematica introduced its notebook interface in 1988. IPython brought numbered In and Out prompts to Python in 2001 and a browser-based notebook in 2011, and Project Jupyter, named for Julia, Python, and R, grew out of it in 2014. From its early days, Jupyter’s goal was the computational narrative: people make sense of the world through stories, so code and results have to be woven into a story for a particular audience before they’re truly useful <a href="https://blog.jupyter.org/project-jupyter-computational-narratives-as-the-engine-of-collaborative-data-science-2b5fb94c3c58">[1]</a>.</p><p>What’s new is a third participant. Agents in Colab, Databricks, Deepnote, and Hex now carry much of that conversation on their own, writing cells, running them, and reacting to what comes back. And notebooks aren’t fading as this happens: GitHub repositories containing Jupyter notebooks grew about 75% in a year, with notebook usage nearly doubling in AI-tagged projects <a href="https://thenewstack.io/kotlin-notebook-jupyter-python-retreat/">[2]</a>.</p><p>Most commentary on AI coding tools asks where you should steer an agent from: the IDE or the terminal. It’s a fair question, but data work raises a more consequential one. When the agent is done, what do you keep? This post argues for a simple division of labor. The conversation, whether it happens in a chat panel or a terminal, is the scratchpad. The notebook is the commit.</p><h3>The debate so far: steering from the editor or the shell</h3><p>Here’s the IDE vs. CLI debate in one table:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wo237oUoCWui65zVaGMDUQ.png" /></figure><p>In shorthand, IDE AI is coding assistance and CLI AI is workflow automation. One augments the developer; the other acts on the developer’s behalf.</p><p>Much of the choice comes down to taste. IDE agents inherit rich visual review, with inline diffs, navigation, and a debugger, while terminal agents trade that visual feedback for speed and <em>scriptability </em><a href="https://www.sonarsource.com/blog/cli-vs-ide/">[3]</a>. What matters more than the environment is verification: agents have made changes cheap to produce but no cheaper to check <a href="https://thenewstack.io/cli-ide-ai-verification/">[4]</a>. One useful way to frame the split is flow versus intent, and for UI-heavy work the IDE keeps an edge for a mundane reason: it’s easier to drag a screenshot into the agent <a href="https://news.ycombinator.com/item?id=46474225">[5]</a>.</p><p>That screenshot detail points at something the debate usually skips: the more visual the work, the more your tools need to show you things. Data work is about as visual as work gets.</p><h3>The same split is already happening inside the notebook</h3><p>Look closely at today’s notebook products and you’ll find the IDE vs. CLI divide replayed in miniature.</p><p>The IDE-style layer is cell-level help. Genie Code, the successor to Databricks Assistant, offers autocomplete, error diagnosis, and suggested fixes you can accept and run <a href="https://docs.databricks.com/aws/en/notebooks/code-assistant">[6]</a><a href="https://docs.databricks.com/gcp/en/notebooks/databricks-assistant-faq">[7]</a>. In Colab, you can describe a change in plain English and have it locate and rewrite the relevant code <a href="https://developers.googleblog.com/new-ai-first-google-colab-now-available-to-everyone/">[8]</a>. You stay in charge of each edit, as you would with a pair programmer.</p><p>The CLI-style layer is agent mode. Give Colab’s Data Science Agent a dataset and a goal, and it builds a working notebook <a href="https://developers.googleblog.com/en/data-science-agent-in-colab-with-gemini/">[9]</a>. Genie Code’s agent plans an approach, looks up relevant tables, runs cells, reads their outputs, and fixes its own errors <a href="https://learn.microsoft.com/en-us/azure/databricks/notebooks/ds-agent">[10]</a>. Hex and Deepnote ship notebook agents of their own <a href="https://hex.tech/blog/introducing-notebook-agent/">[11]</a><a href="https://github.com/deepnote">[12]</a>.</p><p>Then there’s the literal terminal. Google’s Colab MCP Server lets agents like Claude Code and Gemini CLI create and run Colab notebooks <a href="https://developers.googleblog.com/announcing-the-colab-mcp-server-connect-any-ai-agent-to-google-colab/">[13]</a>. The Hex CLI and Databricks’ installable agent skills bring those platforms to coding agents such as Claude Code, Codex, and Cursor <a href="https://hex.tech/blog/introducing-the-hex-cli/">[14]</a><a href="https://docs.databricks.com/aws/en/agent-skills/">[15]</a>. Marimo pair gives terminal agents a live notebook kernel <a href="https://marimo.io/blog/notebooks-as-a-tool-for-agents">[16]</a>, and Jupyter AI 3 connects Claude, Codex, Gemini, and others to JupyterLab through the Agent Client Protocol <a href="https://pypi.org/project/jupyter-ai/">[17]</a>.</p><p>Adapted to the notebook, the comparison looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*znz7kutKLFdivJwAyx6LSg.png" /></figure><p>Cell assistants are coding assistance. Notebook agents are analysis automation.</p><p>Notice which way the defaults are moving. Genie Code has a Chat mode and an Agent mode, and Agent is the default <a href="https://docs.databricks.com/aws/en/notebooks/use-databricks-assistant">[18]</a>. Jupyter AI 3.0 moved its magic commands out of the default install into an optional package, on the reasoning that agents can now edit notebooks directly <a href="https://github.com/jupyterlab/jupyter-ai/releases">[19]</a>. In the notebook world, autonomy is becoming the starting point.</p><h3>Control vs. autonomy: why data raises the stakes</h3><p>In an IDE, the AI behaves like a pair programmer and you approve each change. In a terminal, it behaves more like automation. Developers who value step-by-step precision gravitate to the first, while those facing large codebases, repetitive operations, or infrastructure work gravitate to the second.</p><p>Data work complicates the trade-off because of how things fail. When an autonomous agent breaks application code, something usually complains: a test, a type checker, a build. When an autonomous agent botches an analysis, nothing complains. A join that duplicates rows, a filter applied in the wrong order, or a train/test split that leaks information all produce a clean chart and a plausible number.</p><p>The platforms do add brakes. Genie Code asks for approval before running code, though you can switch it to auto-approve <a href="https://learn.microsoft.com/en-us/azure/databricks/notebooks/ds-agent">[10]</a>, and Jupyter AI’s agents request permission before writing files or running commands <a href="https://pypi.org/project/jupyter-ai/">[17]</a>. But approving an execution isn’t the same as validating a result. And notebooks had a reproducibility problem long before agents showed up. In a 2019 analysis of roughly 1.4 million GitHub notebooks, only about 24% ran without errors and about 4% reproduced their original results, with hidden state and out-of-order execution among the leading causes <a href="https://ieeexplore.ieee.org/document/8816763/">[20]</a>. An agent can manufacture hidden state far faster than a person can.</p><p>So the real question isn’t whether agents should act autonomously in notebooks. They already do. It’s what has to be true of their work before you believe it.</p><h3>Chat is the scratchpad, the notebook is the commit</h3><p>Good notebook practice has long held that you should record your explorations, dead ends included, because a notebook captures code automatically but not the reasoning behind it <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1007007">[21]</a>. That advice has always pulled against the notebook’s other job: a detailed lab record eventually has to be trimmed into a concise story for a wider audience.</p><p>Agents resolve that tension almost by accident, because agent-driven work naturally produces two artifacts. The conversation holds the questions, the instructions, the false starts, and the agent’s reasoning. The notebook holds what survived. That suggests a clean division of labor: treat the conversation as the scratchpad and the notebook as the commit.</p><p>The tools are already converging on that shape. Marimo pair separates the two by design: a terminal agent experiments freely in a scratchpad attached to the live kernel, and only the steps it chooses to keep, and that Marimo accepts, become cells in a notebook someone can re-run and review later <a href="https://marimo.io/blog/notebooks-as-a-tool-for-agents">[16]</a>. A conversation in Hex’s Threads interface can be saved as a notebook project so the data team can extend or audit it <a href="https://hex.tech/blog/fall-2025-launch/">[22]</a>, and the Hex CLI offers the terminal version of the same move: build the analysis locally with the coding agent you prefer, then publish it to Hex as a versioned project <a href="https://hex.tech/blog/introducing-the-hex-cli/">[14]</a>. Through Colab’s MCP Server, agents can produce complete, runnable notebooks rather than loose code snippets <a href="https://www.infoq.com/news/2026/04/colab-mcp-server/">[23]</a>. Genie Code’s full-page experience starts from the chat and opens notebooks and files beside it as tabs <a href="https://docs.databricks.com/aws/en/genie-code/">[24]</a>. And Jupyter AI stores chats as ordinary files in your workspace, so the scratchpad can sit right next to the commit <a href="https://jupyter-ai.readthedocs.io/en/stable/getting-started.html">[25]</a>.</p><p>Why should the notebook, and not the transcript, be the artifact of record? A transcript tells you what the agent said. A notebook can show you, on demand, what the data does. That only holds if the commit is honest. When a terminal agent edits an .ipynb file directly, as Claude Code does with its cell-level notebook tool, it can read the outputs saved in the file <a href="https://code.claude.com/docs/en/tools-reference">[26]</a> but can’t tell whether those outputs are stale, because it isn’t looking at a running kernel <a href="https://docs.kanaries.net/articles/claude-code-jupyter-data-science">[27]</a>. Committing a notebook whose outputs nobody regenerated is like merging code on the strength of last week’s green checkmark. Kernel-connected agents narrow that gap, but the only real proof is a clean run from the top.</p><p>Commit size matters too. Agents do far better work when they build a notebook one step at a time, loading data, checking it, and fixing problems as they go, than when they write the whole thing at once and run it at the end, which tends to produce something tidy-looking that doesn’t hold up <a href="https://talkpython.fm/episodes/show/555/marimo-pair-a-canvas-for-agent-developers-collaboration">[28]</a>. Small commits beat big ones, for agents as much as for people.</p><h3>What a good notebook commit looks like</h3><p>If the notebook is the commit, it deserves the standards we already apply to commits. Several are long-standing notebook best practices <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1007007">[21]</a>; the agent era just makes them non-negotiable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RMNISz6vlIjMW4OYYo35NQ.png" /></figure><p>A minimal setup takes care of the mechanical parts:</p><pre># Keep outputs out of Git diffs (publish executed copies separately if reviewers need them)<br>nbstripout --install</pre><pre># Give agents and reviewers a plain-text view of each notebook<br>jupytext --set-formats ipynb,py:percent analysis.ipynb</pre><pre># CI gate: every committed notebook must run top to bottom in a fresh kernel<br>pytest --nbmake notebooks/*.ipynb</pre><p>If rendered outputs matter to your reviewers, keep them, just not inside the source diff. Deepnote’s VS Code extension, for example, can write outputs to separate snapshot files instead of the main notebook <a href="https://github.com/deepnote/vscode-deepnote/pull/256">[30]</a>.</p><h3>Where the terminal fits, and whether the skeptic is right</h3><p>There’s a blunt counterargument to the terminal enthusiasm: AI companies only moved to the CLI because they don’t have an IDE of their own and don’t want to depend on Microsoft or anyone else, and a strong IDE like IntelliJ still beats the terminal for real coding. The first half probably contains some truth, since platform independence is a real incentive. And for building a large library, a mature IDE is formidable.</p><p>But look at what data platforms actually use the terminal for. It isn’t replacing the notebook. It moves work between the conversation, the commit, and production: building an analysis locally and publishing it to Hex, pushing agent compute off your laptop and into Colab, installing Databricks skills into the agents you already run. Jupyter AI even stopped focusing on its own agent, Jupyternaut, in order to connect the agents developers already use <a href="https://github.com/jupyterlab/jupyter-ai/issues/1531">[31]</a>. In data work, the terminal is the plumbing, not the destination.</p><p>As for the IDE, the bottleneck in most analysis isn’t typing code, which agents now do quickly. It’s judging outputs. Is that distribution plausible? Did that join behave? Does this chart say what we think it says? That judgment needs a canvas that shows code, data, and results together, no matter which tool wrote the code.</p><h3>A hybrid workflow: converse, commit, ship</h3><p>The familiar three-phase model of terminal, then IDE, then finalization adapts neatly to data work.</p><p>Phase 1: Converse. Start in a chat panel or terminal session connected to live data. Ask questions, let the agent explore, and chase dead ends while they’re cheap. Look at every chart as it appears and push back early. Nothing here needs to be tidy. This is the scratchpad.</p><p>Phase 2: Commit. Promote what survived into a clean notebook, one meaningful step per cell, with Markdown explaining why each step exists. Add assertions for the data expectations you relied on. Restart the kernel, run everything from the top, and read the outputs yourself, not just the code. Keep the conversation file alongside it if it explains decisions a future reader will wonder about.</p><p>Phase 3: Ship. Hand the committed notebook to a terminal agent to extract reusable logic into modules, add tests, parameterize runs, and wire everything into CI or a scheduler. Review those diffs in your IDE, then commit, test, and push with full human oversight.</p><h3>Final thoughts</h3><p>For nearly four decades, the notebook has been a conversation between a person and a kernel, recorded as it happens. Agents now join that conversation as a third voice, and a very fast one.</p><p>That makes it tempting to treat the chat as the new notebook. It isn’t. Chats are wonderful for thinking out loud, but you can’t re-run a transcript. In the agent era, the notebook’s job gets narrower and more important: hold only what survived, in a form anyone can execute and question.</p><p>Chat tools help you explore faster. Terminal and IDE tools help you ship. The notebook is where exploration becomes something you can re-run, review, and trust.</p><p>The future of data tooling may not be IDE vs. CLI, or even chat vs. notebook. It may be a conversation that ends in a commit.</p><h3>References</h3><ol><li>Fernando Pérez and Brian Granger, <a href="https://blog.jupyter.org/project-jupyter-computational-narratives-as-the-engine-of-collaborative-data-science-2b5fb94c3c58">Project Jupyter: Computational Narratives as the Engine of Collaborative Data Science</a> (2015)</li><li>The New Stack, <a href="https://thenewstack.io/kotlin-notebook-jupyter-python-retreat/">JetBrains kills Kotlin Notebook months after Microsoft’s Polyglot exit. But Jupyter is doing just fine.</a> (June 2026)</li><li>Sonar, <a href="https://www.sonarsource.com/blog/cli-vs-ide/">CLI vs. IDE: How to choose the best environment</a> (July 2026)</li><li>The New Stack, <a href="https://thenewstack.io/cli-ide-ai-verification/">CLI or IDE? Build in verification first</a> (August 2026, sponsored by Sonar)</li><li>Hacker News, <a href="https://news.ycombinator.com/item?id=46474225">Ask HN: Do you prefer AI coding in an IDE or CLI? And why?</a></li><li>Databricks docs, <a href="https://docs.databricks.com/aws/en/notebooks/code-assistant">Get coding help from Genie Code</a></li><li>Databricks docs, <a href="https://docs.databricks.com/gcp/en/notebooks/databricks-assistant-faq">Databricks Assistant FAQ</a></li><li>Google Developers Blog, <a href="https://developers.googleblog.com/new-ai-first-google-colab-now-available-to-everyone/">The new AI-first Google Colab is now available to everyone</a> (June 2025)</li><li>Google Developers Blog, <a href="https://developers.googleblog.com/en/data-science-agent-in-colab-with-gemini/">Data Science Agent in Colab</a> (March 2025)</li><li>Azure Databricks docs, <a href="https://learn.microsoft.com/en-us/azure/databricks/notebooks/ds-agent">Use Genie Code for data science</a></li><li>Hex, <a href="https://hex.tech/blog/introducing-notebook-agent/">Introducing the Notebook Agent</a></li><li>Deepnote, <a href="https://github.com/deepnote">Deepnote on GitHub</a></li><li>Google Developers Blog, <a href="https://developers.googleblog.com/announcing-the-colab-mcp-server-connect-any-ai-agent-to-google-colab/">Announcing the Colab MCP Server</a> (March 2026)</li><li>Hex, <a href="https://hex.tech/blog/introducing-the-hex-cli/">Introducing the Hex CLI</a> (April 2026)</li><li>Databricks docs, <a href="https://docs.databricks.com/aws/en/agent-skills/">Agent skills for AI coding assistants</a></li><li>marimo, <a href="https://marimo.io/blog/notebooks-as-a-tool-for-agents">Implementing marimo pair: letting agents use notebooks as a tool</a> (July 2026)</li><li>Jupyter AI, <a href="https://pypi.org/project/jupyter-ai/">PyPI project page</a></li><li>Databricks docs, <a href="https://docs.databricks.com/aws/en/notebooks/use-databricks-assistant">Use Genie Code</a></li><li>Jupyter AI, <a href="https://github.com/jupyterlab/jupyter-ai/releases">Release notes</a></li><li>Pimentel et al., <a href="https://ieeexplore.ieee.org/document/8816763/">A Large-Scale Study About Quality and Reproducibility of Jupyter Notebooks</a> (MSR 2019)</li><li>Rule et al., <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1007007">Ten simple rules for writing and sharing computational analyses in Jupyter Notebooks</a> (PLOS Computational Biology, 2019)</li><li>Hex, <a href="https://hex.tech/blog/fall-2025-launch/">Fall 2025 launch</a></li><li>InfoQ, <a href="https://www.infoq.com/news/2026/04/colab-mcp-server/">Coverage of the Colab MCP Server</a> (April 2026)</li><li>Databricks docs, <a href="https://docs.databricks.com/aws/en/genie-code/">Genie Code</a></li><li>Jupyter AI docs, <a href="https://jupyter-ai.readthedocs.io/en/stable/getting-started.html">Getting started</a></li><li>Claude Code docs, <a href="https://code.claude.com/docs/en/tools-reference">Tools reference</a></li><li>Kanaries, <a href="https://docs.kanaries.net/articles/claude-code-jupyter-data-science">Can Claude Code analyze Jupyter notebooks for data science?</a></li><li>Talk Python To Me, <a href="https://talkpython.fm/episodes/show/555/marimo-pair-a-canvas-for-agent-developers-collaboration">Episode #555: marimo pair</a> (July 2026)</li><li>Google, <a href="https://blog.google/innovation-and-ai/technology/developers-tools/colab-updates/">Customize your Gemini agent in Colab</a> (April 2026)</li><li>Deepnote, <a href="https://github.com/deepnote/vscode-deepnote/pull/256">Snapshot mode in the VS Code extension</a> (January 2026)</li><li>Jupyter AI, <a href="https://github.com/jupyterlab/jupyter-ai/issues/1531">v3.0 release plan</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ac0e43d8f1f7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[MermaidBin: Paste a Mermaid diagram and get a shareable SVG]]></title>
            <link>https://medium.com/@gogasca_/mermaidbin-paste-a-mermaid-diagram-and-get-a-shareable-svg-81cc061fe2eb?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/81cc061fe2eb</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Sun, 02 Aug 2026 03:51:01 GMT</pubDate>
            <atom:updated>2026-08-02T03:51:01.299Z</atom:updated>
            <content:encoded><![CDATA[<p>I built <a href="https://mermaidbin.com/">MermaidBin</a>, a small pastebin for Mermaid diagrams.</p><p>Paste Mermaid source, see the diagram render as you type, and click <strong>Share</strong>.<br>You get a permanent page plus a direct `.svg` URL that works in Markdown:</p><p>```md<br>![System diagram](<a href="https://mermaidbin.com/your-paste-id.svg">https://mermaidbin.com/your-paste-id.svg</a>)<br>```</p><p>There is no account to create. Pastes can be public or unlisted, and public is the default. The shared page also lets you copy the source, copy an embed, or download the SVG.</p><p><strong>Why I built it</strong></p><p>Mermaid is convenient because diagrams live as text next to the ideas they<br>describe. The awkward part is sharing one outside the repository or tool where it was written.</p><p>I wanted a workflow as small as this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ngiuUrAD0IZn_m5OBXdLsg.png" /></figure><p>Example: <a href="https://mermaidbin.com/LNze5LO5WP1tqwdB-e4UUG.svg">https://mermaidbin.com/LNze5LO5WP1tqwdB-e4UUG</a></p><p>A screenshot loses the source. A Mermaid code block depends on the destination supporting Mermaid. An online editor is useful, but often the thing I actually want is a URL I can paste into a README, issue, chat, or document.</p><p>MermaidBin is an attempt to make that last step boring.</p><p><strong>The interesting implementation detail</strong></p><p>The browser and the server solve different parts of the problem.</p><p>The browser renders Mermaid immediately. This gives fast feedback while typing and means sharing does not wait for a headless browser. When a paste is created, the API stores its source in PostgreSQL and returns the permanent page.</p><p>A separate worker then renders the durable SVG using Mermaid CLI and Chromium.<br>The worker sanitizes the output and stores it with the paste. The public API<br>process never needs Chromium, and the stable `.svg` URL can be cached and embedded independently of the JavaScript viewer.</p><p><strong>From polling to LISTEN/NOTIFY</strong></p><p>The first version of the worker repeatedly checked PostgreSQL for pending<br>renders. It worked, but the user-visible result was a “Preparing SVG” state for up to the polling interval. Reducing the interval made the UI faster at the cost of constant database queries.</p><p>The current design uses PostgreSQL `LISTEN`/`NOTIFY`. The transaction that creates a paste also sends a notification. PostgreSQL delivers it after commit, and the worker wakes immediately.</p><p>I did not treat notifications as a durable queue. They can be missed while a<br>worker is disconnected. PostgreSQL remains the source of truth, and the worker still performs a reconciliation scan every 30 seconds. It also reclaims jobs that have been stuck in `rendering` for five minutes.</p><p>That combination gave me the behavior I wanted:</p><p>- Low latency in the normal case;<br>- No 250 ms database polling loop;<br>- Recovery after a worker restart or broken LISTEN connection;<br>- A simple path to multiple workers with `FOR UPDATE SKIP LOCKED`.</p><p>At much higher volume I would probably use a dedicated queue, but for this product PostgreSQL is already present and the simpler operational model is valuable.</p><p><strong>Rendering untrusted diagrams</strong></p><p>Running Chromium on user-controlled source deserves care. The renderer is isolated from the web process and runs in a non-root container with a read-only root filesystem, dropped capabilities, a default seccomp profile, and only a small writable temporary volume.</p><p>Before rendering, it rejects external resource URLs. Mermaid runs in strict<br>mode with HTML labels disabled. The resulting SVG goes through `DOMPurify`, with scripts, event handlers, and `foreignObject` removed. Rendering has a timeout, and output is capped at 2 MiB.</p><p>This is defense in depth rather than a claim that rendering arbitrary input is risk-free. Keeping Chromium outside the API container limits what a renderer failure can affect and makes that boundary easier to reason about.</p><p><strong>A container bug that only appeared in the worker</strong></p><p>One of the more useful lessons came from Chromium itself. SVG rendering worked locally but failed in the hardened container with:</p><p>```text<br>chrome_crashpad_handler: — database is required<br>```</p><p>The worker has a read-only root filesystem. Chromium tried to initialize<br>Crashpad before it had a writable profile location and exited before rendering.<br>The fix was to put `HOME`, the Chromium profile, config, cache, and Crashpad state inside the worker’s per-render temporary directory, and to disable crash reporting. I also disabled `/dev/shm` usage because its small container default is another common source of Chromium failures.</p><p>The test suite now runs the real PostgreSQL → worker → Chromium → SVG path in Docker with the same read-only-root constraint as production. Mocking the renderer would never have found this.</p><p><strong>What is running?</strong></p><p>MermaidBin currently runs on a regional Google Kubernetes Engine:</p><p>- two small Node/Express API replicas;<br>- one isolated SVG renderer worker;<br>- Cloud SQL for PostgreSQL;<br>- Cloud SQL Auth Proxy sidecars;<br>- Cloud CDN and Cloud Armor in front of the service;<br>- Prometheus-compatible API and worker metrics.</p><p>SVG is stored in PostgreSQL for now. That keeps render state and output together and is simple at the current scale. PostgreSQL can handle values much larger than my application limits, but every diagram is capped at 200 KiB of source and 2 MiB of SVG. If storage volume becomes meaningful, moving SVG bodies to object storage is an obvious next step.</p><p><strong>Tradeoffs and unfinished parts</strong></p><p>This is a deliberately small product, and there are real limitations:</p><p>- “unlisted” means link-only, not private;<br>- there are no accounts or diagram revisions;<br>- expired rows are currently cleaned up lazily;<br>- the schema is bootstrapped by the app rather than versioned migrations;<br>- the fallback application rate limiter is per replica;<br>- the worker needs richer retry and dead-letter controls before large scale;<br>- browser-level visual regression coverage is still on the list.</p><p>I would rather expose those boundaries than pretend a small service is a fully mature platform.</p><p><strong>Try it</strong></p><p>MermaidBin is live at <a href="https://mermaidbin.com/">mermaidbin.com</a>.</p><p>I would especially value feedback on:</p><p>- whether the paste → share → embed workflow feels obvious;<br>- whether public-by-default is the right default;<br>- which Mermaid diagram types fail or render surprisingly;<br>- whether the direct SVG URL is useful in the tools where you write.</p><p>If you try it, please avoid pasting secrets or sensitive diagrams. Public<br>pastes are listed, and unlisted links are still accessible to anyone who has<br>the URL.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81cc061fe2eb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging & speeding up a vLLM/Gemma deployment on GKE]]></title>
            <link>https://medium.com/@gogasca_/debugging-speeding-up-a-vllm-gemma-deployment-on-gke-b9937fb21af9?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/b9937fb21af9</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Fri, 17 Jul 2026 06:43:24 GMT</pubDate>
            <atom:updated>2026-07-17T06:49:26.523Z</atom:updated>
            <content:encoded><![CDATA[<h3>Setup</h3><p>Followed <a href="https://docs.cloud.google.com/kubernetes-engine/docs/tutorials/serve-gemma-gpu-vllm#gemma-4-e2b-it">GKE’s Serve Gemma with GPUs and vLLM tutorial</a> for `google/gemma-4-E2B-it`, but deployed on a single `nvidia-l4` GPU (`g2-standard-8`) instead of the tutorial’s `nvidia-rtx-pro-6000`.</p><h3>Symptom</h3><p>Chat completions took 20–25+ seconds even for short prompts:</p><pre>$ curl http://127.0.0.1:8000/v1/chat/completions -X POST -H &quot;Content-Type: application/json&quot; -d &#39;{<br> &quot;model&quot;: &quot;google/gemma-4-E2B-it&quot;,<br> &quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Why is the sky blue?&quot;}],<br> &quot;chat_template_kwargs&quot;: {&quot;enable_thinking&quot;: true},<br> &quot;skip_special_tokens&quot;: false<br>}&#39; | python -m json.tool<br> % Total % Received % Xferd Average Speed Time Time Time Current<br> Dload Upload Total Spent Left Speed<br>100 5902 100 5643 100 259 239 10 0:00:25 0:00:23 0:00:02 1237</pre><p>`kubectl logs -f -l app=gemma-server` while that request was running showed the engine steadily reporting only ~45–50 tokens/s generation throughput, with a single request in flight at a time — no queueing or batching contention:</p><pre>(APIServer pid=1) INFO 07–17 05:33:47 [async_llm.py:420] Added request chatcmpl-bfed389dac8894c3–8a84daa5.<br>(APIServer pid=1) INFO 07–17 05:33:48 [loggers.py:259] Engine 000: Avg prompt throughput: 2.2 tokens/s, Avg generation throughput: 16.9 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.0%, Prefix cache hit rate: 0.0%<br>(APIServer pid=1) INFO 07–17 05:33:58 [loggers.py:259] Engine 000: Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 49.7 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.1%, Prefix cache hit rate: 0.0%<br>(APIServer pid=1) INFO 07–17 05:34:08 [loggers.py:259] Engine 000: Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 49.3 tokens/s, Running: 1 reqs, Waiting: 0 reqs, GPU KV cache usage: 0.1%, Prefix cache hit rate: 0.0%<br>(APIServer pid=1) INFO: 127.0.0.1:33494 - &quot;POST /v1/chat/completions HTTP/1.1&quot; 200 OK</pre><p>So the delay wasn’t a stall — it was genuinely slow decode. The response’s `usage` block confirmed the math: 1142 completion tokens at ~49 tok/s ≈ the observed ~23s.</p><pre>&quot;usage&quot;: {<br> &quot;prompt_tokens&quot;: 22,<br> &quot;total_tokens&quot;: 1164,<br> &quot;completion_tokens&quot;: 1142,<br> &quot;prompt_tokens_details&quot;: null<br>}</pre><h3>Root cause</h3><p>Two compounding factors:</p><p>1. <strong>chat_template_kwargs: {“enable_thinking”: true}`</strong> was roughly doubling output length by generating a full reasoning trace (visible in the response’s `”reasoning”` field) before the visible answer. Turning it off cut completion tokens from 1142 to 569 for the same prompt.</p><p>2. <strong>Gemma-4-E2B-it’s “E2B” name is misleading.</strong> Despite ~2B *effective* compute, the actual weight table is much bigger — confirmed straight from the vLLM startup log:</p><pre>(EngineCore pid=73) INFO 07–11 23:30:56 [gpu_model_runner.py:4601] Model loading took 9.79 GiB memory and 118.751103 seconds<br>(EngineCore pid=73) INFO 07–11 23:32:41 [gpu_worker.py:456] Available KV cache memory: 9.67 GiB<br>(EngineCore pid=73) INFO 07–11 23:32:41 [kv_cache_utils.py:1316] GPU KV cache size: 337,968 tokens</pre><p>9.79 GiB in bf16 is what you’d expect from a much larger model — this is a MatFormer/elastic architecture with large per-layer embedding tables. Autoregressive decode is memory-bandwidth-bound, and the L4’s ~300 GB/s bandwidth is the real ceiling here — nowhere near what an RTX PRO 6000 or A100 offers. This is why the tutorial’s stock bf16 config didn’t need quantization: it was written for faster hardware.</p><p>Confirming the node’s actual GPU:</p><pre>$ kubectl get node gke-gemma4-gpu-pool-1-e2a2b531–6jdu -o jsonpath=&#39;{.metadata.labels}&#39; | python3 -m json.tool | grep -i accelerator<br> &quot;cloud.google.com/gke-accelerator&quot;: &quot;nvidia-l4&quot;,</pre><h3>Fixes applied</h3><p>- <strong>`enable_thinking: false`</strong> (client-side) — cuts output length ~2x when the reasoning trace isn’t needed.<br>- `<strong>‑‑quantization=fp8</strong>` — native FP8 tensor cores on L4 (Ada Lovelace); shrank weights and raised throughput ~47 → ~74 tok/s (**+57%**) with no observed quality loss.<br>- `<strong>‑‑enable-auto-tool-choice</strong>` — was missing from the initial deploy; without it, `‑‑tool-call-parser=gemma4` is configured but never engaged, so tool/function calling silently doesn’t activate.<br>- `<strong>‑‑max-model-len=32768</strong>` (was `8192`) — matches the tutorial’s intended context window; the previous cap was silently truncating `max_tokens`.<br>- `<strong>‑‑gpu-memory-utilization=0.95</strong>` (was `0.90`) — grows the KV cache pool to safely support the larger context at the existing `‑‑max-num-seqs=16`, aided by memory freed from FP8.</p><h4>Applying FP8 quantization</h4><p>Patched directly onto the live Deployment to test quickly, before folding it into the checked-in manifest:</p><pre>kubectl patch deployment vllm-gemma-deployment - type=json \<br> -p=&#39;[{&quot;op&quot;: &quot;add&quot;, &quot;path&quot;: &quot;/spec/template/spec/containers/0/args/-&quot;, &quot;value&quot;: &quot; - quantization=fp8&quot;}]&#39;</pre><p>Startup logs confirmed it took effect cleanly, with no errors:</p><pre>(APIServer pid=1) INFO 07–17 05:55:45 [utils.py:299] non-default args: {…, &#39;quantization&#39;: &#39;fp8&#39;, …}<br>(EngineCore pid=174) INFO 07–17 05:57:34 [__init__.py:255] Selected CutlassFP8ScaledMMLinearKernel for Fp8OnlineLinearMethod<br>(EngineCore pid=174) INFO 07–17 05:59:34 [gpu_model_runner.py:4601] Model loading took 8.02 GiB memory and 119.515078 seconds</pre><p>Weight memory dropped from 9.79 GiB → 8.02 GiB (not a full 2x, since large embedding tables stay in bf16 and only linear/attention projections get quantized).</p><h4>Folding all changes into the deployment manifest</h4><p>Final diff against the manifest (`vllm-4-e2b-it.yaml`), reviewed with `kubectl diff -f` before applying:</p><pre>diff<br><br>- - enable-log-requests<br> - - enable-chunked-prefill<br> - - enable-prefix-caching<br>+ - - enable-auto-tool-choice<br> - - generation-config=auto<br> - - tool-call-parser=gemma4<br> - - dtype=bfloat16<br> - - max-num-seqs=16<br>- - - max-model-len=8192<br>- - - gpu-memory-utilization=0.90<br>+ - - max-model-len=32768<br>+ - - gpu-memory-utilization=0.95<br> - - reasoning-parser=gemma4<br> - - trust-remote-code<br> - - quantization=fp8</pre><pre>kubectl apply -f vllm-4-e2b-it.yaml<br>kubectl rollout status deployment/vllm-gemma-deployment - timeout=300s</pre><pre>deployment.apps/vllm-gemma-deployment configured<br>service/llm-service unchanged<br>deployment &quot;vllm-gemma-deployment&quot; successfully rolled out</pre><h4>Result</h4><p>Benchmarked with the same prompt before and after, `enable_thinking: false` in both cases:</p><p>- <strong>Original</strong> (bf16, `‑‑max-model-len=8192`) — 569 completion tokens in ~12s ≈ **47 tok/s**<br>- <strong>+ FP8 quantization</strong> — 592 completion tokens in ~8s ≈ **74 tok/s**<br>- <strong>+ all changes</strong> (FP8, 32K context, tool-choice), 2nd request / steady state — 517 completion tokens in ~7s ≈ **74 tok/s**</p><p>~57% faster generation, correct tool-calling behavior, and 4x the usable context window — all on cheaper L4 hardware than the tutorial assumes, no GPU upgrade needed.</p><h4>Takeaway</h4><p>vLLM’s per-step `Avg generation throughput` log line is the single most useful diagnostic — it separates “the model is slow” from “the request is queued/stuck.” And model name conventions (like “E2B”) can undersell actual memory footprint; check `Model loading took X GiB` in the startup logs before assuming a small model should be fast on modest hardware.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b9937fb21af9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Running Basic CUDA Kernels]]></title>
            <link>https://medium.com/@gogasca_/writting-cuda-kernels-fa0067220861?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/fa0067220861</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Tue, 02 Jun 2026 22:04:01 GMT</pubDate>
            <atom:updated>2026-07-07T05:32:34.219Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Running Basic CUDA Kernels</strong></p><p>A minimal CUDA kernel. The `__global__` keyword marks a function that runs on the GPU but is launched from the CPU. Each thread reads its own index from `threadIdx.x` and prints it.</p><pre>#include &lt;stdio.h&gt;<br><br>__global__ void roll_call() {<br> const int threadIndex = threadIdx.x;<br> printf(&quot;Thread %d here!\n&quot;, threadIndex);<br>}</pre><p>Launching the kernel with 1 thread block containing 10 threads. <em>cudaDeviceSynchronize()</em> makes the CPU wait until the GPU finishes — without it, the program could exit before any output appears.</p><pre>int main() {<br> roll_call&lt;&lt;&lt;1, 10&gt;&gt;&gt;();<br> cudaDeviceSynchronize();<br> return 0;<br>}</pre><p>Compile with NVIDIA’s `nvcc` compiler:</p><pre>$ nvcc roll_call.cu -o roll_call.o</pre><p>Each of the 10 threads announces itself:</p><pre>$ ./roll_call.o<br>Thread 0 here!<br>Thread 1 here!<br>Thread 2 here!<br>Thread 3 here!<br>Thread 4 here!<br>Thread 5 here!<br>Thread 6 here!<br>Thread 7 here!<br>Thread 8 here!<br>Thread 9 here!</pre><p><strong>Reading and Writing Data in CUDA Kernels</strong></p><p>A kernel that increments one array element per thread. Each thread uses its index to pick “its” element, so all 10 elements are updated in parallel.</p><pre>__global__ void array_increment(int* in) {<br> const int threadIndex = threadIdx.x;<br> in[threadIndex] = in[threadIndex] + 1;<br>}</pre><p>First, allocate and initialize the array on the host (CPU) side with plain `malloc`:</p><pre>const int arraySize = 10;<br>// Allocate host memory for the input array.<br>//<br>// The amount of memory allocated is equal to<br>// the length of the array times the size of<br>// an integer, or 10 * 4 = 40 bytes.<br>int* array = (int*)malloc(arraySize * sizeof(int));<br>// Initialize the input array with values 0, 10, 20, 30, …<br>for (int i = 0; i &lt; arraySize; i++) {<br> array[i] = i * 10;<br>}</pre><p>The GPU can’t read host memory directly, so we allocate a matching buffer in GPU (device) memory with <em>cudaMalloc</em>. The `d_` prefix is a common convention for device pointers.</p><pre>// Allocate GPU memory for the input array<br>int* d_array;<br>cudaMalloc(&amp;d_array, arraySize * sizeof(int));</pre><p>Copy the data from host to device. The last argument tells CUDA which direction the copy goes.</p><pre>cudaMemcpy(<br> d_array, // The destination memory address<br> array, // The source memory address<br> arraySize * sizeof(int), // The number of bytes to copy<br> cudaMemcpyHostToDevice // The direction of the copy<br>);</pre><p>Run the kernel on the device data, then copy the results back to the host:</p><pre>array_increment&lt;&lt;&lt;1, arraySize&gt;&gt;&gt;(d_array);<br>cudaMemcpy(<br> array, // The destination memory address<br> d_array, // The source memory address<br> arraySize * sizeof(int), // The number of bytes to copy<br> cudaMemcpyDeviceToHost // The direction of the copy<br>);</pre><p>The full program: allocate → copy to GPU → run kernel → copy back → free. This allocate/copy/compute/copy-back pattern is the backbone of almost every CUDA program.</p><pre>void printArray(int* array, int arraySize) {<br> printf(&quot;[&quot;);<br> for (int i = 0; i &lt; arraySize; i++) {<br>   printf(&quot;%d&quot;, array[i]);<br>   if (i &lt; arraySize - 1) {<br>    printf(&quot;, &quot;);<br>   }<br> }<br> printf(&quot;]\n&quot;);<br>}<br><br>int main() {<br> const int arraySize = 10;<br> // Allocate host memory for the input array<br> int* array = (int*)malloc(arraySize * sizeof(int));<br> // Initialize the input array<br> for (int i = 0; i &lt; arraySize; i++) {<br>  array[i] = i * 10;<br> }<br> printf(&quot;Before: &quot;);<br> printArray(array, arraySize);<br>// Allocate GPU memory for the input array<br> int* d_array;<br> cudaMalloc((void**)&amp;d_array, arraySize * sizeof(int));<br>// Copy the input array from host memory to GPU memory<br> cudaMemcpy(d_array, array, arraySize * sizeof(int), cudaMemcpyHostToDevice);<br>array_increment&lt;&lt;&lt;1, arraySize&gt;&gt;&gt;(d_array);<br>// Copy the result array from GPU memory back to host memory<br> cudaMemcpy(array, d_array, arraySize * sizeof(int), cudaMemcpyDeviceToHost);<br> printf(&quot;After: &quot;);<br> printArray(array, arraySize);<br>// Free the host and GPU memory<br> free(array);<br> cudaFree(d_array);<br> return 0;<br>}</pre><p>Every element was incremented by 1 — each by a different thread, all at once:</p><pre>$ ./array_increment<br>Before: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]<br>After: [1, 11, 21, 31, 41, 51, 61, 71, 81, 91]</pre><p><strong>Thread Blocks</strong></p><p>Thread blocks can be 2D (or 3D), which maps naturally onto matrices. Here each thread moves one element to its transposed position, computing a flat input index from `(x, y)` and a flat output index from the swapped `(y, x)`.</p><pre>__global__ void transpose_matrix(int* in, int* out) {<br> // Select an element from the input matrix by using the<br> // threadIdx x and y values. blockDim.x contains the number<br> // of rows in the 2D Thread Block.<br> const int threadIndex = threadIdx.x + threadIdx.y * blockDim.x;<br>// Select the corresponding position in the output matrix.<br> // blockDim.y contains the number of columns in the 2D Thread Block.<br> const int outIdx = threadIdx.y + threadIdx.x * blockDim.y;<br>out[outIdx] = in[threadIndex];<br>}</pre><p>A `dim3` struct defines the 2D block shape — one thread per matrix element:</p><pre>int main() {<br> // Allocate host &amp; GPU memory; copy the input array to GPU memory<br> // …<br>dim3 numThreadsPerBlock(rows, cols);<br> transpose_matrix&lt;&lt;&lt;1, numThreadsPerBlock&gt;&gt;&gt;(d_in_matrix, d_out_matrix);<br>// Release host and GPU memory<br> // …<br>}</pre><p><strong>Running Multiple Thread Blocks</strong></p><p>A single thread block is capped at 1024 threads. Asking for 1025 fails — and because kernel launches are asynchronous, you need `<em>cudaGetLastError()</em>` to actually see the error.</p><pre>int main() {<br> roll_call_kernel&lt;&lt;&lt;1, 1025&gt;&gt;&gt;();<br> cudaError_t err = cudaGetLastError();<br> if (err != cudaSuccess) {<br>  printf(&quot;Kernel launch error: %s\n&quot;, cudaGetErrorString(err));<br>  exit(-1);<br> }<br> cudaDeviceSynchronize();<br> return 0;<br>}</pre><pre>Kernel launch error: invalid configuration argument</pre><p><strong>Streaming Multiprocessors</strong></p><p>Which physical core is running my thread? This kernel uses a line of inline PTX assembly to read the special `%smid` register — the ID of the Streaming Multiprocessor (SM) executing the thread.</p><pre>__global__ void sm_roll_call() {<br> const int threadIndex = threadIdx.x;<br>uint streamingMultiprocessorId;<br> asm(&quot;mov.u32 %0, %smid;&quot; : &quot;=r&quot;(streamingMultiprocessorId));<br>printf(&quot;Thread %d running on SM %d!\n&quot;, threadIndex, streamingMultiprocessorId);<br>}<br>int main() {<br> sm_roll_call&lt;&lt;&lt;1, 5&gt;&gt;&gt;();<br> cudaDeviceSynchronize();<br> return 0;<br>}</pre><p>With one block, all threads land on the same SM — a block never spans multiple SMs:</p><pre>$ ./sm_roll_call<br>Thread 0 running on SM 0!<br>Thread 1 running on SM 0!<br>Thread 2 running on SM 0!<br>Thread 3 running on SM 0!<br>Thread 4 running on SM 0!</pre><p>Launching 4 blocks instead:</p><pre>int main() {<br> // Launch 4 thread blocks with 2 threads per block<br> sm_roll_call&lt;&lt;&lt;4, 2&gt;&gt;&gt;();<br> cudaDeviceSynchronize();<br> return 0;<br>}</pre><p>Now the blocks are distributed across different SMs and run truly in parallel:</p><pre>Thread 0 running on SM 6!<br>Thread 1 running on SM 6!<br>Thread 0 running on SM 4!<br>Thread 1 running on SM 4!<br>Thread 0 running on SM 0!<br>Thread 1 running on SM 0!<br>Thread 0 running on SM 2!<br>Thread 1 running on SM 2!</pre><p><strong>Warps</strong></p><p>Threads don’t execute individually — the hardware groups them into *warps* of 32 that execute in lockstep. This kernel also reads the `%warpid` and `%laneid` registers to show each thread’s warp and its position (lane) within it.</p><pre>__global__ void warp_roll_call() {<br> const int threadIndex = threadIdx.x;<br>uint streamingMultiprocessorId;<br> asm(&quot;mov.u32 %0, %smid;&quot; : &quot;=r&quot;(streamingMultiprocessorId));<br>uint warpId;<br> asm volatile(&quot;mov.u32 %0, %warpid;&quot; : &quot;=r&quot;(warpId));<br>uint laneId;<br> asm volatile(&quot;mov.u32 %0, %laneid;&quot; : &quot;=r&quot;(laneId));<br>printf(&quot;SM: %d | Warp: %d | Lane: %d | Thread %d - Here!\n&quot;,<br> streamingMultiprocessorId, warpId, laneId, threadIndex);<br>}</pre><p>With 2 threads per block, each block occupies a single (mostly empty) warp:</p><pre>SM: 6 | Warp: 0 | Lane: 0 | Thread 0 - Here!<br>SM: 6 | Warp: 0 | Lane: 1 | Thread 1 - Here!<br>SM: 4 | Warp: 0 | Lane: 0 | Thread 0 - Here!<br>SM: 4 | Warp: 0 | Lane: 1 | Thread 1 - Here!<br>SM: 2 | Warp: 0 | Lane: 0 | Thread 0 - Here!<br>SM: 2 | Warp: 0 | Lane: 1 | Thread 1 - Here!<br>SM: 0 | Warp: 0 | Lane: 0 | Thread 0 - Here!<br>SM: 0 | Warp: 0 | Lane: 1 | Thread 1 - Here!</pre><p>With more than 32 threads in a block, the block is split into multiple warps: threads 0–31 fill warp 0, threads 32+ spill into warp 1, and lane IDs restart at 0 within each warp.</p><pre>SM: 0 | Warp: 1 | Lane: 0 | Thread 32 - Here!<br>SM: 0 | Warp: 1 | Lane: 1 | Thread 33 - Here!<br>SM: 0 | Warp: 1 | Lane: 2 | Thread 34 - Here!<br>…<br>SM: 0 | Warp: 0 | Lane: 30 | Thread 30 - Here!<br>SM: 0 | Warp: 0 | Lane: 31 | Thread 31 - Here!</pre><p><strong>Global Memory</strong></p><p>A deliberately naive dot product: a single thread loops over both vectors in global memory — the largest but slowest memory on the GPU. Every `a[i]` and `b[i]` read goes out to global memory, making this a useful baseline for profiling.</p><pre>__global__ void dot_product(float* a, float* b, float* c, int size) {<br> // Compute the dot product of a and b, then store the result in c<br> float sum = 0;<br> for (int i = 0; i &lt; size; i++) {<br> sum += a[i] * b[i];<br> }<br> *c = sum;<br>}</pre><p>Launched with a single thread (`&lt;&lt;&lt;1, 1&gt;&gt;&gt;`) — no parallelism at all, on purpose:</p><pre>int main() {<br> const int vectorLength = 16;<br>// Allocate memory for the input arrays and the output array<br> // …<br>dot_product&lt;&lt;&lt;1, 1&gt;&gt;&gt;(d_a, d_b, d_c, vectorLength);<br>// Copy the result back to host memory<br> // …<br>}</pre><p>Profile it with Nsight Compute to see where the memory traffic goes:</p><pre>sudo ncu - set=full -f -o dot_product ./dot_product</pre><p><strong>Local Memory &amp; Registers</strong></p><p>In the generated PTX assembly, scalar variables like `sum` live in registers — the fastest storage on the GPU. This instruction initializes register `%f4` to `0.0f`:</p><pre>mov.f32 %f4, 0f00000000;</pre><p><strong>Shared Memory</strong></p><p>Shared memory and L1 cache come out of the same physical pool on the SM. `cudaFuncSetAttribute` lets you hint how to split that pool for a specific kernel:</p><pre>cudaFuncSetAttribute(<br> my_kernel,<br> cudaFuncAttributePreferredSharedMemoryCarveout,<br> 20 // Use 20% of combined L1/Shared Memory for Shared Memory<br>);<br>my_kernel&lt;&lt;&lt;num_blocks, num_threads&gt;&gt;&gt;();</pre><p><strong>Thread Synchronization</strong></p><p>A first parallel softmax. Each thread is responsible for a slice of the output (`start_idx` to `end_idx`), but the max and the sum of exponentials are computed *redundantly* — every thread loops over the entire input. Correct, but wasteful.</p><pre>__global__ void softmax_kernel(float* input, float* output, int size) {<br> // Number of threads in the Thread Block.<br> // Assumes that the Thread Block is one-dimensional.<br> int num_threads = blockDim.x;<br>// Each thread will compute the softmax of<br> // num_elements_per_thread elements<br> int num_elements_per_thread = size / num_threads;<br>int thread_index = threadIdx.x;<br>// This thread will compute the softmax of elements from<br> // start_idx to end_idx in the input array<br> int start_idx = thread_index * num_elements_per_thread;<br> int end_idx = min(size, start_idx + num_elements_per_thread);<br>// Loop over the input array to find the maximum value<br> float max_val = 0.0f;<br> for (int i = 0; i &lt; size; i++) {<br> if (input[i] &gt; max_val) {<br> max_val = input[i];<br> }<br> }<br>// Loop over the input array to compute the sum of the exponentials<br> float sum_exp = 0.0f;<br> for (int i = 0; i &lt; size; i++) {<br> sum_exp += expf(input[i] - max_val);<br> }<br>// Store the softmax result in the output array<br> for (int i = start_idx; i &lt; end_idx; i++) {<br> output[i] = expf(input[i] - max_val) / sum_exp;<br> }<br>}</pre><p><strong>Example: Softmax with Shared Memory</strong></p><p>The optimized version: each thread computes the max and exponential sum only over *its own slice*, publishes the partial result to fast on-chip shared memory (`__shared__`), and then `__syncthreads()` acts as a barrier so no thread reads the shared arrays before every thread has written to them. The partial results are then combined into the global max and sum.</p><pre>__global__ void softmax_kernel_smem(float* input, float* output, int size) {<br> // Number of threads in the Thread Block.<br> // Assumes that the Thread Block is one-dimensional.<br> int num_threads = blockDim.x;<br>// Each thread will compute the softmax of<br> // num_elements_per_thread elements<br> int num_elements_per_thread = size / num_threads;<br>int thread_index = threadIdx.x;<br>// This thread will compute the softmax of elements from<br> // start_idx to end_idx in the input array<br> int start_idx = thread_index * num_elements_per_thread;<br> int end_idx = min(size, start_idx + num_elements_per_thread);<br>// Array in shared memory to store the local max values<br> __shared__ float shared_max_val[NUM_THREADS];<br>float max_val = 0.0f;<br> for (int i = start_idx; i &lt; end_idx; i++) {<br> if (input[i] &gt; max_val) {<br> max_val = input[i];<br> }<br> }<br> shared_max_val[thread_index] = max_val;<br>// Wait for all threads to finish writing their<br> // local max values to shared memory<br> __syncthreads();<br>for (int i = 0; i &lt; num_threads; i++) {<br> if (shared_max_val[i] &gt; max_val) {<br> max_val = shared_max_val[i];<br> }<br> }<br>// Array in shared memory to store the local sums of<br> // the exponentials<br> __shared__ float shared_sum_exp[NUM_THREADS];<br>float sum_exp = 0.0f;<br> for (int i = start_idx; i &lt; end_idx; i++) {<br> sum_exp += expf(input[i] - max_val);<br> }<br> shared_sum_exp[thread_index] = sum_exp;<br>// Wait for all threads to finish writing their<br> // local sums<br> __syncthreads();<br>for (int i = 0; i &lt; num_threads; i++) {<br> sum_exp += shared_sum_exp[i];<br> }<br>// Compute softmax<br> for (int i = start_idx; i &lt; end_idx; i++) {<br> output[i] = expf(input[i] - max_val) / sum_exp;<br> }<br>}</pre><p><strong>Writing Custom PyTorch Kernels — pybind11</strong></p><p>pybind11 is the bridge that exposes C++ functions to Python. Install it first:</p><pre>sudo apt-get install python3-pybind11</pre><p>Kernels can’t be launched directly from Python, so we wrap the launch in a plain C++ “launcher” function:</p><pre>void roll_call_launcher() {<br> roll_call_kernel&lt;&lt;&lt;1, 5&gt;&gt;&gt;();<br> cudaDeviceSynchronize();<br>}</pre><p>The binding file declares the launcher and registers it as a Python function. `PYBIND11_MODULE` generates the module boilerplate — after this, Python can call `example_kernels.roll_call()`.</p><pre>#include &lt;torch/extension.h&gt;<br>// Declare the roll_call_launcher function<br>void roll_call_launcher();<br>// Write the C++ function that we will call from Python<br>void roll_call_binding() {<br> roll_call_launcher();<br>}<br>PYBIND11_MODULE(example_kernels, m) {<br> m.def(<br> &quot;roll_call&quot;, // Name of the Python function to create<br> &amp;roll_call_binding, // Corresponding C++ function to call<br> &quot;Launches the roll_call kernel&quot; // Docstring<br> );<br>}</pre><p>PyTorch’s `CUDAExtension` handles the tricky part — compiling `.cu` files with `nvcc` and `.cpp` files with the host compiler, then linking them into one Python module:</p><pre>from setuptools import setup<br>from torch.utils.cpp_extension import BuildExtension, CUDAExtension<br>__version__ = &quot;0.0.1&quot;<br><br># Define the C++ extension modules<br>ext_modules = [<br> CUDAExtension(&#39;example_kernels&#39;, [<br> &#39;csrc/roll_call/roll_call_binding.cpp&#39;,<br> &#39;csrc/roll_call/roll_call.cu&#39;,<br> ])<br>]<br>setup(<br> name=&quot;cuda_basics&quot;,<br> version=__version__,<br> ext_modules=ext_modules,<br> cmdclass={&quot;build_ext&quot;: BuildExtension},<br>)</pre><p>Once built, the CUDA kernel is callable like any Python function:</p><pre>$ python<br>Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux<br>Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.<br>&gt;&gt;&gt; import example_kernels<br>&gt;&gt;&gt; example_kernels.roll_call()<br>Thread 0 here!<br>Thread 1 here!<br>Thread 2 here!<br>Thread 3 here!<br>Thread 4 here!</pre><p>Best practice: use the `TORCH_EXTENSION_NAME` macro instead of hardcoding the module name, so the name in `setup.py` and the binding can never drift apart:</p><pre>PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {<br> m.def(<br> &quot;roll_call&quot;, // Name of the Python function to create<br> &amp;roll_call_binding, // Corresponding C++ function to call<br> &quot;Launches the roll_call kernel&quot; // Docstring<br> );<br>}</pre><p><strong>Using PyTorch in C++</strong></p><p>To operate on real tensors, the launcher accepts a `torch::Tensor` and extracts the raw GPU pointer with `data_ptr&lt;int&gt;()` — the kernel itself doesn’t change at all:</p><pre>#include &lt;torch/extension.h&gt;<br>__global__ void array_increment(int* in) {<br> const int threadIndex = threadIdx.x;<br> in[threadIndex] = in[threadIndex] + 1;<br>}<br>void array_increment_launcher(torch::Tensor&amp; in, int arraySize) {<br> array_increment&lt;&lt;&lt;1, arraySize&gt;&gt;&gt;(in.data_ptr&lt;int&gt;());<br>}</pre><p>The binding now passes the tensor through from Python to the launcher:</p><pre>#include &lt;torch/extension.h&gt;<br>void array_increment_launcher(torch::Tensor&amp; in, int arraySize);<br>// Write the C++ function that we will call from Python<br>void array_increment_binding(torch::Tensor&amp; in, int arraySize) {<br> array_increment_launcher(in, arraySize);<br>}<br>PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {<br> m.def(<br> &quot;array_increment&quot;, // Name of the Python function to create<br> &amp;array_increment_binding, // Corresponding C++ function to call<br> &quot;Launches the array_increment kernel&quot; // Docstring<br> );<br>}</pre><p>The kernel modifies the PyTorch tensor in place, directly in GPU memory — no copies:</p><pre>&gt;&gt;&gt; import torch<br>&gt;&gt;&gt; import array_increment_kernel<br>&gt;&gt;&gt; input = torch.tensor([1, 2, 3, 4, 5], dtype=torch.int).cuda()<br>&gt;&gt;&gt; array_increment_kernel.array_increment(input, 5)<br>&gt;&gt;&gt; input<br>tensor([2, 3, 4, 5, 6], device=&#39;cuda:0&#39;, dtype=torch.int32)</pre><p><strong>Dealing with Data Types</strong></p><p>There’s a catch: `data_ptr&lt;int&gt;()` hardcodes the type. Pass a float tensor and PyTorch throws immediately:</p><pre>&gt;&gt;&gt; input = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float).cuda()<br>&gt;&gt;&gt; array_increment_kernel.array_increment(input, 5)<br>Traceback (most recent call last):<br> File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;<br>RuntimeError: expected scalar type Int but found Float</pre><p>Step one of the fix: make the kernel a C++ template so it works for any element type:</p><pre>template &lt;typename T&gt;<br>__global__ void array_increment(T* in) {<br> const int threadIndex = threadIdx.x;<br> in[threadIndex] = in[threadIndex] + 1;<br>}</pre><p>Step two: `AT_DISPATCH_FLOATING_TYPES` inspects the tensor’s dtype at runtime and instantiates the template with the matching type, exposed inside the lambda as `scalar_t`:</p><pre>void array_increment_launcher(torch::Tensor&amp; in, int arraySize) {<br> AT_DISPATCH_FLOATING_TYPES(in.scalar_type(), &quot;array_increment_launcher&quot;, [&amp;]() {<br> array_increment&lt;&lt;&lt;1, arraySize&gt;&gt;&gt;(in.data_ptr&lt;scalar_t&gt;());<br> });<br>}</pre><p>The same binding now handles `float32` and `float64` tensors transparently:</p><pre>&gt;&gt;&gt; import torch, array_increment_kernel<br>&gt;&gt;&gt; input_f32 = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32).cuda()<br>&gt;&gt;&gt; array_increment_kernel.array_increment(input_f32, 5)<br>&gt;&gt;&gt; input_f32<br>tensor([2., 3., 4., 5., 6.], device=&#39;cuda:0&#39;)<br>&gt;&gt;&gt; input_f64 = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float64).cuda()<br>&gt;&gt;&gt; array_increment_kernel.array_increment(input_f64, 5)<br>&gt;&gt;&gt; input_f64<br>tensor([2., 3., 4., 5., 6.], device=&#39;cuda:0&#39;, dtype=torch.float64)</pre><p>References:</p><p><a href="https://tinkerd.net/blog/machine-learning/cuda-basics/">https://tinkerd.net/blog/machine-learning/cuda-basics/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fa0067220861" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using OverlayBD to improve startup time]]></title>
            <link>https://medium.com/@gogasca_/using-overlaybd-to-improve-startup-time-6c5f90f23345?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/6c5f90f23345</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Thu, 19 Feb 2026 10:38:33 GMT</pubDate>
            <atom:updated>2026-02-19T10:46:04.893Z</atom:updated>
            <content:encoded><![CDATA[<h3>Faster Container Startup with OverlayBD</h3><p><strong>The Problem with Container Startup</strong></p><p>Containers are a great way to package and deploy applications.<br>However, starting a container can be slow, especially for large images.<br>This is because the container runtime needs to download and extract the entire image before it can start the container.<br>This process can take a long time, especially for large images with many layers. In this blog I explained how I speed up Colab container to &lt; 1 second startup time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/312/1*IYO3a7RkW6LUhufmXL7l-g.jpeg" /><figcaption>overlaybd logo</figcaption></figure><h3>OverlayBD to the Rescue</h3><p><a href="https://containerd.github.io/overlaybd/#/">OverlayBD </a>is a technology that can significantly improve container startup time.<br>It does this by converting the container image into a block device format that can be mounted efficiently by the container runtime.<br>Instead of extracting every layer sequentially, the image is presented as a virtual block device.<br>This allows for lazy loading of the container image, which means that only the necessary parts of the image are loaded into memory when they are needed.</p><h3>How it Works</h3><p>OverlayBD uses the <a href="https://docs.kernel.org/target/tcmu-design.html">Linux TCMU</a> (Target Core Module Userspace) subsystem to present the container image as a virtual block device.<br>When the container runtime starts a container, it mounts the virtual block device as the root filesystem.<br>When the container accesses a file, the corresponding data is read from the virtual block device and loaded into memory.<br>This process is transparent to the container and the application running inside it.</p><h3>Architecture</h3><p>The OverlayBD architecture consists of two main components:</p><p>* **overlaybd-tcmu**: This component is responsible for presenting the container image as a TCMU block device.<br> It reads data from the container image and serves it to the kernel when requested.<br>* **overlaybd-snapshotter**: This component is a containerd proxy plugin that manages the snapshots of the container image.<br> It works with `overlaybd-tcmu` to provide the container runtime with a mountable filesystem.</p><p>Here is a high level diagram of the architecture:</p><p>```<br>containerd<br> └── overlaybd snapshotter (proxy plugin)<br> ├── overlaybd-snapshotter (manages snapshots, serves gRPC)<br> └── overlaybd-tcmu (presents layers as TCMU block devices)<br> ├── reads from /opt/overlaybd/fuse_cache/ (if FUSE cache active)<br> │ ├── SSD tier: /opt/overlayd/registry_cache/<br> │ └── GCS tier: /mnt/gcsfuse_cache/ (optional)<br> ├── or reads from /opt/overlaybd/registry_cache/ (direct, default)<br> └── target_core_user (kernel module)<br>```</p><h3>Optimizing Performance with Caching</h3><p>OverlayBD provides several caching mechanisms to optimize performance.</p><p><strong>Warm Cache</strong></p><p>A warm cache contains all the data that is needed to start an application.<br>By creating a warm cache, you can significantly reduce the startup time of your application.<br>To create a warm cache, you first need to profile the startup of your application.<br>This can be done by running the `overlaybd-deploy profile-startup` command.<br>This command will clear the cache and run a container to record which data blocks are accessed during its initial startup.<br>This will generate a `startup-profile.json` file.</p><p>Once you have the startup profile, you can use it to pre warm the local cache by running the `overlaybd-deploy manage-cache warm` command.<br>This command will read the `startup-profile.json` file and download all the required blobs into the local SSD cache.<br>After this step, subsequent container starts will be much faster as they will read from the local SSD instead of the network.</p><p>You can then snapshot the warm cache and deploy it to other machines. This can be done by running the `overlaybd-deploy manage-cache snapshot` command.</p><p><strong>FUSE Multi-Tier Cache</strong></p><p>OverlayBD also provides a FUSE passthrough filesystem that sits in front of the SSD cache.<br>This adds optional GCS fallback and memory pinning of hot blobs.<br>OverlayBD’s TCMU reads from the FUSE mount instead of the SSD cache directly.</p><p>Reads check SSD first, then fall through to GCS.<br>Blobs served from GCS are asynchronously promoted (copied) to SSD for future reads.</p><p>To enable the FUSE cache, you can run the `overlaybd-deploy fuse-cache start` command.</p><p><strong>Kernel I/O Tuning</strong></p><p>OverlayBD also applies kernel level I/O tunings to optimize for cache workloads.<br>These tunings include increasing the block device read ahead value and adjusting the virtual memory dirty page ratio.<br>These tunings help to improve the performance of the cache and reduce the number of I/O operations.</p><h3>Getting Started</h3><p>The `overlaybd-deploy` package provides a CLI to install and configure OverlayBD. Check the following repo: <a href="https://github.com/codeexec/overlaybd-deploy">https://github.com/codeexec/overlaybd-deploy</a></p><h4>Installation</h4><p>You can install the `overlaybd-deploy` package from PyPI:</p><p>```bash<br>pip install overlaybd-deploy<br>```</p><p>Or you can install it from the git repository:</p><p>```bash<br>git clone <a href="https://github.com/codeexec/overlaybd-deploy.git">https://github.com/containerd/overlaybd.gi</a>t<br>cd overlaybd-deploy<br>pip install -e “.[dev]”<br>```</p><h4>Quick Start</h4><p>Once you have installed the `overlaybd-deploy` package, you can use the following commands to get started:</p><p>```bash<br># 1. Install OverlayBD (idempotent)<br>sudo overlaybd-deploy install</p><p># 2. Set up credentials (pick one)<br>sudo overlaybd-deploy setup-credentials oauth2 # temporary (1 hour)<br>sudo overlaybd-deploy setup-credentials service-account /path/to/key.json # permanent</p><p># 3. Pull and run<br>export GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT:-$(gcloud config get-value project)}<br>sudo overlaybd-deploy pull-image<br>sudo ctr run — snapshotter overlaybd — rm \<br> “us-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/colab-optimized/runtime:latest_obd” \<br> test /bin/echo “hello from overlaybd”<br>```</p><p>For more information, please refer to the [git repository](<a href="https://github.com/containerd/overlaybd">https://github.com/containerd/overlaybd</a>).</p><h3>The Benefits of OverlayBD</h3><p>The main benefit of using OverlayBD is faster container startup time.<br>For example, with OverlayBD, a 27GB container image can start in as little as 170 milliseconds with a warm cache.<br>With lazy loading, the same image can be pulled in 2.2 seconds and started in 5.6 seconds.<br>This is a significant improvement compared to the traditional container startup process, which can take several minutes for an image of this size.</p><p>Another benefit of OverlayBD is that it allows for lazy loading of the container image.<br>This means that you don’t have to wait for the entire image to be downloaded before you can start the container.<br>This is especially useful in development environments, where you may want to quickly test a change to your application.</p><h3>Conclusion</h3><p>OverlayBD is a powerful technology that can significantly improve container startup time.<br>It is a great choice for anyone who is working with large container images and wants to improve the performance of their containerized applications.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6c5f90f23345" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker container analysis and optimizations]]></title>
            <link>https://medium.com/@gogasca_/docker-container-analysis-and-optimizations-629f58073784?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/629f58073784</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Mon, 29 Dec 2025 02:16:56 GMT</pubDate>
            <atom:updated>2026-02-01T03:42:35.611Z</atom:updated>
            <content:encoded><![CDATA[<p>In the world of Machine Learning operations (MLOps), “fat” containers are often a necessary evil. To minimize runtime latency for Data Scientists, we pre-bake gigabytes of libraries: TensorFlow, PyTorch, CUDA, Julia into a single image. But when your Docker image swells to 38GB, efficient compute infrastructure isn’t just about storage; it’s about the microscopic details of startup performance.</p><p>How do you optimize infrastructure for Prediction and Runtimes when the container itself is a monolith? It requires moving beyond simple size checks and performing a forensic analysis of the boot process.</p><p>In this post, we are going to perform an autopsy on the public Google Colab runtime image. We will use tools like docker inspect, ncdu, and strace to look inside the layers, visualize the disk budget, and debunk common performance myths. By tracing the exact system calls during startup, we will identify what actually causes latency—distinguishing between necessary overhead and invisible bloat like unused Jupyter extensions.</p><p>Analyzing Docker image:</p><p>The following commands allow you to get an understanding of what is contained in each image, in this case we are analyzing the Public Colab image:</p><pre>DOCKER_IMAGE=us-docker.pkg.dev/colab-images/public/runtime<br><br>docker manifest inspect $DOCKER_IMAGE<br>docker inspect $DOCKER_IMAGE<br>docker history $DOCKER_IMAGE<br>docker history --format &#39;{{.Size}}\t{{.ID}}\t{{.CreatedAt}}\t{{.CreatedBy}}&#39; $DOCKER_IMAGE --no-trunc | sort -rh<br>alias dive=&quot;docker run -ti --rm  -v /var/run/docker.sock:/var/run/docker.sock docker.io/wagoodman/dive&quot;<br>dive $DOCKER_IMAGE</pre><pre>DOCKER_IMAGE=us-docker.pkg.dev/colab-images/public/runtime<br>docker manifest inspect $DOCKER_IMAGE<br>{<br>   &quot;schemaVersion&quot;: 2,<br>   &quot;mediaType&quot;: &quot;application/vnd.oci.image.index.v1+json&quot;,<br>   &quot;manifests&quot;: [<br>      {<br>         &quot;mediaType&quot;: &quot;application/vnd.oci.image.manifest.v1+json&quot;,<br>         &quot;size&quot;: 12111,<br>         &quot;digest&quot;: &quot;sha256:d954b05cd315ef5db8a6aac71487f28cce49df9a27a8c1bdff3dea94638edc24&quot;,<br>         &quot;platform&quot;: {<br>            &quot;architecture&quot;: &quot;amd64&quot;,<br>            &quot;os&quot;: &quot;linux&quot;<br>         }<br>      },<br>      {<br>         &quot;mediaType&quot;: &quot;application/vnd.oci.image.manifest.v1+json&quot;,<br>         &quot;size&quot;: 566,<br>         &quot;digest&quot;: &quot;sha256:06bc74963df5e8cef1bed9f2bcd54818fc4b6b675b61ed4c3247e67c53914459&quot;,<br>         &quot;platform&quot;: {<br>            &quot;architecture&quot;: &quot;unknown&quot;,<br>            &quot;os&quot;: &quot;unknown&quot;<br>         }<br>      }<br>   ]<br>}</pre><p>This output indicates that the tag is an <strong>OCI Image Index</strong> (a “manifest list”), which acts as a wrapper grouping two distinct items:</p><ol><li><strong>The Runnable Image (</strong><strong>linux/amd64):</strong> This is the actual container image that executes your code.</li><li><strong>The Metadata Artifact (</strong><strong>unknown/unknown):</strong> This is a non-runnable attachment—likely a <strong>SLSA provenance attestation</strong> or signature.</li></ol><p><strong>The takeaway:</strong> Modern registries use this structure to bundle security verification data alongside the software itself. Docker clients are smart enough to pull the runnable image (Item 1) while security tools can inspect the metadata (Item 2) to verify the build integrity.</p><pre>DIGEST=&quot;sha256:06bc74963df5e8cef1bed9f2bcd54818fc4b6b675b61ed4c3247e67c53914459&quot;<br>REPO=&quot;us-docker.pkg.dev/colab-images/public/runtime&quot;<br><br>curl -H &quot;Authorization: Bearer $(gcloud auth print-access-token)&quot; \<br>     -H &quot;Accept: application/vnd.oci.image.manifest.v1+json&quot; \<br>     &quot;https://us-docker.pkg.dev/v2/colab-images/public/runtime/manifests/$DIGEST&quot;<br>{<br>  &quot;schemaVersion&quot;: 2,<br>  &quot;mediaType&quot;: &quot;application/vnd.oci.image.manifest.v1+json&quot;,<br>  &quot;config&quot;: {<br>    &quot;mediaType&quot;: &quot;application/vnd.oci.image.config.v1+json&quot;,<br>    &quot;digest&quot;: &quot;sha256:679c6533dc9666962a1371d3dd633f6defe7c11de372db6941f9854dbe35eca9&quot;,<br>    &quot;size&quot;: 167<br>  },<br>  &quot;layers&quot;: [<br>    {<br>      &quot;mediaType&quot;: &quot;application/vnd.in-toto+json&quot;,<br>      &quot;digest&quot;: &quot;sha256:93646c93355e912fcfe5f7feb944c8b9f7c0cbdbc6421c0b30aeb2d5ac53ecce&quot;,<br>      &quot;size&quot;: 1369,<br>      &quot;annotations&quot;: {<br>        &quot;in-toto.io/predicate-type&quot;: &quot;https://slsa.dev/provenance/v0.2&quot;<br>      }<br>    }<br>  ]<br>}</pre><p>“When we inspected the ‘unknown’ artifact attached to the Docker image, we found a <strong>SLSA Provenance Attestation</strong>.</p><p>Think of this as a <strong>digital, tamper-proof receipt</strong> for the container image. It is not code that runs; it is a signed document that proves:</p><ol><li><strong>Where</strong> the image was built (e.g., Google Cloud Build).</li><li><strong>When</strong> it was built.</li><li><strong>What</strong> source code (Git commit hash) was used to build it.</li></ol><p>By attaching this receipt directly to the image in the registry (using the OCI Image Index), Google allows security tools to automatically verify the image’s origin before allowing it to run in production.”. If you want to see the actual “receipt” (the build details, repo source, and builder ID), you can inspect the blob in the layers section:</p><pre>LAYER_DIGEST=&quot;sha256:93646c93355e912fcfe5f7feb944c8b9f7c0cbdbc6421c0b30aeb2d5ac53ecce&quot;<br>curl -s -H &quot;Authorization: Bearer $(gcloud auth print-access-token)&quot;      &quot;https://us-docker.pkg.dev/v2/colab-images/public/runtime/blobs/$LAYER_DIGEST&quot; | python -m json.to<br>ol<br>{<br>    &quot;_type&quot;: &quot;https://in-toto.io/Statement/v0.1&quot;,<br>    &quot;predicateType&quot;: &quot;https://slsa.dev/provenance/v0.2&quot;,<br>    &quot;subject&quot;: [<br>        {<br>            &quot;name&quot;: &quot;pkg:docker/us-docker.pkg.dev/colab-images/private/public-runtime@release-colab-external_20251217-060051_RC00?platform=linux%2Famd64&quot;,<br>            &quot;digest&quot;: {<br>                &quot;sha256&quot;: &quot;d954b05cd315ef5db8a6aac71487f28cce49df9a27a8c1bdff3dea94638edc24&quot;<br>            }<br>        },<br>        {<br>            &quot;name&quot;: &quot;pkg:docker/us-docker.pkg.dev/colab-images/private/public-runtime@staging?platform=linux%2Famd64&quot;,<br>            &quot;digest&quot;: {<br>                &quot;sha256&quot;: &quot;d954b05cd315ef5db8a6aac71487f28cce49df9a27a8c1bdff3dea94638edc24&quot;<br>            }<br>        }<br>    ],<br>    &quot;predicate&quot;: {<br>        &quot;builder&quot;: {<br>            &quot;id&quot;: &quot;&quot;<br>        },<br>        &quot;buildType&quot;: &quot;https://mobyproject.org/buildkit@v1&quot;,<br>        &quot;materials&quot;: [<br>            {<br>                &quot;uri&quot;: &quot;pkg:docker/us-docker.pkg.dev/colab-images/private/base-runtime@release-colab-external_20251217-060051_RC00?platform=linux%2Famd64&quot;,<br>                &quot;digest&quot;: {<br>                    &quot;sha256&quot;: &quot;bde98c047e4fd7b39325a5abb632e68b0a8d90dab5f11ca30ac3d65d8a9da407&quot;<br>                }<br>            }<br>        ],<br>        &quot;invocation&quot;: {<br>            &quot;configSource&quot;: {<br>                &quot;entryPoint&quot;: &quot;public-image.Dockerfile&quot;<br>            },<br>            &quot;parameters&quot;: {<br>                &quot;frontend&quot;: &quot;dockerfile.v0&quot;,<br>                &quot;locals&quot;: [<br>                    {<br>                        &quot;name&quot;: &quot;context&quot;<br>                    },<br>                    {<br>                        &quot;name&quot;: &quot;dockerfile&quot;<br>                    }<br>                ]<br>            },<br>            &quot;environment&quot;: {<br>                &quot;platform&quot;: &quot;linux/amd64&quot;<br>            }<br>        },<br>        &quot;metadata&quot;: {<br>            &quot;buildInvocationID&quot;: &quot;2bhz6smjgkfn76jkv05ayr9j5&quot;,<br>            &quot;buildStartedOn&quot;: &quot;2025-12-17T14:42:17.943253663Z&quot;,<br>            &quot;buildFinishedOn&quot;: &quot;2025-12-17T14:45:18.025930694Z&quot;,<br>            &quot;completeness&quot;: {<br>                &quot;parameters&quot;: false,<br>                &quot;environment&quot;: true,<br>                &quot;materials&quot;: false<br>            },<br>            &quot;reproducible&quot;: false,<br>            &quot;https://mobyproject.org/buildkit@v1#metadata&quot;: {}<br>        }<br>    }<br>}</pre><p>Inspecting the runnable image reveals it is an <strong>Ubuntu 22.04</strong> environment heavily optimized for NVIDIA GPUs (CUDA 12.5). Unlike typical microservices, this is a ‘fat’ image with over 60 layers. This architectural choice trades download size for speed: by pre-baking gigabytes of data science libraries into the layers, the runtime allows users to start training models seconds after booting, rather than waiting for dependencies to install.”</p><pre>docker inspect $DOCKER_IMAGE<br>[<br>    {<br>        &quot;Id&quot;: &quot;sha256:d5458217b23549186bb7d0b5e67f4eef0bd82ca12f84c3a341e7a46aa148b02a&quot;,<br>        &quot;RepoTags&quot;: [<br>            &quot;us-docker.pkg.dev/colab-images/public/runtime:latest&quot;<br>        ],<br>        &quot;RepoDigests&quot;: [<br>            &quot;us-docker.pkg.dev/colab-images/public/runtime@sha256:53b9c1a98f563d1dc4eab3f1b54ff43c8a82531f893e17d8818aa7671b36c6fd&quot;<br>        ],<br>        &quot;Parent&quot;: &quot;&quot;,<br>        &quot;Comment&quot;: &quot;&quot;,<br>        &quot;Created&quot;: &quot;2025-10-24T14:11:20.743187819Z&quot;,<br>        &quot;DockerVersion&quot;: &quot;20.10.24&quot;,<br>        &quot;Author&quot;: &quot;&quot;,<br>        &quot;Config&quot;: {<br>            &quot;Hostname&quot;: &quot;&quot;,<br>            &quot;Domainname&quot;: &quot;&quot;,<br>            &quot;User&quot;: &quot;&quot;,<br>            &quot;AttachStdin&quot;: false,<br>            &quot;AttachStdout&quot;: false,<br>            &quot;AttachStderr&quot;: false,<br>            &quot;ExposedPorts&quot;: {<br>                &quot;8080/tcp&quot;: {}<br>            },<br>            &quot;Tty&quot;: false,<br>            &quot;OpenStdin&quot;: false,<br>            &quot;StdinOnce&quot;: false,<br>            &quot;Env&quot;: [<br>                &quot;PATH=/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin&quot;,<br>                &quot;NVARCH=x86_64&quot;,<br>                &quot;NVIDIA_REQUIRE_CUDA=cuda&gt;=12.5 brand=unknown,driver&gt;=470,driver&lt;471 brand=grid,driver&gt;=470,driver&lt;471 brand=tesla,driver&gt;=470,driver&lt;471 brand=nvidia,driver&gt;=470,driver&lt;471 brand=quadro,driver&gt;=470,driver&lt;471 brand=quadrortx,driver&gt;=470,driver&lt;471 brand=nvidiartx,driver&gt;=470,driver&lt;471 brand=vapps,driver&gt;=470,driver&lt;471 brand=vpc,driver&gt;=470,driver&lt;471 brand=vcs,driver&gt;=470,driver&lt;471 brand=vws,driver&gt;=470,driver&lt;471 brand=cloudgaming,driver&gt;=470,driver&lt;471 brand=unknown,driver&gt;=535,driver&lt;536 brand=grid,driver&gt;=535,driver&lt;536 brand=tesla,driver&gt;=535,driver&lt;536 brand=nvidia,driver&gt;=535,driver&lt;536 brand=quadro,driver&gt;=535,driver&lt;536 brand=quadrortx,driver&gt;=535,driver&lt;536 brand=nvidiartx,driver&gt;=535,driver&lt;536 brand=vapps,driver&gt;=535,driver&lt;536 brand=vpc,driver&gt;=535,driver&lt;536 brand=vcs,driver&gt;=535,driver&lt;536 brand=vws,driver&gt;=535,driver&lt;536 brand=cloudgaming,driver&gt;=535,driver&lt;536 brand=unknown,driver&gt;=550,driver&lt;551 brand=grid,driver&gt;=550,driver&lt;551 brand=tesla,driver&gt;=550,driver&lt;551 brand=nvidia,driver&gt;=550,driver&lt;551 brand=quadro,driver&gt;=550,driver&lt;551 brand=quadrortx,driver&gt;=550,driver&lt;551 brand=nvidiartx,driver&gt;=550,driver&lt;551 brand=vapps,driver&gt;=550,driver&lt;551 brand=vpc,driver&gt;=550,driver&lt;551 brand=vcs,driver&gt;=550,driver&lt;551 brand=vws,driver&gt;=550,driver&lt;551 brand=cloudgaming,driver&gt;=550,driver&lt;551&quot;,<br>                &quot;NV_CUDA_CUDART_VERSION=12.5.82-1&quot;,<br>                &quot;CUDA_VERSION=12.5.1&quot;,<br>                &quot;LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64&quot;,<br>                &quot;NVIDIA_VISIBLE_DEVICES=all&quot;,<br>                &quot;NVIDIA_DRIVER_CAPABILITIES=compute,utility&quot;,<br>                &quot;NV_CUDA_LIB_VERSION=12.5.1-1&quot;,<br>                &quot;NV_NVTX_VERSION=12.5.82-1&quot;,<br>                &quot;NV_LIBNPP_VERSION=12.3.0.159-1&quot;,<br>                &quot;NV_LIBNPP_PACKAGE=libnpp-12-5=12.3.0.159-1&quot;,<br>                &quot;NV_LIBCUSPARSE_VERSION=12.5.1.3-1&quot;,<br>                &quot;NV_LIBCUBLAS_PACKAGE_NAME=libcublas-12-5&quot;,<br>                &quot;NV_LIBCUBLAS_VERSION=12.5.3.2-1&quot;,<br>                &quot;NV_LIBCUBLAS_PACKAGE=libcublas-12-5=12.5.3.2-1&quot;,<br>                &quot;NV_LIBNCCL_PACKAGE_NAME=libnccl2&quot;,<br>                &quot;NV_LIBNCCL_PACKAGE_VERSION=2.22.3-1&quot;,<br>                &quot;NCCL_VERSION=2.22.3-1&quot;,<br>                &quot;NV_LIBNCCL_PACKAGE=libnccl2=2.22.3-1+cuda12.5&quot;,<br>                &quot;NVIDIA_PRODUCT_NAME=CUDA&quot;,<br>                &quot;NV_CUDA_CUDART_DEV_VERSION=12.5.82-1&quot;,<br>                &quot;NV_NVML_DEV_VERSION=12.5.82-1&quot;,<br>                &quot;NV_LIBCUSPARSE_DEV_VERSION=12.5.1.3-1&quot;,<br>                &quot;NV_LIBNPP_DEV_VERSION=12.3.0.159-1&quot;,<br>                &quot;NV_LIBNPP_DEV_PACKAGE=libnpp-dev-12-5=12.3.0.159-1&quot;,<br>                &quot;NV_LIBCUBLAS_DEV_VERSION=12.5.3.2-1&quot;,<br>                &quot;NV_LIBCUBLAS_DEV_PACKAGE_NAME=libcublas-dev-12-5&quot;,<br>                &quot;NV_LIBCUBLAS_DEV_PACKAGE=libcublas-dev-12-5=12.5.3.2-1&quot;,<br>                &quot;NV_CUDA_NSIGHT_COMPUTE_VERSION=12.5.1-1&quot;,<br>                &quot;NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE=cuda-nsight-compute-12-5=12.5.1-1&quot;,<br>                &quot;NV_NVPROF_VERSION=12.5.82-1&quot;,<br>                &quot;NV_NVPROF_DEV_PACKAGE=cuda-nvprof-12-5=12.5.82-1&quot;,<br>                &quot;NV_LIBNCCL_DEV_PACKAGE_NAME=libnccl-dev&quot;,<br>                &quot;NV_LIBNCCL_DEV_PACKAGE_VERSION=2.22.3-1&quot;,<br>                &quot;NV_LIBNCCL_DEV_PACKAGE=libnccl-dev=2.22.3-1+cuda12.5&quot;,<br>                &quot;LIBRARY_PATH=/usr/local/cuda/lib64/stubs&quot;,<br>                &quot;NV_CUDNN_VERSION=9.2.1.18-1&quot;,<br>                &quot;NV_CUDNN_PACKAGE_NAME=libcudnn9-cuda-12&quot;,<br>                &quot;NV_CUDNN_PACKAGE=libcudnn9-cuda-12=9.2.1.18-1&quot;,<br>                &quot;NV_CUDNN_PACKAGE_DEV=libcudnn9-dev-cuda-12=9.2.1.18-1&quot;,<br>                &quot;PYTHONPATH=/env/python&quot;,<br>                &quot;TF_FORCE_GPU_ALLOW_GROWTH=true&quot;,<br>                &quot;CLOUDSDK_CONFIG=/content/.config&quot;,<br>                &quot;CLOUDSDK_PYTHON=python3&quot;,<br>                &quot;DEBIAN_FRONTEND=noninteractive&quot;,<br>                &quot;LANG=en_US.UTF-8&quot;,<br>                &quot;LANGUAGE=en_US&quot;,<br>                &quot;LC_ALL=en_US.UTF-8&quot;,<br>                &quot;PYTHONUTF8=1&quot;,<br>                &quot;LAST_FORCED_REBUILD=20250623&quot;,<br>                &quot;COLAB_JUPYTER_IP=127.0.0.1&quot;,<br>                &quot;COLAB_DEBUG_ADAPTER_MUX_PATH=/usr/local/bin/dap_multiplexer&quot;,<br>                &quot;COLAB_LANGUAGE_SERVER_PROXY=/usr/colab/bin/language_service&quot;,<br>                &quot;COLAB_WARMUP_DEFAULTS=1&quot;,<br>                &quot;TCLLIBPATH=/usr/share/tcltk/tcllib1.20&quot;,<br>                &quot;UV_BUILD_CONSTRAINT=&quot;,<br>                &quot;UV_CONSTRAINT=&quot;,<br>                &quot;UV_INSTALL_DIR=/usr/local/bin&quot;,<br>                &quot;UV_SYSTEM_PYTHON=true&quot;,<br>                &quot;KMP_LISTEN_PORT=6000&quot;,<br>                &quot;COLAB_KERNEL_MANAGER_PROXY_PORT=6000&quot;,<br>                &quot;KMP_TARGET_PORT=9000&quot;,<br>                &quot;COLAB_FILE_HANDLER_ADDR=localhost:3453&quot;,<br>                &quot;VM_GCE_METADATA_HOST=169.254.169.254&quot;,<br>                &quot;ENV=/root/.bashrc&quot;,<br>                &quot;SHELL=/bin/bash&quot;,<br>                &quot;COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00&quot;,<br>                &quot;COLAB_TPU_1VM=&quot;,<br>                &quot;COLAB_JUPYTER_ALLOW_ORIGIN_PAT=https://colab\\.(sandbox|research)\\.google\\.com&quot;,<br>                &quot;COLAB_HUMAN_READABLE_NODE_LOGS=1&quot;<br>            ],<br>            &quot;Cmd&quot;: null,<br>            &quot;Image&quot;: &quot;sha256:bc1532477588f8bece50515c5fc9d8e499338b84923945959fb29184a888daf2&quot;,<br>            &quot;Volumes&quot;: null,<br>            &quot;WorkingDir&quot;: &quot;&quot;,<br>            &quot;Entrypoint&quot;: [<br>                &quot;/datalab/run.sh&quot;<br>            ],<br>            &quot;OnBuild&quot;: null,<br>            &quot;Labels&quot;: {<br>                &quot;com.nvidia.cudnn.version&quot;: &quot;9.2.1.18-1&quot;,<br>                &quot;maintainer&quot;: &quot;Google Colaboratory Team &lt;colaboratory-team@google.com&gt;&quot;,<br>                &quot;org.opencontainers.image.ref.name&quot;: &quot;ubuntu&quot;,<br>                &quot;org.opencontainers.image.version&quot;: &quot;22.04&quot;<br>            },<br>            &quot;Shell&quot;: [<br>                &quot;/bin/bash&quot;,<br>                &quot;-o&quot;,<br>                &quot;pipefail&quot;,<br>                &quot;-c&quot;<br>            ]<br>        },<br>        &quot;Architecture&quot;: &quot;amd64&quot;,<br>        &quot;Os&quot;: &quot;linux&quot;,<br>        &quot;Size&quot;: 41045136128,<br>        &quot;GraphDriver&quot;: {<br>            &quot;Data&quot;: {<br>                &quot;LowerDir&quot;: &quot;/var/lib/docker/overlay2/50143af3eb6b3901afb5e990f12a930a1199b312f9a58fa9eef152ed1ee0051d/diff:/var/lib/docker/overlay2/29048d08e14128cd2c3d1df7e79da94c980ed9f271599bf550334d47719d64fc/diff:/var/lib/docker/overlay2/d65a5b55f9d12a621ebaf574e35e91dc89cd09ab8e95ddce866a1ef1ff09c756/diff:/var/lib/docker/overlay2/d240cba4a175d90bfa09f2046e9b61a91b7e93c6b20659ba61c6d38a15177c16/diff:/var/lib/docker/overlay2/3e0c7e8bb2ac66a9cdce9578f48fdb2321520cebcebebed2816c730ba2eae3d8/diff:/var/lib/docker/overlay2/ac502588bd77aadee70544c044fee4a01c9b14d613e699f99c12a35c73255b80/diff:/var/lib/docker/overlay2/f99246d6897594b3bd61be3ddb1bf1988174561ad2c36bfef85ee4a5e733d58a/diff:/var/lib/docker/overlay2/b3beb6128cda61259defeb13d8c0e4cdd57425636e0ffbdd1651770b09e2ae13/diff:/var/lib/docker/overlay2/90c55f10385c33ac193d2d15890378ee5acf7a2c98cceb4140a36fbbb84be484/diff:/var/lib/docker/overlay2/c1b33a768c602682239ca8f5049df33708eec5bf1d93ef084b7d4f93b87dc734/diff:/var/lib/docker/overlay2/a903633614cc2b7bd6d544d6a6135b546c0460dee418f889838c54ec69607a70/diff:/var/lib/docker/overlay2/7ad0e8e9ffdd8ddaed3862362441471aaf069d06a59cf52e9465b74934e2ddf3/diff:/var/lib/docker/overlay2/e3bde1f23dc61bdb02ae4c47cf986f625f090ff82d595d4a6821ab8619676e9a/diff:/var/lib/docker/overlay2/b471212efb1b0faa7a84f7356270646660e0201882bbaba8440a28ad5896cf49/diff:/var/lib/docker/overlay2/16f36e1113634bb9e88a334622cf05b34347bb64631a9d488b28a9d6951efc01/diff:/var/lib/docker/overlay2/ca0a8e66388ffe8261edf7768abf440e7748d60e97e8df8ffd3bc75d6c7b3327/diff:/var/lib/docker/overlay2/eedbad0a5761a8388c5927f4e6aa580b73c8fc6493821584c894d81c1549f440/diff:/var/lib/docker/overlay2/bc5ded093a5cd6e44c6242dcd9f415ee8719bfd4428780c648b575e169e3176f/diff:/var/lib/docker/overlay2/c44e543209ecf7dffa54f200d4d34ca2455b844d1ce2d7ed2a9dd2ce0f49c2e4/diff:/var/lib/docker/overlay2/33131410c62cece58f3f06e7e22ff15b8644279f3dc718add5e135682a19478a/diff:/var/lib/docker/overlay2/b431d1e048891ea61d809e3f9097e2278641c730d1dc27a0903af9e19b524eff/diff:/var/lib/docker/overlay2/7efca97d820c4e09bc399344ecfaa3413e982a83391f2cc2599631c37b08aa79/diff:/var/lib/docker/overlay2/38c57b7866c2be3f3872542d122a677df5badf63915036da2fa762f3de950128/diff:/var/lib/docker/overlay2/4a1827eee6c582d92ab3e73f1f1ae80a044736bd972021065eb943a23752052d/diff:/var/lib/docker/overlay2/10854d88a7a9a9116f85dee92b0b11664a8ef1ec5dcdb74cbfdfef991859560f/diff:/var/lib/docker/overlay2/0ca10bffb45d8aec05802848fb1dde61e2812acd69422e2b8f685342823762d3/diff:/var/lib/docker/overlay2/8ec560583309f1a618f1eb2c786cf38caad92eb9feb4fb865bc3fc797ed0f50e/diff:/var/lib/docker/overlay2/71b888aeb067c03ef910f2fe1cf98735b0b8ba03777681e806ed30cdb8d54554/diff:/var/lib/docker/overlay2/b8035c9e683749440f038f49cbdcf46dd5de2873b2484d3021c460e808ea0836/diff:/var/lib/docker/overlay2/98ee8667be22a23edaa31c6a17049b5e7354ce34fd8d12e53f86c5e1f9bf28bf/diff:/var/lib/docker/overlay2/5376b20d9ec992ee336b06c9356654dae8ae50b1d330f1082d315f3b8e8bed4d/diff:/var/lib/docker/overlay2/c5637244899ef86d1b91655a090c8faa3486a33f40e26ee6ea4f510da0beb311/diff:/var/lib/docker/overlay2/9ba3e7c6d8ba9d09b7300612d2de4d6b526b5589b5698be02b938eb38d909c0b/diff:/var/lib/docker/overlay2/df942cce1fa03d57fc12460d19f59e1d71fb5c6d7e91b808027f65b9aad6dc1a/diff:/var/lib/docker/overlay2/4ae2f7575ae1a7df51f75eabffc3a2b92bca3a352fbaef8b31ccc42bae0ade11/diff:/var/lib/docker/overlay2/e6a6af12d09868dbc97201314341c864711354ea49752b457ce86545a760453c/diff:/var/lib/docker/overlay2/701b2cdef3fd972636871b8a9872e3bba014b111918b3bd535a887a8a2ee079c/diff:/var/lib/docker/overlay2/440fb4d50c760ec0dac1a9d1eb3bf2317aafa6f83daed06f8e251b8f8a5c6e83/diff:/var/lib/docker/overlay2/20109eabbe2be55b3e0833c674e705d3dfa0d4f97737ecb74077f11a67a79508/diff:/var/lib/docker/overlay2/4264cf569e4101b8142d323ef55e1a4846081096cd0181b5fce2b3e8d05b4ef9/diff:/var/lib/docker/overlay2/b523c2c980147cfbe1cf8303e35be7a90fd45a8f4050de988926703c77ba3e74/diff:/var/lib/docker/overlay2/c25b759a4dbe0e21f3eec29631aa1af59ce121b29a3b482cd77dc9d623c27c84/diff:/var/lib/docker/overlay2/e82c21fa732f432857a02180057c78df9a0b3d65fc4c85a18d66d21447fef991/diff:/var/lib/docker/overlay2/373771e39758ac1244fa00319a2286f741b3ae6d90a102dfcab093185ef06f93/diff:/var/lib/docker/overlay2/8b3c49056cc46285693e989fbb137184f3a0122c3b48768000b61f7e36474c73/diff:/var/lib/docker/overlay2/db8d787efacb0cc66603e61ed086fd6bd715c1e479fe2bfb81bc080f11763748/diff:/var/lib/docker/overlay2/105af54dc4b24834eec5377d5186cd61826889b68cf74c76942178e3dc0d2815/diff:/var/lib/docker/overlay2/35ef4bf9be99de2eadb915844b680f7bd938f6a7269c8c21fee721395ab2779a/diff:/var/lib/docker/overlay2/11da47f03b8323d2b3bb63ac17fc5a7fcfdaf0fafebb26c5c786f900854f374a/diff:/var/lib/docker/overlay2/35f04ebae6e7de070aa3242fb673b7b781b7455765e30299c8ef4e535843b927/diff:/var/lib/docker/overlay2/7cdc8f72b68a222e9e6a00c6c7af0d2e83d1463e591d88aa925a7d196ae4b290/diff:/var/lib/docker/overlay2/4e9f1a06e3e1b897874a9c4416f14b8f7dc1a091ba548553fb0ff7b8d7efbf02/diff:/var/lib/docker/overlay2/9b963bd29c9ac65c7270b7b2ecc04105421516d6af24dfb201f668d41b91f80a/diff:/var/lib/docker/overlay2/93ecbf0b3f58b9deb748c2b09be7a46c3cb28969f6e4b0d6ecc7cda4c8876c54/diff:/var/lib/docker/overlay2/4a82e1544d4cf0e3a85adc652e92f3b91d306b4caf5a7b4f93310f45abd56957/diff:/var/lib/docker/overlay2/c139193edf9f0c99db1dd8a5806040939cc47fb98142a76c195f73bba987c01e/diff:/var/lib/docker/overlay2/95c253a72d4ec01a9d87a8a00fd0f9155fa8d2d30099f4495b61abf1db6453c7/diff:/var/lib/docker/overlay2/2b04239c3402988d10bfeb227e229f1357176c5980228be0861418d8c72cf007/diff:/var/lib/docker/overlay2/9ba1ac5e083d1d34cf5c85b4700dd7f4bc36af7ccc71ed8e12f03d11ceacbd62/diff:/var/lib/docker/overlay2/6b1ee7711b7964b53ffeff9d87a14f3fb6e232c897872ce00a04f38369fbdae8/diff:/var/lib/docker/overlay2/02c389eed2a2e3ce53a7a72a4dc8870d112fb3cfe7d5a6e440239bc39f73dc06/diff&quot;,<br>                &quot;MergedDir&quot;: &quot;/var/lib/docker/overlay2/b25799bdae7a1d3ee1c8fc36146ef64a85d87fcf07ef1223a4792628d0af98bb/merged&quot;,<br>                &quot;UpperDir&quot;: &quot;/var/lib/docker/overlay2/b25799bdae7a1d3ee1c8fc36146ef64a85d87fcf07ef1223a4792628d0af98bb/diff&quot;,<br>                &quot;WorkDir&quot;: &quot;/var/lib/docker/overlay2/b25799bdae7a1d3ee1c8fc36146ef64a85d87fcf07ef1223a4792628d0af98bb/work&quot;<br>            },<br>            &quot;Name&quot;: &quot;overlay2&quot;<br>        },<br>        &quot;RootFS&quot;: {<br>            &quot;Type&quot;: &quot;layers&quot;,<br>            &quot;Layers&quot;: [<br>                &quot;sha256:931b7ff0cb6f494b27d31a4cbec3efe62ac54676add9c7469560302f1541ecaf&quot;,<br>                &quot;sha256:db406658b54593a161c3de8c38f321420ed569ae94d7e16b1a6b7ce4d46bfba0&quot;,<br>                &quot;sha256:c35c14c72813fbfa41e48dee6d70ccf43984e3943eea24e6e7bc7be423056f42&quot;,<br>                &quot;sha256:888b66dc5a210906050f1550c3caa0b69b9b48d0adc022431f50d8fcf34a6dda&quot;,<br>                &quot;sha256:563e7c97ed4d3ed6e70bb03b82738fe7131ca91147b502659423b46f3fa7dfab&quot;,<br>                &quot;sha256:1bdf3319743984171af50d4f825439769df5a9aac393cc716bff7793ecc4fe89&quot;,<br>                &quot;sha256:31633e4421ebd1030235b44510c716a76ea2edad37fd07a9ce646af331becf8c&quot;,<br>                &quot;sha256:a6ec53dbbbce285cd5df125037e2426ade00cd4cc9aaab88f67a54848e5d56ea&quot;,<br>                &quot;sha256:22f1a6014bd5e4c2ae5c127142860e65ea08406b74c0a4c468c384d396aa30cb&quot;,<br>                &quot;sha256:55234d4baee80a9a386e05bf6f2eab6acb91a2f13b1502ea702537b00303233b&quot;,<br>                &quot;sha256:d0059c7387112aa7b80113a77956702d498be28dc0cac94ccbe1194da3a2f494&quot;,<br>                &quot;sha256:28e4bd83ae2754e3ff412bef2ca1fff2a580748074ee5c261bb28abc4f55f6c8&quot;,<br>                &quot;sha256:4dc90ba6ab7a3e335066c918d640af38e5e1e4a583c8f1bfe6698a14cffce978&quot;,<br>                &quot;sha256:3cde843408435ec7690f9a8076ecce15b919cbee4cacf6fc7b0365ff9626c6ed&quot;,<br>                &quot;sha256:208731d38664f139be3c632953e87d8c426c6ba2b84bcecdecb55cd1d455376d&quot;,<br>                &quot;sha256:379ab108fa86ba4af88261e1fa4e713b3b7ea1bd61c629bac67360026f6655c5&quot;,<br>                &quot;sha256:ce0bb5285a19e9d595607d4547661984f034f7d700bb246a0404491495dad9c9&quot;,<br>                &quot;sha256:c64cde5c8a6945933aafdce17c88a28f88f6845009c81d2ec075050f9182a9d4&quot;,<br>                &quot;sha256:5fd664040aca6c6665fef21fc1a1242d0888838af268b79426e8dfb3855f088a&quot;,<br>                &quot;sha256:4924e38231fc6d412b7221573e1fa2d0fd72e72deebd96c7ccb761b7da2cc8a8&quot;,<br>                &quot;sha256:7fa654400d7ec0ab172f0a4a63879ffc18228b8170d30328be8be89672277332&quot;,<br>                &quot;sha256:ffa32dbee57a89aa4283d4d5f30db582c434f532ebf22e6cb71555b71ceef265&quot;,<br>                &quot;sha256:a8d92e6daccc9480d0d9211b30e97a322218995114c2f7f6551041171ac6aa77&quot;,<br>                &quot;sha256:1dbd9b7e5fdda476816400b61ff7e89b23e4f08b325cc1e115d89a9bac839220&quot;,<br>                &quot;sha256:c15d66af5ecc888961a383237e3969298e8ee4e029101be54ed2f28e45382261&quot;,<br>                &quot;sha256:7f31386006660fde268705a6b11967364d9dad5725d782e1cb58a80456ccfb42&quot;,<br>                &quot;sha256:731f5501ca58f6a0f9d62896e1e504f3358913e3e58f9ff8d96cc8e619b3f119&quot;,<br>                &quot;sha256:a21b50e553cec8f091b86fbac8a2449fcde61ef60be56c8c26a837488d015731&quot;,<br>                &quot;sha256:2e77443fe3e15d9d64a7105160e652ceb6a60f2962b20c28dea91e215532c6ac&quot;,<br>                &quot;sha256:4d15737a6435c13328a2218d99d5e7fe74d06471490f90831fee9b546f3e0955&quot;,<br>                &quot;sha256:af638a2a8290a8718c8b6e84ad949c7dc1be0fc504bd01741c6471c73df5c791&quot;,<br>                &quot;sha256:b6feceb87de8f8c40ad8f72d677ba6d9ff25f208305bf5afa06ab5dc92c03d5f&quot;,<br>                &quot;sha256:c4f457784b49a96278d80f6d3b59057470cea2eab33367649418d1127b56f728&quot;,<br>                &quot;sha256:c230a2be23e4118c85eb86cf9370bf4982e051d0a05dbb3d28be9ef801ac6dda&quot;,<br>                &quot;sha256:de3703b62e9e9549d5a1a4ef3fb125d257c390aacabe406a17f191dfcd8cc512&quot;,<br>                &quot;sha256:1337f63226f634fa86c9dac03f21f75552ba096399e76ee8d80bcb2216cbb7e2&quot;,<br>                &quot;sha256:845a1ee022e56c55a4ec4f94b5bce82e974ffc2cfa0bad7b046e7fa10d8ca8f0&quot;,<br>                &quot;sha256:7f7df2718427941de1f6bb8cf7f8d96b37ebe668a42241502a755eefc2606eb0&quot;,<br>                &quot;sha256:b3b2cd19a67bd706cad52272557b1c34e28ee7625aebe9e6854107789bd70590&quot;,<br>                &quot;sha256:32336cde1d5adc41f09d77f21d6c1f9f2a008734c63d5b33aeadbaebd238b55f&quot;,<br>                &quot;sha256:d15d5df36a16147777c90c3e84dc3fc492f213b2d11c9278a97bb724d7eb97d6&quot;,<br>                &quot;sha256:b76b797623429d19b92beb93e2b5ab2d8df499937414927082f2c1ab48826c4a&quot;,<br>                &quot;sha256:8db1f8803ae6467d9c93c478264fa4641f14aa44f68458041e5c480c2382a316&quot;,<br>                &quot;sha256:b4e5b507afead7b2cd45ec66dbf653cceffe6da8ff2663879d46b561e504bbf1&quot;,<br>                &quot;sha256:56663e6473d3d0b0e9ba9d831f864cc20f2141b4e58f7637bc1330cd3880d684&quot;,<br>                &quot;sha256:068d81b73ae54936d3d19e7fb78376db81d6fa8fef2de0559a2bae6add81b9e0&quot;,<br>                &quot;sha256:5a05938580404d3cff28c54af75469a9f4d52f7595488c7c4aa1d7d9b0a22ce6&quot;,<br>                &quot;sha256:d5e899031dccede8cc815c3b60f6d417aadbe80d2fe971eaacf5812d95a5a957&quot;,<br>                &quot;sha256:dd2ea2973c2c20014c4e8574312436d78834790605c67c6f566ded312e2033d9&quot;,<br>                &quot;sha256:39e1272faa4613408b3a5193c7256bc4bdc728bab256c98a8c19782e1721d757&quot;,<br>                &quot;sha256:54c6cab3c1b04371cae357a3e775143929a42a0ee83bbf6c2d2601ddcfc7eb36&quot;,<br>                &quot;sha256:2cb24a4965f7fd91569353dc200c64faadc7d4bb112c5c92c89a0f284bec7b9a&quot;,<br>                &quot;sha256:c9d749c7b9e70905e0f3df1385ccd7a957b292c324ef2ee29333d96d62e58ec0&quot;,<br>                &quot;sha256:abc7716586408a8a39078a0fb498b04cf4d4b858fec6ec04556fc2e59098bfc4&quot;,<br>                &quot;sha256:055ad36cbc628cae0ddfe8e278f260424265ce3b6c3960227559d7d62bb6a268&quot;,<br>                &quot;sha256:d28ae9da1731e3b12e717382ed338edbceb8e5bddc7c8c37021189c5f2c02e51&quot;,<br>                &quot;sha256:1eed00f06fabbea913284a0a0d050873b18e648c80e43e3e62686d7e49f847d8&quot;,<br>                &quot;sha256:06c331f4e52aeaee5e427b2bfaded102492302ad9f15cccdc6aa3330df5d13ff&quot;,<br>                &quot;sha256:0d75d682e239dcadac98c1712f596fb81e35ec1ff9ac0a4af6e92438cf07938d&quot;,<br>                &quot;sha256:fab02472979b1c342afe6d2585591b753e91241e4ddeb2ff664a754413ed97b3&quot;,<br>                &quot;sha256:70f56a036c9da06865f3f1cad3fc0ac285ad1578413010b3b07224bb53813a2c&quot;,<br>                &quot;sha256:f86c1788bb90d3fb68827f0ebf16cad50556510762a0255c2d59d9e56fd948b2&quot;<br>            ]<br>        },<br>        &quot;Metadata&quot;: {<br>            &quot;LastTagTime&quot;: &quot;0001-01-01T00:00:00Z&quot;<br>        }<br>    }<br>]</pre><p>When analyzing a bloated container, docker inspect is your X-ray machine. Here is how to read the three most critical sections for optimization, using a 38GB deep-learning image as our case study:</p><p><strong>1. </strong><strong>Size: The &quot;Cold Start&quot; Tax</strong></p><ul><li><strong>What it is:</strong> The cumulative size of the image on disk (in our example: 41045136128 bytes, or ~38.2 GB).</li><li><strong>Why it matters:</strong> This number directly correlates to your cold-start latency. Every gigabyte represents network time required to pull the image and I/O time to unpack it.</li></ul><p><strong>2. </strong><strong>RootFS (Layers): The Build Efficiency Log</strong></p><ul><li><strong>What it is:</strong> A JSON array of SHA256 hashes, where each hash represents a unique layer in the filesystem.</li><li><strong>Why it matters:</strong> This tracks the history of your filesystem changes. A long list (like the 60+ layers in the example) indicates a `Dockerfile` with too many separate instructions.</li></ul><p><strong>3. </strong><strong>GraphDriver (Overlay2): The Runtime Assembly</strong></p><ul><li><strong>What it is:</strong> This section reveals how the <strong>overlay2</strong> driver stacks your image.</li><li><strong>LowerDir:</strong> The stack of read-only image layers (the immutable base).</li><li><strong>UpperDir:</strong> The thin, writable layer where the running container stores changes.</li><li><strong>MergedDir:</strong> The unified view the application actually sees.</li><li><strong>Why it matters:</strong> This exposes the <strong>Copy-on-Write (CoW)</strong> mechanism. If your application frequently modifies files that exist in the huge LowerDir stack, the driver must copy them to the UpperDir first, causing I/O overhead.</li></ul><p>Checking Docker history</p><pre>docker history --format &#39;{{.Size}}\t{{.ID}}\t{{.CreatedAt}}\t{{.CreatedBy}}&#39; $DOCKER_IMAGE --no-trunc | sort -rh<br>20.2GB  &lt;missing&gt;       2025-10-24T13:52:13Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c export REQUIREMENTS_FILE=requirements3.txt &amp;&amp;     if [[ &quot;${COLAB_TPU_1VM}&quot; == &quot;1&quot; ]]; then         export REQUIREMENTS_FILE=requirements3_tpu.txt;      fi &amp;&amp;     uv pip install -U         --no-cache         $(grep &#39;^numpy==&#39; /backend-container/containers/${REQUIREMENTS_FILE}) &amp;&amp;     echo &#39;uv install for pre-required packages done.&#39; &amp;&amp;     uv pip install         --no-cache         --reinstall-package six six==1.16.0 &amp;&amp;     echo &#39;uv install exceptional packages for Ubuntu 22.04 done.&#39; &amp;&amp;     uv pip install -U         --no-cache         -r /backend-container/containers/${REQUIREMENTS_FILE} &amp;&amp;     C_INCLUDE_PATH=/usr/include/gdal CPLUS_INCLUDE_PATH=/usr/include/gdal         uv pip install --no-build-isolation         --no-cache         GDAL==&quot;$(gdal-config --version)&quot; &amp;&amp;     if [[ &quot;${COLAB_TPU_1VM}&quot; != &quot;1&quot; ]]; then         mkdir -p /etc/OpenCL/vendors &amp;&amp;         echo &quot;libnvidia-opencl.so.1&quot; &gt; /etc/OpenCL/vendors/nvidia.icd &amp;&amp;         mkdir /tmp/lightgbm &amp;&amp;         pushd /tmp/lightgbm &amp;&amp;         LIGHTGBM_VERSION=&quot;$(uv pip show lightgbm 2&gt;/dev/null | grep Version: | sed &#39;s/.*Version: //&#39;)&quot; &amp;&amp;         git clone --recursive --quiet --branch=v${LIGHTGBM_VERSION} https://github.com/microsoft/LightGBM &amp;&amp;         pushd LightGBM &amp;&amp;         ./build-python.sh bdist_wheel --gpu             --opencl-library=&quot;/usr/local/cuda/lib64/libOpenCL.so&quot;             --opencl-include-dir=&quot;/usr/local/cuda/include/&quot; &amp;&amp;         uv pip install --no-cache             --system             --no-build-isolation             dist/lightgbm-${LIGHTGBM_VERSION}-py3-none-linux_x86_64.whl &amp;&amp;         popd &amp;&amp;         popd &amp;&amp;         rm -Rf /tmp/lightgbm &amp;&amp;         uv pip install             --no-cache             --no-build-isolation             --reinstall-package tensorflow tensorflow[and-cuda]==2.19.0;         uv pip install             --index-url  https://download.pytorch.org/whl/cu126             --no-cache             --no-build-isolation             --reinstall-package torch torch==2.8.0             --reinstall-package torchaudio torchaudio==2.8.0             --reinstall-package torchvision torchvision==0.23.0;     else         uv pip install             --no-cache             --no-build-isolation             --reinstall-package libtpu libtpu==0.0.17;     fi &amp;&amp;     python3 -m spacy download en_core_web_sm &amp;&amp;     echo &#39;py3 spacy en_core_web_sm download done&#39; &amp;&amp;     rm -rf /root/.cache/{pip,pip-tools,uv} /*whl &amp;&amp;     cp /backend-container/containers/${REQUIREMENTS_FILE} /etc/ &amp;&amp;     cp /backend-container/containers/requirements.core.in /etc/ &amp;&amp;     cp /backend-container/containers/requirements.user.in /etc/ &amp;&amp;     cp /backend-container/containers/requirements.in /etc/<br>5.02GB  &lt;missing&gt;       2025-10-24T13:18:54Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c apt-get install --no-install-recommends -y -qq         apt-utils         bc         build-essential         ca-certificates         clang         clinfo         cmake         coinor-libipopt1v5         coinor-libipopt-dev         curl         dirmngr         dnsutils         ffmpeg         file         fio         fonts-humor-sans         fonts-liberation         fuse         gfortran         gh         gir1.2-gtk-4.0         git         git-lfs         google-perftools         graphviz         intel-mkl         jq         kmod         less         libapt-pkg-dev         libasound2-dev         libatlas-base-dev         libavdevice58         libblocksruntime-dev         libboost-all-dev         libcairo2-dev         libcurl4         libcurl4-openssl-dev         libegl1         libfontconfig1         libfreetype6-dev         libgdal-dev         libgirepository1.0-dev         libgit2-dev         libgl1         libgles2         libglib2.0-0         libglvnd0         libgl1-mesa-glx         libhdf5-dev         libicu-dev         libjack-dev         liblapack-dev         libncurses5-dev         libopenblas-dev         libopencv-dev         libopengl0         libopenmpi-dev         libpng-dev         libprotobuf-java         libpython3-dev         libsm6         libsndfile-dev         libsndfile1         libssl-dev         libturbojpeg         libudunits2-0         libudunits2-dev         libwebpdemux2         libxext6         libxft-dev         libxml2-dev         libxrender1         libxslt1.1         libzmq3-dev         libzmq5         linux-headers-generic         locales         lsof         man         manpages         manpages-dev         manpages-posix         manpages-posix-dev         moreutils         net-tools         nvidia-opencl-dev         openjdk-11-jdk-headless         openssh-client         p7zip-full         pigz         pkgconf         protobuf-compiler         psmisc         python3.12         python3.12-dev         python3.12-tk         r-base         r-base-core         r-base-dev         r-recommended         rename         ripgrep         rsync         socat         sudo         tcllib         tcl-dev         tesseract-ocr         tk-dev         tmux         unrar         unzip         vim         xvfb         zip &amp;&amp;     locale-gen en_US.UTF-8 &amp;&amp;     update-locale LANG=en_US.UTF-8 &amp;&amp;     mkdir -p /tools &amp;&amp;     update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1 &amp;&amp;     update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.12 2 &amp;&amp;     sed -i -e &#39;1,1s/python3/python3.12/&#39; /usr/bin/add-apt-repository<br>4.87GB  &lt;missing&gt;       2024-07-10T23:20:17Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-get update &amp;&amp; apt-get install -y --no-install-recommends     cuda-cudart-dev-12-5=${NV_CUDA_CUDART_DEV_VERSION}     cuda-command-line-tools-12-5=${NV_CUDA_LIB_VERSION}     cuda-minimal-build-12-5=${NV_CUDA_LIB_VERSION}     cuda-libraries-dev-12-5=${NV_CUDA_LIB_VERSION}     cuda-nvml-dev-12-5=${NV_NVML_DEV_VERSION}     ${NV_NVPROF_DEV_PACKAGE}     ${NV_LIBNPP_DEV_PACKAGE}     libcusparse-dev-12-5=${NV_LIBCUSPARSE_DEV_VERSION}     ${NV_LIBCUBLAS_DEV_PACKAGE}     ${NV_LIBNCCL_DEV_PACKAGE}     ${NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE}     &amp;&amp; rm -rf /var/lib/apt/lists/* # buildkit<br>2.05GB  &lt;missing&gt;       2024-07-10T22:50:58Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-get update &amp;&amp; apt-get install -y --no-install-recommends     cuda-libraries-12-5=${NV_CUDA_LIB_VERSION}     ${NV_LIBNPP_PACKAGE}     cuda-nvtx-12-5=${NV_NVTX_VERSION}     libcusparse-12-5=${NV_LIBCUSPARSE_VERSION}     ${NV_LIBCUBLAS_PACKAGE}     ${NV_LIBNCCL_PACKAGE}     &amp;&amp; rm -rf /var/lib/apt/lists/* # buildkit<br>2.02GB  &lt;missing&gt;       2025-10-24T13:27:21Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c julia -e &#39;using Pkg; Pkg.add([&quot;IJulia&quot;, &quot;CSV&quot;, &quot;DataFrames&quot;, &quot;Makie&quot;, &quot;Plots&quot;]); precompile;&#39;<br>1.49GB  &lt;missing&gt;       2025-10-24T13:31:34Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c julia -e &#39;using Pkg; Pkg.add([&quot;Adapt&quot;, &quot;Lux&quot;, &quot;Reactant&quot;]); precompile;&#39; || true<br>1.3GB   &lt;missing&gt;       2025-10-24T13:21:09Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c wget -nv https://julialang-s3.julialang.org/bin/linux/x64/1.11/julia-1.11.5-linux-x86_64.tar.gz -O /tmp/julia.tar.gz &amp;&amp;     tar -xf /tmp/julia.tar.gz -C /usr/local --strip-components 1<br>895MB   &lt;missing&gt;       2025-10-24T13:37:33Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c wget -nv https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.zip &amp;&amp;     unzip -qq google-cloud-sdk.zip -d tools &amp;&amp;     rm google-cloud-sdk.zip &amp;&amp;     tools/google-cloud-sdk/install.sh --usage-reporting=false         --path-update=false --bash-completion=false &amp;&amp;     tools/google-cloud-sdk/bin/gcloud -q components update         gcloud core bq gsutil compute preview alpha beta &amp;&amp;     tools/google-cloud-sdk/bin/gcloud config set component_manager/disable_update_check true &amp;&amp;     tools/google-cloud-sdk/bin/gcloud config set compute/gce_metadata_read_timeout_sec 0 &amp;&amp;     touch -t 204001010000.00 /content/.config/gce &amp;&amp;     rm -rf tools/google-cloud-sdk/.install<br>874MB   &lt;missing&gt;       2024-07-10T23:22:21Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-get update &amp;&amp; apt-get install -y --no-install-recommends     ${NV_CUDNN_PACKAGE}     ${NV_CUDNN_PACKAGE_DEV}     &amp;&amp; apt-mark hold ${NV_CUDNN_PACKAGE_NAME}     &amp;&amp; rm -rf /var/lib/apt/lists/* # buildkit<br>715MB   &lt;missing&gt;       2025-10-24T13:54:40Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c ipython profile create default &amp;&amp;     jupyter server --generate-config &amp;&amp;     python3 -m ipykernel install &amp;&amp;     R -e &#39;IRkernel::installspec(user = FALSE)&#39; &amp;&amp;     julia -e &#39;using IJulia; IJulia.installkernel(&quot;julia&quot;, specname = &quot;julia&quot;, env=Dict(&quot;JULIA_NUM_THREADS&quot;=&gt;&quot;auto&quot;))&#39; &amp;&amp;     mv /root/.local/share/jupyter/kernels/julia-1.11 /usr/local/share/jupyter/kernels/julia &amp;&amp;     sed -i &#39;s/julia 1.11.5/julia/&#39; &#39;/usr/local/share/jupyter/kernels/julia/kernel.json&#39; &amp;&amp;     mkdir -p /tools/node &amp;&amp;     wget -nv https://nodejs.org/dist/v20.19.0/node-v20.19.0-linux-x64.tar.gz -O node.tar.gz &amp;&amp;     tar xzf node.tar.gz -C /tools/node --strip-components=1 &amp;&amp;     rm node.tar.gz &amp;&amp;     npm install -g npm@v10.8.2 &amp;&amp;     mkdir -p /datalab/web &amp;&amp;     cd /backend-container/sources &amp;&amp;     mv * /datalab/web &amp;&amp;     cd /datalab/web &amp;&amp;     /tools/node/bin/npm ci &amp;&amp;     unzip /backend-container/pyright.zip -d pyright &amp;&amp;     npm install -g @google/gemini-cli &amp;&amp;     mkdir /var/colab &amp;&amp;     cd / &amp;&amp; unzip /backend-container/colabtools.zip &amp;&amp; cd /colabtools &amp;&amp;     python setup.py sdist &amp;&amp;     uv pip install         --no-cache         /colabtools/dist/google_colab-1.0.0.tar.gz &amp;&amp;     jupyter server extension enable --py nbclassic &amp;&amp;     jupyter server extension enable --py google.colab &amp;&amp;     jupyter server extension enable --system --py google.colab._serverextension &amp;&amp;     python3 -m pytest &amp;&amp;     python3 /backend-container/containers/update_kernelspecs.py &amp;&amp;     uv pip uninstall --no-cache --system jedi &amp;&amp;     echo &#39;uninstalling problematic jedi done&#39; &amp;&amp;     rm -rf /tmp/* /backend-container /colabtools &amp;&amp;     cd /<br>625MB   &lt;missing&gt;       2025-10-24T13:36:19Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c if [[ &quot;${COLAB_TPU_1VM}&quot; != &quot;1&quot; ]]; then         julia -e &#39;env = &quot;/root/.julia/environments/v1.11&quot;;                   mkpath(env);                   write(&quot;$env/LocalPreferences.toml&quot;, &quot;[CUDA_Runtime_jll]\nversion = \&quot;&#39;${CUDA_VERSION}&#39;\&quot;\nlocal = \&quot;true\&quot;&quot;);                   using Pkg;                   Pkg.add(&quot;CUDA_Runtime_jll&quot;);                   pkg&quot;add CUDA&quot;; using CUDA; CUDA.precompile_runtime()&#39;;     fi<br>317MB   &lt;missing&gt;       2025-10-24T13:20:21Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c apt-get update &amp;&amp;     echo &#39;local({options(repos = c(CRAN = &quot;https://cran.rstudio.com&quot;))})&#39; &gt;&gt;/etc/R/Rprofile.site &amp;&amp;     apt-get install --no-install-recommends -y -qq         r-cran-devtools         r-cran-tidyverse<br>211MB   &lt;missing&gt;       2025-10-24T13:12:48Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c apt-get update &amp;&amp;     rm -f /etc/dpkg/dpkg.cfg.d/excludes &amp;&amp;     apt-get install --no-install-recommends -y -qq         software-properties-common wget gnupg2<br>152MB   &lt;missing&gt;       2024-07-10T22:46:29Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-get update &amp;&amp; apt-get install -y --no-install-recommends     cuda-cudart-12-5=${NV_CUDA_CUDART_VERSION}     cuda-compat-12-5     &amp;&amp; rm -rf /var/lib/apt/lists/* # buildkit<br>77.9MB  &lt;missing&gt;       2024-06-27T20:10:12Z    /bin/sh -c #(nop) ADD file:d5da92199726e42da09a6f75a778befb607fe3f79e4afaf7ef5188329b26b386 in / <br>63.5MB  &lt;missing&gt;       2025-10-24T13:19:06Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c curl -LsSf https://astral.sh/uv/install.sh | sh &amp;&amp;     uv pip install         --no-cache         --requirement /backend-container/containers/requirements.constraints<br>56.8MB  &lt;missing&gt;       2025-10-24T13:37:52Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c chmod +w -R /content/sample_data &amp;&amp;     cd /content/sample_data &amp;&amp;     curl -OL https://dl.google.com/mlcc/mledu-datasets/california_housing_train.csv &amp;&amp;     curl -OL https://dl.google.com/mlcc/mledu-datasets/california_housing_test.csv &amp;&amp;     curl -OL https://dl.google.com/mlcc/mledu-datasets/mnist_train_small.csv &amp;&amp;     curl -OL https://dl.google.com/mlcc/mledu-datasets/mnist_test.csv<br>26.6MB  &lt;missing&gt;       2025-10-24T13:19:44Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c tar -xf python-apt.tar.xz &amp;&amp;     pushd python-apt &amp;&amp;     python3 setup.py build &amp;&amp;     python3 setup.py install &amp;&amp;     popd<br>22.8MB  &lt;missing&gt;       2025-10-24T13:13:20Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c if [[ &quot;${COLAB_TPU_1VM}&quot; != &quot;1&quot; ]]; then         /backend-container/containers/retrying.sh add-apt-repository --yes             &#39;ppa:graphics-drivers/ppa&#39;;     fi &amp;&amp;     /backend-container/containers/retrying.sh add-apt-repository --yes         &#39;ppa:deadsnakes/ppa&#39; &amp;&amp;     wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc |         tee -a /etc/apt/trusted.gpg.d/cran_ubuntu_key.asc &amp;&amp;     /backend-container/containers/retrying.sh add-apt-repository --yes         &quot;deb https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran40/&quot; &amp;&amp;     wget -qO- https://r2u.stat.illinois.edu/ubuntu/dirk_eddelbuettel_pubkey.asc |         tee -a /etc/apt/trusted.gpg.d/r2u_key.asc &amp;&amp;     /backend-container/containers/retrying.sh add-apt-repository --enable-source --yes         &quot;deb [arch=amd64] https://r2u.stat.illinois.edu/ubuntu $(lsb_release -cs) main&quot; &amp;&amp;     /backend-container/containers/retrying.sh add-apt-repository --yes         &#39;ppa:ubuntugis/ppa&#39; &amp;&amp;     out=$(mktemp) &amp;&amp; wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg &amp;&amp;     cat &quot;${out}&quot; | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg &gt; /dev/null &amp;&amp;     chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg &amp;&amp;     mkdir -p -m 755 /etc/apt/sources.list.d &amp;&amp;     echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main&quot; |         tee /etc/apt/sources.list.d/github-cli.list &gt; /dev/null &amp;&amp;     apt-get update<br>15.3MB  &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop) COPY file:485df831b8044b58daeb052e392b59e6e916f125eb1996a1ece6d2a064d3022c in /usr/colab/bin/language_service <br>11MB    &lt;missing&gt;       2025-10-24T13:54:45Z    /bin/bash -o pipefail -c #(nop) COPY file:f63757945088dc9d43194565a0aa4e503f6f1ebecf804a7ebc42fbbfed62de34 in /usr/colab/bin/kernel_manager_proxy <br>10.6MB  &lt;missing&gt;       2024-07-10T22:46:16Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-get update &amp;&amp; apt-get install -y --no-install-recommends     gnupg2 curl ca-certificates &amp;&amp;     curl -fsSLO https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/${NVARCH}/cuda-keyring_1.1-1_all.deb &amp;&amp;     dpkg -i cuda-keyring_1.1-1_all.deb &amp;&amp;     apt-get purge --autoremove -y curl     &amp;&amp; rm -rf /var/lib/apt/lists/* # buildkit<br>4.57MB  &lt;missing&gt;       2025-10-24T13:52:26Z    /bin/bash -o pipefail -c #(nop) COPY file:015df91e83e2a1774fb5f70ac47c706c45b497353955f145a51bb901600c216b in /backend-container/colabtools.zip <br>4.06MB  &lt;missing&gt;       2025-10-24T13:54:43Z    /bin/bash -o pipefail -c #(nop) COPY file:7b7829f883a105cb7d559e3786779ba996ff367be034ebc4504b630575f54e0d in /usr/local/bin/dap_multiplexer <br>2.33MB  &lt;missing&gt;       2025-10-24T13:52:27Z    /bin/bash -o pipefail -c #(nop) COPY file:f078e82601068dfd08be60c63e861d1d2c019a509babd74f4e66781a807c3ab1 in /backend-container/pyright.zip <br>1.99MB  &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop) COPY file:778a79bd2c394874556d5f16bcc884387d073873501cb3ec4de41cee066f2033 in /usr/grte/v5/lib64/locale/locale-archive <br>1.12MB  &lt;missing&gt;       2025-10-24T13:20:46Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c R -e &#39;install.packages(c(&quot;repr&quot;, &quot;IRdisplay&quot;, &quot;IRkernel&quot;), type = &quot;source&quot;)&#39; &amp;&amp;     R -e &#39;stopifnot(length(grep(&quot;agpl&quot;, installed.packages(), ignore.case = TRUE)) == 0)&#39;<br>458kB   &lt;missing&gt;       2025-10-24T13:52:26Z    /bin/bash -o pipefail -c #(nop) COPY dir:aa10accd20deb0021f05ca24f11f6608d901b4786081d520b035fc57ab8714a7 in /backend-container/sources <br>390kB   &lt;missing&gt;       2024-07-10T23:20:17Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-mark hold ${NV_LIBCUBLAS_DEV_PACKAGE_NAME} ${NV_LIBNCCL_DEV_PACKAGE_NAME} # buildkit<br>346kB   &lt;missing&gt;       2025-10-24T13:19:06Z    /bin/bash -o pipefail -c #(nop) COPY file:c93c3fb181b5ffadff510f270f3593aa992de3d8fd9a2fc2b4270b3eeb10963d in python-apt.tar.xz <br>263kB   &lt;missing&gt;       2024-07-10T22:50:58Z    RUN |1 TARGETARCH=amd64 /bin/sh -c apt-mark hold ${NV_LIBCUBLAS_PACKAGE_NAME} ${NV_LIBNCCL_PACKAGE_NAME} # buildkit<br>179kB   &lt;missing&gt;       2025-10-24T13:52:24Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c pushd /usr/local/lib/python3.12/dist-packages/IPython/ &amp;&amp;     patch -p1 &lt; /backend-container/containers/py311_tokenize_backport.patch &amp;&amp;     popd<br>164kB   &lt;missing&gt;       2025-10-24T13:52:26Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c {       echo &#39;/usr/local/lib&#39; ;     } &gt; /etc/ld.so.conf.d/colab.conf &amp;&amp;     ldconfig<br>98.9kB  &lt;missing&gt;       2025-10-24T13:37:52Z    /bin/bash -o pipefail -c #(nop) COPY multi:7a15c3b78e0c65540051dee5ebddc031890b49b14225711e8f14edb27eb5c1f4 in /backend-container/containers/ <br>26.5kB  &lt;missing&gt;       2025-10-24T13:52:23Z    /bin/bash -o pipefail -c #(nop) COPY file:06c7afae6e572613d4643243e1583f34175d3e4d9e8303fa3d369968a63cf9f6 in /usr/local/lib/python3.12/dist-packages/IPython/lib/py311_tokenize.py <br>18kB    &lt;missing&gt;       2025-10-24T13:37:53Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c python3 /backend-container/containers/require_pinned_requirements.py       /backend-container/containers/requirements.core.in &amp;&amp;     export REQUIREMENTS_FILE=requirements3.txt &amp;&amp;     if [[ &quot;${COLAB_TPU_1VM}&quot; == &quot;1&quot; ]]; then         export REQUIREMENTS_FILE=requirements3_tpu.txt;      fi &amp;&amp;     echo &quot;Requirements file: ${REQUIREMENTS_FILE}&quot; &amp;&amp;     if [[ &quot;${COLAB_PIP_COMPILE}&quot; == &quot;1&quot; ]]; then         rm /backend-container/containers/${REQUIREMENTS_FILE} &amp;&amp;         if [[ &quot;${COLAB_TPU_1VM}&quot; == &quot;1&quot; ]]; then             uv pip compile                 --verbose                 --upgrade                 --no-cache                 --find-links=https://storage.googleapis.com/jax-releases/libtpu_releases.html                 --find-links=https://storage.googleapis.com/libtpu-releases/index.html                 --find-links=https://storage.googleapis.com/libtpu-wheels/index.html                 --find-links=https://download.pytorch.org/whl/torch_stable.html                 --emit-find-links                 --no-emit-package pip                 --no-emit-package setuptools                 --output-file /backend-container/containers/${REQUIREMENTS_FILE}                 /backend-container/containers/requirements.core.in                 /backend-container/containers/requirements.user.in                 /backend-container/containers/requirements.tpu.in;         else             uv pip compile                 --verbose                 --upgrade                 --no-cache                 --emit-find-links                 --no-emit-package pip                 --no-emit-package setuptools                 --output-file /backend-container/containers/${REQUIREMENTS_FILE}                 /backend-container/containers/requirements.core.in                 /backend-container/containers/requirements.user.in                 /backend-container/containers/requirements.in;         fi;     fi<br>17.3kB  &lt;missing&gt;       2024-07-10T22:46:29Z    COPY NGC-DL-CONTAINER-LICENSE / # buildkit<br>8.69kB  &lt;missing&gt;       2025-10-24T13:54:43Z    /bin/bash -o pipefail -c #(nop) COPY file:3682207aec072d6574dcad95ab0452f77465b8a9dd4d6cf73b8e747d798420db in /usr/local/bin/ <br>6.16kB  &lt;missing&gt;       2025-10-24T13:54:43Z    /bin/bash -o pipefail -c #(nop) COPY file:3d0156b109771b650783f7d676023b8a0ae245443f3e84eb6b66bc131e2de248 in /usr/local/bin/ <br>3.71kB  &lt;missing&gt;       2025-10-24T13:52:27Z    /bin/bash -o pipefail -c #(nop) COPY file:41ea9712e0d68c47118b36f6871e8998a2fd8f2705903c47ac2ef5cc75e59ca8 in /datalab/ <br>3.06kB  &lt;missing&gt;       2024-07-10T22:50:58Z    COPY entrypoint.d/ /opt/nvidia/entrypoint.d/ # buildkit<br>2.95kB  &lt;missing&gt;       2025-10-24T13:52:23Z    /bin/bash -o pipefail -c #(nop) COPY file:ee705e8c64dea950825cecdc80dfabe46c25bd5c990af203948223ced59bb07e in /backend-container/containers/py311_tokenize_backport.patch <br>2.66kB  &lt;missing&gt;       2025-10-24T13:37:36Z    /bin/bash -o pipefail -c #(nop) COPY dir:fad3446edbc5f9c2127e7c88d5016d30439c738bb221f63c6f52a35704ed5131 in /content/sample_data <br>2.53kB  &lt;missing&gt;       2024-07-10T22:50:58Z    COPY nvidia_entrypoint.sh /opt/nvidia/ # buildkit<br>2.09kB  &lt;missing&gt;       2025-10-24T13:52:27Z    /bin/bash -o pipefail -c #(nop) COPY file:1f6e83fcc0f425bb1f6648ba682eb4d96b71b5536408501af87a63ecaf28b1a0 in /etc/ipython/ipython_config.py <br>1.89kB  &lt;missing&gt;       2025-10-24T13:52:26Z    /bin/bash -o pipefail -c #(nop) COPY file:986599674cfa20d7412b36c784c21062024f3ed221414f3ff2d2b2a10d7e7179 in /etc/jupyter/ <br>1.48kB  &lt;missing&gt;       2025-10-24T13:52:27Z    /bin/bash -o pipefail -c #(nop) COPY file:72f07be42222b55d55791e2615a6b52f6885fa86312f4e35dfa696c8ea4e0373 in /backend-container/containers/ <br>1.45kB  &lt;missing&gt;       2025-10-24T13:54:45Z    /bin/bash -o pipefail -c #(nop) COPY file:449cb0cd152ea14ab688051363372bd5b75b623b9629b10d3692b3744b2bb8d7 in /usr/local/colab/bin/ <br>1.36kB  &lt;missing&gt;       2025-10-24T13:52:27Z    /bin/bash -o pipefail -c #(nop) COPY file:fd538c60ab58911429ba14debbafd873951a903e2588786d1d5915af82d09071 in /backend-container/ <br>1.34kB  &lt;missing&gt;       2025-10-24T13:54:43Z    /bin/bash -o pipefail -c #(nop) COPY file:d138c03ba730a9cbc1aab19322ceaa59f11d8ab13eb534403ccd534f17c557c6 in /usr/local/bin/colab_diagnose_me <br>1.16kB  &lt;missing&gt;       2025-06-24T13:14:34Z    /bin/bash -o pipefail -c #(nop) COPY file:a00c06c317a595406ea6277f7f769df8e4b71da12f63dde99a0649aa1354cbe9 in /backend-container/containers/ <br>1.07kB  &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop) COPY file:970e55d4a3689467c605712adb7b084bc02d0a401e69398c5ae0b2c82ed80ffe in /root/.bashrc <br>357B    &lt;missing&gt;       2025-10-24T13:37:52Z    /bin/bash -o pipefail -c #(nop) COPY file:043943da3ac8b166378a4342b9835110d59de9a9e121beee7eedfd85cea612ff in /etc/ <br>254B    &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop) COPY file:c6467618ded482efe5080c32135c0d350ad44d10165b40aa7b0f540f30c1dd44 in /root/.tmux.conf <br>195B    &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop) COPY file:aa34963a155c947ab427480ebf40370b0efb8437fd0faa79a26b9bb17810c91d in /backend-container/containers/ <br>162B    &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop) COPY file:03c28878188f6da017439637bf91d5cfead17de2d7f2b96b7091a3b73aabfb29 in /backend-container/containers/ <br>155B    &lt;missing&gt;       2025-10-24T13:37:36Z    /bin/bash -o pipefail -c #(nop) COPY file:7c86ae5c29200bbcfe8eb1e66a782ffd59e61baae7b8207bb3de317f3762b6ae in /usr/local/bin/python <br>155B    &lt;missing&gt;       2025-10-24T13:37:36Z    /bin/bash -o pipefail -c #(nop) COPY file:7c86ae5c29200bbcfe8eb1e66a782ffd59e61baae7b8207bb3de317f3762b6ae in /usr/local/bin/pip <br>150B    &lt;missing&gt;       2025-10-24T13:37:35Z    /bin/bash -o pipefail -c #(nop) COPY file:5c02dad9e444ea87f4386bf9299ab71e1f28f3ff5681c815b8a9f9da3341d619 in /etc/apt/apt.conf.d/ <br>57B     &lt;missing&gt;       2025-10-24T13:19:45Z    /bin/bash -o pipefail -c #(nop) COPY file:82290c1ae07d98579bac01b736d1c5f486fe538210bf4002fd51b2ad975b2054 in /usr/local/bin/R <br>46B     &lt;missing&gt;       2024-07-10T22:46:29Z    RUN |1 TARGETARCH=amd64 /bin/sh -c echo &quot;/usr/local/nvidia/lib&quot; &gt;&gt; /etc/ld.so.conf.d/nvidia.conf     &amp;&amp; echo &quot;/usr/local/nvidia/lib64&quot; &gt;&gt; /etc/ld.so.conf.d/nvidia.conf # buildkit<br>0B      sha256:d5458217b23549186bb7d0b5e67f4eef0bd82ca12f84c3a341e7a46aa148b02a 2025-10-24T14:11:20Z    /bin/bash -o pipefail -c /usr/local/bin/colab_diagnose_me<br>0B      &lt;missing&gt;       2025-10-24T14:11:19Z    /bin/bash -o pipefail -c #(nop)  ENV COLAB_JUPYTER_ALLOW_ORIGIN_PAT=https://colab\.(sandbox|research)\.google\.com<br>0B      &lt;missing&gt;       2025-10-24T14:11:19Z    /bin/bash -o pipefail -c #(nop)  ENV COLAB_HUMAN_READABLE_NODE_LOGS=1<br>0B      &lt;missing&gt;       2025-10-24T13:54:46Z    /bin/bash -o pipefail -c #(nop)  ENV SHELL=/bin/bash<br>0B      &lt;missing&gt;       2025-10-24T13:54:46Z    /bin/bash -o pipefail -c #(nop)  ENV COLAB_TPU_1VM=<br>0B      &lt;missing&gt;       2025-10-24T13:54:46Z    /bin/bash -o pipefail -c #(nop)  ENV COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00<br>0B      &lt;missing&gt;       2025-10-24T13:54:46Z    /bin/bash -o pipefail -c #(nop)  ENTRYPOINT [&quot;/datalab/run.sh&quot;]<br>0B      &lt;missing&gt;       2025-10-24T13:54:45Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c [[ $(find /usr/lib/jvm -maxdepth 1 -mindepth 1 -type d |wc -l) = 1 ]]<br>0B      &lt;missing&gt;       2025-10-24T13:54:45Z    /bin/bash -o pipefail -c #(nop)  ENV ENV=/root/.bashrc<br>0B      &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop)  ENV VM_GCE_METADATA_HOST=169.254.169.254<br>0B      &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop)  ENV KMP_TARGET_PORT=9000<br>0B      &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop)  ENV KMP_LISTEN_PORT=6000<br>0B      &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop)  ENV COLAB_KERNEL_MANAGER_PROXY_PORT=6000<br>0B      &lt;missing&gt;       2025-10-24T13:54:44Z    /bin/bash -o pipefail -c #(nop)  ENV COLAB_FILE_HANDLER_ADDR=localhost:3453<br>0B      &lt;missing&gt;       2025-10-24T13:54:43Z    /bin/bash -o pipefail -c #(nop)  ENV UV_CONSTRAINT=<br>0B      &lt;missing&gt;       2025-10-24T13:54:43Z    /bin/bash -o pipefail -c #(nop)  ENV UV_BUILD_CONSTRAINT=<br>0B      &lt;missing&gt;       2025-10-24T13:19:02Z    /bin/bash -o pipefail -c #(nop)  ENV UV_SYSTEM_PYTHON=true<br>0B      &lt;missing&gt;       2025-10-24T13:19:01Z    |2 COLAB_PIP_COMPILE= COLAB_RELEASE_TAG=release-colab-external_20251024-060052_RC00 /bin/bash -o pipefail -c apt-get autoremove -y &amp;&amp;     apt-get clean &amp;&amp;     rm -rf /usr/local/src/*<br>0B      &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop)  ENV UV_INSTALL_DIR=/usr/local/bin<br>0B      &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop)  ENV UV_CONSTRAINT=/backend-container/containers/requirements.constraints<br>0B      &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop)  ENV UV_BUILD_CONSTRAINT=/backend-container/containers/build.constraints<br>0B      &lt;missing&gt;       2025-10-24T13:19:01Z    /bin/bash -o pipefail -c #(nop)  ENV TCLLIBPATH=/usr/share/tcltk/tcllib1.20<br>0B      &lt;missing&gt;       2025-06-24T13:14:34Z    /bin/sh -c #(nop)  ENV COLAB_WARMUP_DEFAULTS=1<br>0B      &lt;missing&gt;       2025-06-24T13:14:34Z    /bin/sh -c #(nop)  ENV COLAB_LANGUAGE_SERVER_PROXY=/usr/colab/bin/language_service<br>0B      &lt;missing&gt;       2025-06-24T13:14:34Z    /bin/sh -c #(nop)  ENV COLAB_JUPYTER_IP=127.0.0.1<br>0B      &lt;missing&gt;       2025-06-24T13:14:34Z    /bin/sh -c #(nop)  ENV COLAB_DEBUG_ADAPTER_MUX_PATH=/usr/local/bin/dap_multiplexer<br>0B      &lt;missing&gt;       2025-06-24T13:14:34Z    /bin/bash -o pipefail -c #(nop)  SHELL [/bin/bash -o pipefail -c]<br>0B      &lt;missing&gt;       2025-06-24T13:14:33Z    /bin/sh -c #(nop)  ENV LAST_FORCED_REBUILD=20250623<br>0B      &lt;missing&gt;       2025-03-26T13:13:50Z    /bin/sh -c #(nop)  ENV PYTHONUTF8=1<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  EXPOSE 8080<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV TF_FORCE_GPU_ALLOW_GROWTH=true<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV PYTHONPATH=/env/python<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV PATH=/opt/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/tools/node/bin:/tools/google-cloud-sdk/bin<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV LC_ALL=en_US.UTF-8<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV LANGUAGE=en_US<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV LANG=en_US.UTF-8<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV DEBIAN_FRONTEND=noninteractive<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV CLOUDSDK_PYTHON=python3<br>0B      &lt;missing&gt;       2025-01-28T14:13:14Z    /bin/sh -c #(nop)  ENV CLOUDSDK_CONFIG=/content/.config<br>0B      &lt;missing&gt;       2025-01-28T14:13:13Z    /bin/sh -c #(nop)  LABEL maintainer=Google Colaboratory Team &lt;colaboratory-team@google.com&gt;<br>0B      &lt;missing&gt;       2025-01-28T14:13:13Z    /bin/sh -c #(nop)  ARG COLAB_TPU_1VM<br>0B      &lt;missing&gt;       2025-01-28T14:13:13Z    /bin/sh -c #(nop)  ARG COLAB_RELEASE_TAG<br>0B      &lt;missing&gt;       2025-01-28T14:13:13Z    /bin/sh -c #(nop)  ARG COLAB_PIP_COMPILE<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    LABEL maintainer=NVIDIA CORPORATION &lt;cudatools@nvidia.com&gt;<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    LABEL com.nvidia.cudnn.version=9.2.1.18-1<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    ENV NV_CUDNN_VERSION=9.2.1.18-1<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    ENV NV_CUDNN_PACKAGE_NAME=libcudnn9-cuda-12<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    ENV NV_CUDNN_PACKAGE_DEV=libcudnn9-dev-cuda-12=9.2.1.18-1<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    ENV NV_CUDNN_PACKAGE=libcudnn9-cuda-12=9.2.1.18-1<br>0B      &lt;missing&gt;       2024-07-10T23:22:21Z    ARG TARGETARCH<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    LABEL maintainer=NVIDIA CORPORATION &lt;cudatools@nvidia.com&gt;<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_NVPROF_VERSION=12.5.82-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_NVPROF_DEV_PACKAGE=cuda-nvprof-12-5=12.5.82-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_NVML_DEV_VERSION=12.5.82-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBNPP_DEV_VERSION=12.3.0.159-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBNPP_DEV_PACKAGE=libnpp-dev-12-5=12.3.0.159-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBNCCL_DEV_PACKAGE_VERSION=2.22.3-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBNCCL_DEV_PACKAGE_NAME=libnccl-dev<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBNCCL_DEV_PACKAGE=libnccl-dev=2.22.3-1+cuda12.5<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBCUSPARSE_DEV_VERSION=12.5.1.3-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBCUBLAS_DEV_VERSION=12.5.3.2-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBCUBLAS_DEV_PACKAGE_NAME=libcublas-dev-12-5<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_LIBCUBLAS_DEV_PACKAGE=libcublas-dev-12-5=12.5.3.2-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_CUDA_NSIGHT_COMPUTE_VERSION=12.5.1-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE=cuda-nsight-compute-12-5=12.5.1-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_CUDA_LIB_VERSION=12.5.1-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NV_CUDA_CUDART_DEV_VERSION=12.5.82-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV NCCL_VERSION=2.22.3-1<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ENV LIBRARY_PATH=/usr/local/cuda/lib64/stubs<br>0B      &lt;missing&gt;       2024-07-10T23:20:17Z    ARG TARGETARCH<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    LABEL maintainer=NVIDIA CORPORATION &lt;cudatools@nvidia.com&gt;<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_NVTX_VERSION=12.5.82-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBNPP_VERSION=12.3.0.159-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBNPP_PACKAGE=libnpp-12-5=12.3.0.159-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBNCCL_PACKAGE_VERSION=2.22.3-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBNCCL_PACKAGE_NAME=libnccl2<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBNCCL_PACKAGE=libnccl2=2.22.3-1+cuda12.5<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBCUSPARSE_VERSION=12.5.1.3-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBCUBLAS_VERSION=12.5.3.2-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBCUBLAS_PACKAGE_NAME=libcublas-12-5<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_LIBCUBLAS_PACKAGE=libcublas-12-5=12.5.3.2-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NV_CUDA_LIB_VERSION=12.5.1-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NVIDIA_PRODUCT_NAME=CUDA<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENV NCCL_VERSION=2.22.3-1<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ENTRYPOINT [&quot;/opt/nvidia/nvidia_entrypoint.sh&quot;]<br>0B      &lt;missing&gt;       2024-07-10T22:50:58Z    ARG TARGETARCH<br>0B      &lt;missing&gt;       2024-07-10T22:46:29Z    ENV PATH=/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin<br>0B      &lt;missing&gt;       2024-07-10T22:46:29Z    ENV NVIDIA_VISIBLE_DEVICES=all<br>0B      &lt;missing&gt;       2024-07-10T22:46:29Z    ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility<br>0B      &lt;missing&gt;       2024-07-10T22:46:29Z    ENV LD_LIBRARY_PATH=/usr/local/nvidia/lib:/usr/local/nvidia/lib64<br>0B      &lt;missing&gt;       2024-07-10T22:46:16Z    LABEL maintainer=NVIDIA CORPORATION &lt;cudatools@nvidia.com&gt;<br>0B      &lt;missing&gt;       2024-07-10T22:46:16Z    ENV NV_CUDA_CUDART_VERSION=12.5.82-1<br>0B      &lt;missing&gt;       2024-07-10T22:46:16Z    ENV NVIDIA_REQUIRE_CUDA=cuda&gt;=12.5 brand=unknown,driver&gt;=470,driver&lt;471 brand=grid,driver&gt;=470,driver&lt;471 brand=tesla,driver&gt;=470,driver&lt;471 brand=nvidia,driver&gt;=470,driver&lt;471 brand=quadro,driver&gt;=470,driver&lt;471 brand=quadrortx,driver&gt;=470,driver&lt;471 brand=nvidiartx,driver&gt;=470,driver&lt;471 brand=vapps,driver&gt;=470,driver&lt;471 brand=vpc,driver&gt;=470,driver&lt;471 brand=vcs,driver&gt;=470,driver&lt;471 brand=vws,driver&gt;=470,driver&lt;471 brand=cloudgaming,driver&gt;=470,driver&lt;471 brand=unknown,driver&gt;=535,driver&lt;536 brand=grid,driver&gt;=535,driver&lt;536 brand=tesla,driver&gt;=535,driver&lt;536 brand=nvidia,driver&gt;=535,driver&lt;536 brand=quadro,driver&gt;=535,driver&lt;536 brand=quadrortx,driver&gt;=535,driver&lt;536 brand=nvidiartx,driver&gt;=535,driver&lt;536 brand=vapps,driver&gt;=535,driver&lt;536 brand=vpc,driver&gt;=535,driver&lt;536 brand=vcs,driver&gt;=535,driver&lt;536 brand=vws,driver&gt;=535,driver&lt;536 brand=cloudgaming,driver&gt;=535,driver&lt;536 brand=unknown,driver&gt;=550,driver&lt;551 brand=grid,driver&gt;=550,driver&lt;551 brand=tesla,driver&gt;=550,driver&lt;551 brand=nvidia,driver&gt;=550,driver&lt;551 brand=quadro,driver&gt;=550,driver&lt;551 brand=quadrortx,driver&gt;=550,driver&lt;551 brand=nvidiartx,driver&gt;=550,driver&lt;551 brand=vapps,driver&gt;=550,driver&lt;551 brand=vpc,driver&gt;=550,driver&lt;551 brand=vcs,driver&gt;=550,driver&lt;551 brand=vws,driver&gt;=550,driver&lt;551 brand=cloudgaming,driver&gt;=550,driver&lt;551<br>0B      &lt;missing&gt;       2024-07-10T22:46:16Z    ENV NVARCH=x86_64<br>0B      &lt;missing&gt;       2024-07-10T22:46:16Z    ENV CUDA_VERSION=12.5.1<br>0B      &lt;missing&gt;       2024-07-10T22:46:16Z    ARG TARGETARCH<br>0B      &lt;missing&gt;       2024-06-27T20:10:12Z    /bin/sh -c #(nop)  CMD [&quot;/bin/bash&quot;]<br>0B      &lt;missing&gt;       2024-06-27T20:10:10Z    /bin/sh -c #(nop)  LABEL org.opencontainers.image.version=22.04<br>0B      &lt;missing&gt;       2024-06-27T20:10:10Z    /bin/sh -c #(nop)  LABEL org.opencontainers.image.ref.name=ubuntu<br>0B      &lt;missing&gt;       2024-06-27T20:10:10Z    /bin/sh -c #(nop)  ARG RELEASE<br>0B      &lt;missing&gt;       2024-06-27T20:10:10Z    /bin/sh -c #(nop)  ARG LAUNCHPAD_BUILD_ARCH</pre><p>Here is the breakdown of the “disk budget”:</p><ul><li><strong>The ML Core (20.2 GB):</strong> The single largest layer. It contains the entire Python data science ecosystem (TensorFlow, PyTorch, GDAL), installed via uv for speed, with custom GPU-compiled extensions (like LightGBM).</li><li><strong>The “GPU Tax” (~7 GB):</strong> Split between runtime libraries (2GB) and development headers (4.9GB), allowing users to compile custom CUDA kernels inside the notebook.</li><li><strong>The System OS (5 GB):</strong> A heavy apt-get layer that turns the container into a full development machine, installing compilers (clang, gcc), generic tools (ffmpeg, git), and other languages (R, Java).</li><li><strong>The Julia Stack (~5 GB):</strong> A surprising amount of space is dedicated to the Julia language, including the runtime (1.3GB) and pre-installed plotting/ML libraries (3.5GB).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jE_chpJXgSe61797c1mvpQ.png" /></figure><p>When you start a Colab Runtime you can see the specifics via Terminal</p><pre>df -H<br>Filesystem      Size  Used Avail Use% Mounted on<br>overlay         121G   42G   80G  35% /<br>tmpfs            68M     0   68M   0% /dev<br>shm             6.2G     0  6.2G   0% /dev/shm<br>/dev/root       2.1G  1.3G  787M  62% /usr/sbin/docker-init<br>tmpfs           6.9G   50k  6.9G   1% /var/colab<br>/dev/sda1        80G   44G   36G  56% /kaggle/input<br>tmpfs           6.9G     0  6.9G   0% /proc/acpi<br>tmpfs           6.9G     0  6.9G   0% /proc/scsi<br>tmpfs           6.9G     0  6.9G   0% /sys/firmware</pre><p>You can use `ncdu` tool to get more insights of disk space:</p><pre>apt-get update &amp;&amp; apt-get install -y ncdu<br>ncdu /</pre><pre>ncdu 1.15.1 ~ Use the arrow keys to navigate, press ? for help                                                                 <br>--- / -------------------------------------------------------------------------------------------------------------------------<br>   30.7 GiB [######### ] /usr                                                                                                  <br>    4.3 GiB [#         ] /root <br>    1.7 GiB [          ] /tools<br>    1.2 GiB [          ] /opt<br>  135.2 MiB [          ] /var  <br>   54.3 MiB [          ] /content<br>   50.7 MiB [          ] /datalab<br>   20.0 MiB [          ] /python-apt<br>    3.4 MiB [          ] /etc  <br>  340.0 KiB [          ]  python-apt.tar.xz<br>   84.0 KiB [          ] /run <br>   40.0 KiB [          ] /tmp<br>   20.0 KiB [          ]  NGC-DL-CONTAINER-LICENSE<br>    8.0 KiB [          ] /kaggle<br>    8.0 KiB [          ]  cuda-keyring_1.1-1_all.deb<br>e   4.0 KiB [          ] /srv    <br>e   4.0 KiB [          ] /mnt  <br>e   4.0 KiB [          ] /media<br>e   4.0 KiB [          ] /home<br>e   4.0 KiB [          ] /boot<br>    0.0   B [          ] /sys<br>.   0.0   B [          ] /proc<br>    0.0   B [          ] /dev<br>@   0.0   B [          ]  libx32<br>@   0.0   B [          ]  lib64<br>@   0.0   B [          ]  lib32<br>@   0.0   B [          ]  sbin<br>@   0.0   B [          ]  lib<br>@   0.0   B [          ]  bin<br>    0.0   B [          ]  .dockerenv<br><br> Total disk usage:  38.3 GiB  Apparent size:  37.9 GiB  Items: 520162 </pre><pre>ncdu 1.15.1 ~ Use the arrow keys to navigate, press ? for help                                                                 <br>--- /usr ----------------------------------------------------------------------------------------------------------------------<br>                         /..                                                                                                   <br>   24.3 GiB [##########] /local<br>    4.9 GiB [#         ] /lib  <br>  436.3 MiB [          ] /lib64-nvidia<br>  361.1 MiB [          ] /share<br>  342.2 MiB [          ] /bin<br>  254.0 MiB [          ] /include<br>  140.1 MiB [          ] /src<br>   28.9 MiB [          ] /colab<br>    8.6 MiB [          ] /sbin<br>    1.9 MiB [          ] /grte<br>    1.6 MiB [          ] /libexec<br>   20.0 KiB [          ] /etc<br>    4.0 KiB [          ] /lib64 <br>e   4.0 KiB [          ] /libx32<br>e   4.0 KiB [          ] /lib32  <br>e   4.0 KiB [          ] /games<br><br> Total disk usage:  30.7 GiB  Apparent size:  30.1 GiB  Items: 316027</pre><p>Analyzing the `dist-packages` folder.</p><pre>ncdu 1.15.1 ~ Use the arrow keys to navigate, press ? for help                                                                 <br>--- /usr/local/lib/python3.12/dist-packages -----------------------------------------------------------------------------------<br>                         /..<br>    3.6 GiB [##########] /nvidia                                                                                               <br>    2.1 GiB [#####     ] /libcugraph<br>    1.7 GiB [####      ] /tensorflow<br>    1.6 GiB [####      ] /torch<br>  926.7 MiB [##        ] /libcudf<br>  682.7 MiB [#         ] /libcuml<br>  591.3 MiB [#         ] /triton<br>  455.3 MiB [#         ] /pyspark<br>  370.2 MiB [#         ] /jax_plugins<br>  305.4 MiB [          ] /jaxlib<br>  207.5 MiB [          ] /xgboost<br>  196.8 MiB [          ] /gradio<br>  186.3 MiB [          ] /cupy<br>  162.2 MiB [          ] /google<br>  128.3 MiB [          ] /llvmlite<br>  126.7 MiB [          ] /pyarrow<br>  118.7 MiB [          ] /cuda<br>  116.5 MiB [          ] /spacy<br>  113.9 MiB [          ] /panel<br>  106.3 MiB [          ] /polars<br>  100.6 MiB [          ] /music21<br>   98.3 MiB [          ] /opencv_python.libs<br>   98.3 MiB [          ] /opencv_contrib_python.libs<br>   97.3 MiB [          ] /scipy<br>   94.6 MiB [          ] /plotly<br>   91.6 MiB [          ] /googleapiclient<br>   86.2 MiB [          ] /libucx<br>   79.8 MiB [          ] /cv2<br>   76.9 MiB [          ] /pyogrio.libs<br>   76.6 MiB [          ] /cmake<br>   76.2 MiB [          ] /imageio_ffmpeg<br> Total disk usage:  17.6 GiB  Apparent size:  17.2 GiB  Items: 187914</pre><p>In the case of Colab Enterprise Runtimes, the image container is baked into the OS, hence uses disk space into the boot disk to prevent delays during startup time. The boot disk is a persistent SSD PD that improves startup time performance, compared to just Standard PD.</p><p>If you want to see what exactly happens when the container starts you can use `strace`, in this case using the entrypoint `/datalab/run.sh` we analyze the files read and write:</p><pre>docker run --name colab-startup-strace --entrypoint /bin/bash us-docker.pkg.dev/colab-images/public/runtime:latest -c &quot;apt-get update &amp;&amp; apt-get install -y strace &amp;&amp; strace -f -T -e trace=openat,execve -o /tmp/startup_trace.txt /datalab/run.sh&quot;</pre><p>To extract a copy of file to local server use:</p><pre>docker cp colab-startup-strace:/tmp/startup_trace.txt ./startup_trace.txt</pre><p>Now you can start identify potential delays during Container startup:</p><pre>[2025-12-29T00:03:54.333Z]  INFO: app/776 on 9e3e2d1d9f84: app: starting with settings: {&quot;contentDir&quot;:&quot;/content&quot;,&quot;datalabRoot&quot;:&quot;&quot;,&quot;jupyterArgs&quot;:[&quot;--debug&quot;,&quot;--ServerApp.allow_origin_pat=\&quot;https://colab\\.(sandbox|research)\\.google\\.com\&quot;&quot;,&quot;--ServerApp.allow_origin=&quot;,&quot;--ServerApp.allow_credentials=True&quot;,&quot;--ip=127.0.0.1&quot;],&quot;nextJupyterPort&quot;:9000,&quot;serverHost&quot;:&quot;&quot;,&quot;serverPort&quot;:8080,&quot;languageServerProxy&quot;:&quot;/usr/colab/bin/language_service&quot;,&quot;debugAdapterMultiplexerPath&quot;:&quot;/usr/local/bin/dap_multiplexer&quot;,&quot;kernelManagerProxyPort&quot;:6000,&quot;fileHandlerAddr&quot;:&quot;localhost:3453&quot;}<br>[2025-12-29T00:03:54.355Z]  INFO: app/776 on 9e3e2d1d9f84: Launching Jupyter server at 9000<br>[2025-12-29T00:03:54.356Z]  INFO: app/776 on 9e3e2d1d9f84: Using jupyter server address 127.0.0.1<br>[2025-12-29T00:03:54.366Z]  INFO: app/776 on 9e3e2d1d9f84: Jupyter process started with pid 864 and args [&quot;server&quot;,&quot;--debug&quot;,&quot;--ServerApp.allow_origin_pat=\&quot;https://colab\\.(sandbox|research)\\.google\\.com\&quot;&quot;,&quot;--ServerApp.allow_origin=&quot;,&quot;--ServerApp.allow_credentials=True&quot;,&quot;--ip=127.0.0.1&quot;,&quot;--port=9000&quot;,&quot;--FileContentsManager.root_dir=/&quot;,&quot;--FileContentsManager.allow_hidden=True&quot;,&quot;--ServerApp.log_format=\&quot;|%(levelname)s|%(message)s\&quot;&quot;,&quot;--ServerApp.iopub_data_rate_limit=1e10&quot;,&quot;--MappingKernelManager.root_dir=/content&quot;]<br>[2025-12-29T00:03:54.391Z]  INFO: app/776 on 9e3e2d1d9f84: Starting server at http://localhost:8080<br>[2025-12-29T00:03:54.448Z]  INFO: app/776 on 9e3e2d1d9f84:<br>    /usr/local/bin/dap_multiplexer: DAP multiplexer listening on /tmp/debugger_2fez5h8cl0<br>    <br>2025-12-29 00:03:55.663793: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered<br>WARNING: All log messages before absl::InitializeLog() is called are written to STDERR<br>E0000 00:00:1766966635.962661     839 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered<br>E0000 00:00:1766966636.054792     839 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered<br>W0000 00:00:1766966636.774572     839 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.<br>W0000 00:00:1766966636.774997     839 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.<br>W0000 00:00:1766966636.775086     839 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.<br>W0000 00:00:1766966636.775147     839 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.<br>2025-12-29 00:03:56.848269: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.<br>To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.<br>[2025-12-29T00:03:57.067Z]  WARN: app/776 on 9e3e2d1d9f84:   warn( (type=jupyter)<br>[2025-12-29T00:03:57.069Z]  WARN: app/776 on 9e3e2d1d9f84:   warn( (type=jupyter)<br>[2025-12-29T00:03:57.069Z]  WARN: app/776 on 9e3e2d1d9f84: ServerApp.iopub_data_rate_limit config is deprecated in 2.0. Use ZMQChannelsWebsocketConnection.iopub_data_rate_limit. (type=jupyter)<br>[2025-12-29T00:03:57.877Z]  WARN: app/776 on 9e3e2d1d9f84:      /etc/jupyter/jupyter_server_config.json (type=jupyter)<br>[2025-12-29T00:03:57.879Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/google.colab._serverextension.json (type=jupyter)<br>[2025-12-29T00:03:57.879Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/ipyparallel.json (type=jupyter)<br>[2025-12-29T00:03:57.879Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/jupyter_server_terminals.json (type=jupyter)<br>[2025-12-29T00:03:57.879Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/jupytext.json (type=jupyter)<br>[2025-12-29T00:03:57.880Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/nbclassic.json (type=jupyter)<br>[2025-12-29T00:03:57.880Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/notebook_shim.json (type=jupyter)<br>[2025-12-29T00:03:57.880Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.d/panel-client-jupyter.json (type=jupyter)<br>[2025-12-29T00:03:57.880Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_server_config.json (type=jupyter)<br>[2025-12-29T00:03:57.893Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/etc/jupyter/jupyter_server_config.d/google.colab.json (type=jupyter)<br>[2025-12-29T00:03:57.893Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/etc/jupyter/jupyter_server_config.d/nbclassic.json (type=jupyter)<br>[2025-12-29T00:03:57.893Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/etc/jupyter/jupyter_server_config.json (type=jupyter)<br>[2025-12-29T00:03:57.897Z]  WARN: app/776 on 9e3e2d1d9f84:      /root/.local/etc/jupyter/jupyter_server_config.json (type=jupyter)<br>[2025-12-29T00:03:57.898Z]  WARN: app/776 on 9e3e2d1d9f84:      /root/.jupyter/jupyter_server_config.json (type=jupyter)<br>[2025-12-29T00:03:57.904Z]  WARN: app/776 on 9e3e2d1d9f84: /usr/local/lib/python3.12/dist-packages/jupyter_events/schema.py:68: JupyterEventsVersionWarning: The `version` property of an event schema must be a string. It has been type coerced, but in a future version of this library, it will fail to validate. Please update schema: https://events.jupyter.org/jupyter_server/contents_service/v1 (type=jupyter)<br>[2025-12-29T00:03:57.904Z]  WARN: app/776 on 9e3e2d1d9f84:   validate_schema(_schema) (type=jupyter)<br>[2025-12-29T00:03:57.908Z]  WARN: app/776 on 9e3e2d1d9f84: /usr/local/lib/python3.12/dist-packages/jupyter_events/schema.py:68: JupyterEventsVersionWarning: The `version` property of an event schema must be a string. It has been type coerced, but in a future version of this library, it will fail to validate. Please update schema: https://events.jupyter.org/jupyter_server/gateway_client/v1 (type=jupyter)<br>[2025-12-29T00:03:57.908Z]  WARN: app/776 on 9e3e2d1d9f84:   validate_schema(_schema) (type=jupyter)<br>[2025-12-29T00:03:57.913Z]  WARN: app/776 on 9e3e2d1d9f84: /usr/local/lib/python3.12/dist-packages/jupyter_events/schema.py:68: JupyterEventsVersionWarning: The `version` property of an event schema must be a string. It has been type coerced, but in a future version of this library, it will fail to validate. Please update schema: https://events.jupyter.org/jupyter_server/kernel_actions/v1 (type=jupyter)<br>[2025-12-29T00:03:57.913Z]  WARN: app/776 on 9e3e2d1d9f84:   validate_schema(_schema) (type=jupyter)<br>[2025-12-29T00:03:58.142Z]  INFO: app/776 on 9e3e2d1d9f84: Extension package ipyparallel took 0.2273s to import (type=jupyter)<br>[2025-12-29T00:03:58.142Z]  WARN: app/776 on 9e3e2d1d9f84: A `_jupyter_server_extension_points` function was not found in ipyparallel. Instead, a `_jupyter_server_extension_paths` function was found and will be used for now. This function name will be deprecated in future releases of Jupyter Server. (type=jupyter)<br>[2025-12-29T00:03:58.740Z]  INFO: app/776 on 9e3e2d1d9f84: Extension package jupyterlab_jupytext took 0.5650s to import (type=jupyter)<br>[2025-12-29T00:03:58.764Z]  WARN: app/776 on 9e3e2d1d9f84: A `_jupyter_server_extension_points` function was not found in nbclassic. Instead, a `_jupyter_server_extension_paths` function was found and will be used for now. This function name will be deprecated in future releases of Jupyter Server. (type=jupyter)<br>[2025-12-29T00:04:00.872Z]  INFO: app/776 on 9e3e2d1d9f84: Extension package panel.io.jupyter_server_extension took 2.1041s to import (type=jupyter)<br>[2025-12-29T00:04:00.872Z]  INFO: app/776 on 9e3e2d1d9f84: google.colab  (type=jupyter)<br>[2025-12-29T00:04:00.872Z]  INFO: app/776 on 9e3e2d1d9f84: google.colab._serverextension  (type=jupyter)<br>[2025-12-29T00:04:00.873Z]  INFO: app/776 on 9e3e2d1d9f84: ipyparallel  (type=jupyter)<br>[2025-12-29T00:04:00.884Z]  INFO: app/776 on 9e3e2d1d9f84: jupyter_server_terminals  (type=jupyter)<br>[2025-12-29T00:04:00.885Z]  INFO: app/776 on 9e3e2d1d9f84: jupyterlab_jupytext  (type=jupyter)<br>[2025-12-29T00:04:00.896Z]  INFO: app/776 on 9e3e2d1d9f84: nbclassic  (type=jupyter)<br>[2025-12-29T00:04:00.902Z]  INFO: app/776 on 9e3e2d1d9f84: Writing Jupyter server cookie secret to /root/.local/share/jupyter/runtime/jupyter_cookie_secret (type=jupyter)<br>[2025-12-29T00:04:00.909Z]  WARN: app/776 on 9e3e2d1d9f84:      /root/.jupyter/jupyter_notebook_config.json (type=jupyter)<br>[2025-12-29T00:04:00.910Z]  WARN: app/776 on 9e3e2d1d9f84:      /etc/jupyter/jupyter_notebook_config.json (type=jupyter)<br>[2025-12-29T00:04:00.912Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_notebook_config.d/ipyparallel.json (type=jupyter)<br>[2025-12-29T00:04:00.912Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_notebook_config.d/jupytext.json (type=jupyter)<br>[2025-12-29T00:04:00.912Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_notebook_config.d/panel-client-jupyter.json (type=jupyter)<br>[2025-12-29T00:04:00.912Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/local/etc/jupyter/jupyter_notebook_config.json (type=jupyter)<br>[2025-12-29T00:04:00.918Z]  WARN: app/776 on 9e3e2d1d9f84:      /usr/etc/jupyter/jupyter_notebook_config.json (type=jupyter)<br>[2025-12-29T00:04:00.918Z]  WARN: app/776 on 9e3e2d1d9f84:      /root/.local/etc/jupyter/jupyter_notebook_config.json (type=jupyter)<br>[2025-12-29T00:04:00.919Z]  WARN: app/776 on 9e3e2d1d9f84:      /root/.jupyter/jupyter_notebook_config.json (type=jupyter)<br>[2025-12-29T00:04:00.919Z]  INFO: app/776 on 9e3e2d1d9f84: notebook_shim  (type=jupyter)<br>[2025-12-29T00:04:00.920Z]  INFO: app/776 on 9e3e2d1d9f84: panel.io.jupyter_server_extension  (type=jupyter)<br>[2025-12-29T00:04:01.504Z]  WARN: app/776 on 9e3e2d1d9f84: Customizing authentication via ServerApp.login_handler_class=&lt;class &#39;google.colab._login_handler.ColabLoginHandler&#39;&gt; is deprecated in Jupyter Server 2.0. Use ServerApp.identity_provider_class. Falling back on legacy authentication. (type=jupyter)<br>[2025-12-29T00:04:01.601Z]  INFO: app/776 on 9e3e2d1d9f84: notebook_shim  (type=jupyter)<br>[2025-12-29T00:04:01.601Z]  INFO: app/776 on 9e3e2d1d9f84: google.colab  (type=jupyter)<br>[2025-12-29T00:04:01.616Z]  INFO: app/776 on 9e3e2d1d9f84: google.colab server extension initialized on 9e3e2d1d9f84. (type=jupyter)<br>[2025-12-29T00:04:01.616Z]  INFO: app/776 on 9e3e2d1d9f84: google.colab._serverextension  (type=jupyter)<br>[2025-12-29T00:04:01.626Z]  INFO: app/776 on 9e3e2d1d9f84: Loading IPython parallel extension (type=jupyter)<br>[2025-12-29T00:04:01.627Z]  INFO: app/776 on 9e3e2d1d9f84: ipyparallel  (type=jupyter)<br>[2025-12-29T00:04:01.629Z]  INFO: app/776 on 9e3e2d1d9f84: jupyter_server_terminals  (type=jupyter)<br>[2025-12-29T00:04:01.630Z]  INFO: app/776 on 9e3e2d1d9f84: [Jupytext Server Extension] Deriving an AsyncTextFileContentsManager from AsyncLargeFileManager (type=jupyter)<br>[2025-12-29T00:04:01.631Z]  INFO: app/776 on 9e3e2d1d9f84: jupyterlab_jupytext  (type=jupyter)<br>[2025-12-29T00:04:01.664Z]  INFO: app/776 on 9e3e2d1d9f84: nbclassic  (type=jupyter)<br>[2025-12-29T00:04:01.665Z]  INFO: app/776 on 9e3e2d1d9f84: panel.io.jupyter_server_extension  (type=jupyter)<br>[2025-12-29T00:04:01.666Z]  INFO: app/776 on 9e3e2d1d9f84: Serving notebooks from local directory: / (type=jupyter)<br>[2025-12-29T00:04:01.666Z]  INFO: app/776 on 9e3e2d1d9f84: Jupyter Server 2.14.0 is running at: (type=jupyter)<br>[2025-12-29T00:04:01.667Z]  INFO: app/776 on 9e3e2d1d9f84: http://127.0.0.1:9000/?token=e66132c0813a6756c1eaefd4249be89237d2a07455cc8289 (type=jupyter)<br>[2025-12-29T00:04:01.667Z]  INFO: app/776 on 9e3e2d1d9f84:     http://127.0.0.1:9000/?token=e66132c0813a6756c1eaefd4249be89237d2a07455cc8289 (type=jupyter)<br>[2025-12-29T00:04:01.667Z]  INFO: app/776 on 9e3e2d1d9f84: Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). (type=jupyter)<br>[2025-12-29T00:04:01.674Z] ERROR: app/776 on 9e3e2d1d9f84:  (type=jupyter)<br>[2025-12-29T00:04:01.675Z]  WARN: app/776 on 9e3e2d1d9f84:     To access the server, open this file in a browser: (type=jupyter)<br>[2025-12-29T00:04:01.675Z]  WARN: app/776 on 9e3e2d1d9f84:         file:///root/.local/share/jupyter/runtime/jpserver-864-open.html (type=jupyter)<br>[2025-12-29T00:04:01.675Z]  WARN: app/776 on 9e3e2d1d9f84:     Or copy and paste one of these URLs: (type=jupyter)<br>[2025-12-29T00:04:01.675Z]  WARN: app/776 on 9e3e2d1d9f84:         http://127.0.0.1:9000/?token=e66132c0813a6756c1eaefd4249be89237d2a07455cc8289 (type=jupyter)<br>[2025-12-29T00:04:01.675Z]  WARN: app/776 on 9e3e2d1d9f84:         http://127.0.0.1:9000/?token=e66132c0813a6756c1eaefd4249be89237d2a07455cc8289 (type=jupyter)</pre><p>During the boot-up process you see the following:</p><h4>The panel Extension (2.1s)</h4><p>The log explicitly states: Extension package panel.io.jupyter_server_extension took 2.1041s to import. Panel is a high-level dashboarding library. To initialize, it likely imports Bokeh, Param, and potentially parts of Pandas or Matplotlib. If you are not strictly using Panel for dashboards in every single session, this is expensive &quot;bloat&quot; to pay on every startup.</p><h4>The Duplicate Registration Errors (CUDA)</h4><p>The logs Unable to register cuDNN factory... when one has already been registered indicate that your TensorFlow/XLA binary might be statically linking libraries that are also being dynamically loaded (or linked twice). This forces the CPU to verify symbols and handle errors, contributing to the 2.4s gap before TensorFlow is ready.</p><p>Check for Python open files</p><pre>Count successes (Found the file) vs Failures (File not found)<br>echo &quot;Total python opens: $(grep &quot;openat&quot; ./startup_trace.txt | grep &quot;.py&quot; | wc -l)&quot;<br>echo &quot;Failed opens (ENOENT): $(grep &quot;openat&quot; ./startup_trace.txt | grep &quot;.py&quot; | grep &quot;ENOENT&quot; | wc -l)&quot;<br>Total python opens: 16559<br>Failed opens (ENOENT): 3408</pre><p><strong>The Data:</strong></p><ul><li><strong>Total Filesystem Lookups:</strong> 16,559</li><li><strong>Startup Time:</strong> ~7.3s</li><li><strong>The Culprit:</strong> A combination of heavy extensions (Panel) and an inefficient sys.path caused the interpreter to hammer the filesystem with thousands of failed lookups per second.</li></ul><p><strong>The Fix:</strong></p><ol><li><strong>Disable Unused Extensions:</strong> removing panel cut the dependency graph.</li><li><strong>Optimize </strong><strong>PYTHONPATH:</strong> (Optional) Reordering the path so frequently used library directories come <em>first</em> reduces the &#39;seek time&#39; for imports.&quot;</li></ol><p>To dig further on panel</p><pre> python3 -X importtime -c &quot;import panel.io.jupyter_server_extension&quot; 2&gt; import_log.txt  </pre><pre>import time:   1096259 |    1118432 |                rfc3987_syntax.syntax_helpers<br>import time:       233 |    1118664 |              rfc3987_syntax<br>...<br>import time:       748 |    1209474 |            jsonschema._format<br>import time:       224 |    1223863 |          jsonschema</pre><p>Here is where your 2.2 seconds went:</p><ol><li><strong>rfc3987_syntax</strong>: <strong>1.1s</strong> (Regex compilation overhead)</li><li><strong>panel logic</strong>: <strong>0.5s</strong> (Layout engine init)</li><li><strong>bokeh logic</strong>: <strong>0.4s</strong> (Plotting engine init)</li><li><strong>numpy</strong>: <strong>0.07s</strong> (Surprisingly fast!)</li></ol><p>The recommendation here would be disabling panel extension.</p><p><strong>The “IO Myth”</strong></p><p>Common wisdom says “Docker is slow because opening thousands of files is expensive.”</p><p>Your Data: “False. We opened 3,536 TensorFlow files in just 0.22 seconds. The filesystem is blazing fast.”</p><p>The delay isn’t opening the files; it’s running them.</p><ul><li><strong>TensorFlow:</strong></li><li>Time to find/open 3,536 files: <strong>0.2s</strong></li><li>Time to initialize (from previous logs): <strong>~2.4s</strong></li><li><strong>Verdict:</strong> 90% of the time is spent on CPU/Linking, not Disk.</li><li><strong>Panel:</strong></li><li>Time to find/open 170 files: <strong>0.008s</strong></li><li>Time to initialize: <strong>2.2s</strong></li><li><strong>Verdict:</strong> 99% of the time is CPU (specifically that rfc3987 regex compilation we found earlier).</li></ul><pre>| Library | Files Opened | Time to Open (I/O) | Time to Load (CPU) | Bottleneck |<br>| :--- | :--- | :--- | :--- | :--- |<br>| **TensorFlow** | 3,536 | 0.23s | ~2.40s | CPU / Linking |<br>| **Panel** | 170 | 0.008s | ~2.10s | CPU (Regex) |<br>| **Scipy** | 787 | 0.14s | ~0.50s | Mixed |</pre><p>You can use this AWK script to measure library startup time and files opened. <a href="https://gist.github.com/gogasca/409259cc7efd75c0741894b243885bd5">https://gist.github.com/gogasca/409259cc7efd75c0741894b243885bd5</a></p><pre>awk -f library_import_time.awk startup_trace.txt | sort -t&#39;|&#39; -k2 -rn<br>tensorflow|0.233495|3536<br>keras|0.072873|985<br>scipy|0.065992|787<br>jax|0.035728|451<br>stdlib:__pycache__|0.032074|455<br>numpy|0.027703|441<br>IPython|0.019341|359</pre><p>Checking extensions:</p><p>You could pass: `COLAB_WARMUP_DEFAULTS=0` and disable panel extension:</p><pre>docker exec colab-startup-strace ls /usr/local/etc/jupyter/jupyter_server_config.d/<br>google.colab._serverextension.json<br>ipyparallel.json<br>jupyter_server_terminals.json<br>jupytext.json<br>nbclassic.json<br>notebook_shim.json<br>panel-client-jupyter.json<br>(base) jupyter@instance-20251214-215021:~$ docker exec colab-startup-strace cat /usr/local/etc/jupyter/jupyter_server_config.d/panel-client-jupyter.json<br>{<br>  &quot;ServerApp&quot;: {<br>    &quot;jpserver_extensions&quot;: {<br>      &quot;panel.io.jupyter_server_extension&quot;: true<br>    }<br>  }<br>}<br>(base) jupyter@instance-20251214-215021:~$ docker exec colab-startup-strace cat /usr/local/etc/jupyter/jupyter_server_config.d/ipyparallel.json<br>{<br>  &quot;ServerApp&quot;: {<br>    &quot;jpserver_extensions&quot;: {<br>      &quot;ipyparallel&quot;: true<br>    }<br>  }<br>}</pre><p>Disable panel</p><pre># Option A: Remove the config only (Keeps library, disables auto-load)<br>RUN rm /usr/local/etc/jupyter/jupyter_server_config.d/panel-client-jupyter.json<br><br># Option B: Uninstall completely (Best for disk space + speed)<br>RUN pip uninstall -y panel</pre><p>Optimizing large-scale ML containers requires looking past the file size and understanding the runtime behavior. Our analysis of the Colab image revealed that high latency isn’t always a result of disk I/O, but often stems from CPU-bound tasks during initialization.</p><p>By using strace and forensic profiling, we uncovered three critical insights:</p><ol><li><strong>The “Cold Start” Tax is Complex:</strong> It’s not just pulling bytes; it’s the cost of verifying OCI layers, overlay filesystem overhead, and the initialization of massive libraries.</li><li><strong>The I/O Myth:</strong> Contrary to popular belief, opening thousands of files is fast. The real bottleneck lies in <em>processing</em> those files specifically, CPU-intensive tasks like regex compilation in the panel extension or symbol linking in TensorFlow.</li><li><strong>Invisible Bloat:</strong> A single unused extension can add seconds to your boot time. In our case, the panel dashboarding library was adding over 2 seconds to startup, regardless of whether the user needed it.</li></ol><p>Optimizing your compute infrastructure isn’t just about faster SSDs; it’s about curating your dependencies and validating that every second of startup time delivers value to the user.</p><p><strong>Coming Next:</strong> Now that we understand how to analyze the current state of a massive image, the natural next step is re-architecture. In our next post, we will explore how to simplify Docker image creation, reduce the attack surface and size, and leverage advanced container layering strategies to build lighter, faster Runtimes.</p><p>References:</p><ul><li><a href="https://lokiwager.github.io/posts/reduce-image-size/">How to reduce the Nvidia Docker image size</a></li><li><a href="https://lokiwager.github.io/posts/accelerate-image-startup/">How to accelerate the startup of a large Docker image?</a></li><li><a href="https://medium.com/nttlabs/startup-containers-in-lightning-speed-with-lazy-image-distribution-on-containerd-243d94522361">Startup Containers in Lightning Speed with Lazy Image Distribution on Containerd</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=629f58073784" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Improving Runtime Creation and Execution]]></title>
            <link>https://medium.com/@gogasca_/improving-runtime-creation-and-execution-33c27fafba96?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/33c27fafba96</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Tue, 16 Dec 2025 06:19:28 GMT</pubDate>
            <atom:updated>2025-12-16T06:19:28.122Z</atom:updated>
            <content:encoded><![CDATA[<p>Recently, I have been working internally with the Colab team to deploy the Data Science Agent. Our team achieved using the same Data Science Agent across <strong>our</strong> Enterprise and Consumer platforms, all powered by ADK, Agent Engine, Gemini, and other internal services. As part of this effort, we have a dedicated team for Evals.</p><p>Evals for notebooks is an interesting topic. Not only does it require you to execute Python code and compare results, but you also need an underlying infrastructure that provides the following:</p><ol><li><strong>Same Runtime environment:</strong> Python version, pip libraries</li><li><strong>End-User credentials</strong></li><li><strong>Code Execution</strong></li></ol><p>Currently, Colab Enterprise already supports all of these. However, for #3, if you want to execute code, you typically require <strong>connecting to the</strong> UI. Fortunately, I developed a new dedicated API that allows users to execute code by starting a kernel and passing a blob of text to execute, the same way a user would via the Colab UI. The advantage is that this API interacts directly with the Jupyter Kernel and supports long-running operations (<strong>e.g.,</strong> cells that take a long time to execute).</p><p>During this discovery, I struggled to find an external dedicated sample for creating a Runtime Template and Runtime. I found REST, gcloud, and a few Python references, <strong>so</strong> I decided to just show a minimal example. Please take a look at the following:</p><ol><li>Define Cloud environment and library imports</li></ol><pre>from google.cloud import aiplatform_v1<br>import os<br><br>PROJECT_ID = os.environ.get(&#39;GOOGLE_CLOUD_PROJECT&#39;)<br>LOCATION = os.environ.get(&#39;GOOGLE_CLOUD_REGION&#39;)<br>API_ENDPOINT = f&quot;{LOCATION}-aiplatform.googleapis.com&quot;<br>PARENT = f&quot;projects/{PROJECT_ID}/locations/{LOCATION}&quot;<br><br># Initialize the client once globally<br>notebook_service_client = aiplatform_v1.NotebookServiceClient(client_options = {<br>    &quot;api_endpoint&quot;: API_ENDPOINT,<br>})</pre><p>2. Create a Runtime Template</p><pre>def create_notebook_runtime_template(client: aiplatform_v1.NotebookServiceClient):<br>    notebook_runtime_template = aiplatform_v1.NotebookRuntimeTemplate()<br>    notebook_runtime_template.display_name = &quot;eval&quot;<br>    notebook_runtime_template.machine_spec = aiplatform_v1.MachineSpec(machine_type=&quot;e2-standard-4&quot;)<br>    notebook_runtime_template.network_spec = aiplatform_v1.NetworkSpec(<br>        network=f&quot;projects/{PROJECT_ID}/global/networks/default&quot;,<br>        subnetwork=f&quot;projects/{PROJECT_ID}/regions/{LOCATION}/subnetworks/default&quot;<br>    )<br>    notebook_runtime_template.euc_config.euc_disabled = True<br>    notebook_runtime_template.idle_shutdown_config.idle_timeout = &quot;10800s&quot;<br>    request = aiplatform_v1.CreateNotebookRuntimeTemplateRequest(<br>        parent=PARENT,<br>        notebook_runtime_template=notebook_runtime_template,<br>    )<br>    print(&quot;Creating Runtime Template, waiting for operation to complete...&quot;)<br>    try:<br>        operation = client.create_notebook_runtime_template(request=request)<br>        runtime_template_ref = operation.result()<br>        print(f&quot;Runtime Template: {runtime_template_ref}&quot;)<br>        print(f&quot;Operation status: {operation.done()}&quot;)<br>        return runtime_template_ref.name<br>    except Exception as e:<br>        print(f&quot;Error creating Notebook Runtime Template: {e}&quot;)</pre><p>3. Create a new Runtime</p><pre>def assign_runtime(runtime_template_ref: str, client: aiplatform_v1.NotebookServiceClient):<br>    if not runtime_template_ref:<br>        print(&quot;Cannot assign runtime: Notebook Runtime Template reference is missing.&quot;)<br>        return<br><br>    notebook_runtime = aiplatform_v1.NotebookRuntime()<br>    notebook_runtime.runtime_user = &quot;gascagonzalo@gmail.com&quot;<br>    notebook_runtime.display_name = &quot;my-eval-runtime&quot;<br><br>    request = aiplatform_v1.AssignNotebookRuntimeRequest(<br>        parent=PARENT,<br>        notebook_runtime_template=runtime_template_ref,<br>        notebook_runtime=notebook_runtime,<br>    )<br><br>    print(&quot;Creating Runtime, waiting for operation to complete...&quot;)<br>    try:<br>        operation = client.assign_notebook_runtime(request=request)<br>        response = operation.result()<br>        print(response)<br>        print(f&quot;Operation status: {operation.done()}&quot;)<br>    except Exception as e:<br>        print(f&quot;Error assigning Notebook Runtime: {e}&quot;)</pre><p>Running example:</p><pre>runtime_template_ref = create_notebook_runtime_template(notebook_service_client)<br>assign_runtime(runtime_template_ref, notebook_service_client)</pre><p>One of the things I would love to do is to abstract this even more and integrate runtime template and runtime creation and code execution:</p><pre>from google.colab import compute<br><br>runtime = compute.Runtime.assign(&quot;my-runtime&quot;, default=True)<br>p = runtime.exec(&quot;python&quot;, &quot;-c&quot;, &quot;print(123)&quot;, timeout=5)<br>print(p.stdout.read())<br><br># Long running cell<br>p = runtime.exec(&quot;bash&quot;, &quot;-c&quot;, &quot;for i in {1..10}; do date +%T; sleep 1; done&quot;, timeout=15)<br>for line in p.stdout:<br>    print(line, end=&quot;&quot;)<br><br>runtime.stop()</pre><p>This can simplify the developer experience, but also allow provide users to define parameters to templates if they want to do so.</p><pre><br>from google.colab import compute<br><br>runtime_template_ref = compute.RuntimeTemplate.create(&quot;my-runtime-template&quot;, <br>   machine_type=&quot;g2-standard-4&quot;, <br>   idle_timeout=&quot;10800s&quot;, <br>   euc=False, <br>   python_version=&quot;3.12&quot;)</pre><p>Would you be interested in using this new interface and a programatic code execution functionality?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=33c27fafba96" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Colab Enterprise on managed GKE (VRP)]]></title>
            <link>https://medium.com/@gogasca_/colab-enterprise-on-managed-gke-vrp-37305ab3bb27?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/37305ab3bb27</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Fri, 21 Feb 2025 00:35:56 GMT</pubDate>
            <atom:updated>2025-02-21T00:38:13.542Z</atom:updated>
            <content:encoded><![CDATA[<p>Experience faster startup times with our new infrastructure based on GKE, where new <strong>default Runtimes are now accessible in under 30 seconds!</strong></p><p>Vertex Runtime Platform (VRP) is our new <strong>GKE</strong>-based system running in Google Managed Tenant Projects. This infrastructure enables customization options and faster default runtimes allocation. VRP maintains full compatibility with all Colab Enterprise features, including Idle Shutdown, End User Credentials and Library preservation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bMpKUOT5SVAlTec52sVKhA.gif" /></figure><p>This is available in private preview for external customers.</p><p>Please send me an email so I can whitelist your project and start testing!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=37305ab3bb27" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Colab Enterprise support for Python 3.11]]></title>
            <link>https://medium.com/@gogasca_/colab-enterprise-support-for-python-3-11-17132fba8afc?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/17132fba8afc</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Sat, 15 Feb 2025 07:56:56 GMT</pubDate>
            <atom:updated>2025-02-15T07:56:56.523Z</atom:updated>
            <content:encoded><![CDATA[<p>With the recent addition of Python 3.11 from Research Colab</p><p><a href="https://medium.com/google-colab/colab-updated-to-python-3-11-00197d6172b1">https://medium.com/google-colab/colab-updated-to-python-3-11-00197d6172b1</a> Colab Enterprise team also has made Python 3.11 available in Private Preview for default Runtimes. If you are interested send me an email and I will whitelist your project.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=17132fba8afc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to connect from VSCode to Vertex Notebooks directly]]></title>
            <link>https://medium.com/@gogasca_/how-to-connect-from-vscode-to-vertex-notebooks-directly-8c078d538b0b?source=rss-53e6ac1fe11e------2</link>
            <guid isPermaLink="false">https://medium.com/p/8c078d538b0b</guid>
            <dc:creator><![CDATA[Gonzalo Gasca Meza]]></dc:creator>
            <pubDate>Thu, 16 May 2024 17:01:34 GMT</pubDate>
            <atom:updated>2025-01-30T17:42:50.563Z</atom:updated>
            <content:encoded><![CDATA[<p>I have seen many tutorials on how to connect from VScode using Jupyter extension to Vertex Notebooks. The mainstream solution is mainly opening an SSH tunnel, which works, but not available in all scenarios: No Public IP, Organization restricts SSH access via Firewall rules or OS Login. Please take a look at this alternative solution using <a href="https://jupyter-enterprise-gateway.readthedocs.io/en/latest/users/client-config.html">Jupyter Gateway Client.</a></p><blockquote>Note: This is not supported by GCP, if you encounter any issues ping me here or via StackOverflow.</blockquote><p>1. Install JupyterLab in your local workstation.</p><pre>pip install jupyter-lab</pre><p>2. Create a new Jupyter config file `jupyter_notebook_config.py`</p><p>Modify PROXY_URL with your Proxy URL. This can be obtained by clicking Open JupyterLab in Google Cloud Console.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uo_K6RzULoaDyyzRj7oNDw.png" /></figure><pre>&quot;&quot;&quot;VScode support for Workbench Instances.&quot;&quot;&quot;<br>import datetime<br>import subprocess<br>import hashlib<br>import json<br><br>PROXY_URL = &quot;https://&lt;Your endpoint&gt;-dot-us-central1.notebooks.googleusercontent.com&quot;<br><br>c.NotebookApp.open_browser = False<br>c.ServerApp.token = &quot;&quot;<br>c.ServerApp.password = &quot;&quot;<br>c.ServerApp.port = 8080<br>c.ServerApp.allow_remote_access = True<br>c.ServerApp.disable_check_xsrf = True<br><br>def get_gcloud_token():<br>  &quot;&quot;&quot;Helper method to get an OAuth token from gcloud.&quot;&quot;&quot;<br>  p = subprocess.run(<br>      [&quot;gcloud&quot;, &quot;auth&quot;, &quot;print-access-token&quot;],<br>      capture_output=True,<br>      check=True,<br>      encoding=&quot;UTF-8&quot;,<br>  )<br>  bearer_token = p.stdout.strip()<br>  return bearer_token<br><br><br>c.GatewayClient.auth_scheme = &quot;Bearer&quot;<br>c.GatewayClient.auth_token = get_gcloud_token()<br>c.GatewayClient.headers = json.dumps({&quot;Origin&quot;: PROXY_URL, &quot;Cookie&quot;: &quot;_xsrf=XSRF&quot;, &quot;X-XSRFToken&quot;: &quot;XSRF&quot;})<br>c.GatewayClient.validate_cert = False<br>c.GatewayClient.url = PROXY_URL<br>c.Application.log_level = 0</pre><p>3. Start JupyterLab locally</p><pre>jupyter lab --port-retries=0 --ip 0.0.0.0 --allow-root --config=jupyter_notebook_config.py --debug</pre><p>4. Start VSCode with JupyterLab extension pre-installed and connnect to the local Jupyter server. (`http://127.0.0.1:8080`). Local Jupyter server via Gateway client will connect to remote server and you should be able to access remote Vertex instance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ORiDjubw0ohZqtCsbNajCQ.png" /></figure><p>5. Verify you are connected to remote instance.</p><p>The next step is to add support to refresh token via `JUPYTER_GATEWAY_TOKEN_RENEWER_CLASS` Reference: <a href="https://github.com/jupyter-server/jupyter_server/blob/main/jupyter_server/gateway/gateway_client.py#L51">https://github.com/jupyter-server/jupyter_server/blob/main/jupyter_server/gateway/gateway_client.py#L51</a></p><p>For Colab Enterprise, you can use same configuration. (Solved here: <a href="https://github.com/jupyter-server/jupyter_server/pull/1440">https://github.com/jupyter-server/jupyter_server/pull/1440</a>)</p><p>Video here: <a href="https://www.youtube.com/watch?v=8o0Jo6KfuAU">https://www.youtube.com/watch?v=8o0Jo6KfuAU</a></p><p><strong>Troubleshooting</strong></p><ol><li>Create a new Virtual environment</li><li>Install latest version of JupyterLab and Jupyter Server (jupyter_server)</li><li>Check Websockets connections are not blocked or additional libraries are not overwritting the original version in JupyterLab.</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8c078d538b0b" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>