Thicket data repository for the EEG
1{
2 "id": "https://www.tunbury.org/2025/06/14/windows-containerd-2",
3 "title": "Containerd on Windows",
4 "link": "https://www.tunbury.org/2025/06/14/windows-containerd-2/",
5 "updated": "2025-06-14T12:00:00",
6 "published": "2025-06-14T12:00:00",
7 "summary": "If you were following along with my previous post on containerd on Windows, you may recall that I lamented the lack of an installer. Since then, I have found a PowerShell script on Microsoft’s GitHub, which does a lot of the grunt work for us.",
8 "content": "<p>If you were following along with my previous post on <a href=\"https://www.tunbury.org/windows-containerd/\">containerd on Windows</a>, you may recall that I lamented the lack of an installer. Since then, I have found a PowerShell <a href=\"https://github.com/microsoft/Windows-Containers/blob/Main/helpful_tools/Install-ContainerdRuntime/install-containerd-runtime.ps1\">script</a> on Microsoft’s GitHub, which does a lot of the grunt work for us.</p>\n\n<p>Trying anything beyond my <code>echo Hello</code> test showed an immediate problem: there is no network. <code>ipconfig</code> didn’t display any network interfaces.</p>\n\n<pre><code>C:\\>ctr run --rm mcr.microsoft.com/windows/nanoserver:ltsc2022 my-container ipconfig\n\nWindows IP Configuration\n</code></pre>\n\n<p>Checking the command line options, there is one called <code>--net-host</code>, which sounded promising, only for that to be immediately dashed:</p>\n\n<pre><code>C:\\>ctr run --rm --net-host mcr.microsoft.com/windows/nanoserver:ltsc2022 my-container ipconfig\nctr: Cannot use host mode networking with Windows containers\n</code></pre>\n\n<p>The solution is <code>--cni</code>, but more work is required to get that working. We need to download the plugins and populate them in the <code>cni/bin</code> subdirectory. Fortunately, the installation script does all of this for us but leaves it unconfigured.</p>\n\n<pre><code>C:\\Windows\\System32>ctr run --rm --cni mcr.microsoft.com/windows/nanoserver:ltsc2022 my-container ipconfig\nctr: no network config found in C:\\Program Files\\containerd\\cni\\conf: cni plugin not initialized\n</code></pre>\n\n<p>From the top, this is how you get from a fresh install of Windows 11, to a container with networking. Firstly, use installation script to install <code>containerd</code>.</p>\n\n<pre><code>curl.exe https://raw.githubusercontent.com/microsoft/Windows-Containers/refs/heads/Main/helpful_tools/Install-ContainerdRuntime/install-containerd-runtime.ps1 -o install-containerd-runtime.ps1\nSet-ExecutionPolicy Bypass\n.\\install-containerd-runtime.ps1 -ContainerDVersion 2.1.1 -WinCNIVersion 0.3.1 -ExternalNetAdapter Ethernet\n</code></pre>\n\n<p>Now create <code>C:\\Program Files\\containerd\\cni\\conf\\0-containerd-nat.conf</code> containing the following:</p>\n\n<div><div><pre><code>{\n \"cniVersion\": \"0.3.0\",\n \"name\": \"nat\",\n \"type\": \"nat\",\n \"master\": \"Ethernet\",\n \"ipam\": {\n \"subnet\": \"172.20.0.0/16\",\n \"routes\": [\n {\n \"gateway\": \"172.20.0.1\"\n }\n ]\n },\n \"capabilities\": {\n \"portMappings\": true,\n \"dns\": true\n }\n}\n</code></pre></div></div>\n\n<p>Easy when you know how…</p>\n\n<pre><code>C:\\>ctr run --rm --cni mcr.microsoft.com/windows/nanoserver:ltsc2022 my-container ping 1.1.1.1\n\nPinging 1.1.1.1 with 32 bytes of data:\nReply from 1.1.1.1: bytes=32 time=5ms TTL=58\nReply from 1.1.1.1: bytes=32 time=7ms TTL=58\nReply from 1.1.1.1: bytes=32 time=7ms TTL=58\nReply from 1.1.1.1: bytes=32 time=6ms TTL=58\n\nPing statistics for 1.1.1.1:\n Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),\nApproximate round trip times in milli-seconds:\n Minimum = 5ms, Maximum = 7ms, Average = 6ms\n</code></pre>\n\n<p>The next challenge is, what do you put in your own <code>config.json</code> to reproduce this behaviour?</p>\n\n<p>Firstly, we need our <code>layerFolders</code>:</p>\n\n<pre><code>C:\\>ctr snapshot ls\nKEY PARENT KIND\nsha256:44b913d145adda5364b5465664644b11282ed3c4b9bd9739aa17832ee4b2b355 Committed\n</code></pre>\n\n<pre><code>C:\\>ctr snapshot prepare --mounts my-snapshot sha256:44b913d145adda5364b5465664644b11282ed3c4b9bd9739aa17832ee4b2b355\n[\n {\n \"Type\": \"windows-layer\",\n \"Source\": \"C:\\\\ProgramData\\\\containerd\\\\root\\\\io.containerd.snapshotter.v1.windows\\\\snapshots\\\\14\",\n \"Target\": \"\",\n \"Options\": [\n \"rw\",\n \"parentLayerPaths=[\\\"C:\\\\\\\\ProgramData\\\\\\\\containerd\\\\\\\\root\\\\\\\\io.containerd.snapshotter.v1.windows\\\\\\\\snapshots\\\\\\\\1\\\"]\"\n ]\n }\n]\n</code></pre>\n\n<p>Let’s create a <code>config.json</code> without a network stanza just to check we can create a container:</p>\n\n<div><div><pre><code>{\n \"ociVersion\": \"1.1.0\",\n \"process\": {\n \"terminal\": false,\n \"user\": { \"uid\": 0, \"gid\": 0 },\n \"args\": [\n \"cmd\", \"/c\",\n \"ipconfig && ping 1.1.1.1\"\n ],\n \"cwd\": \"c:\\\\\"\n },\n \"root\": { \"path\": \"\", \"readonly\": false },\n \"hostname\": \"builder\",\n \"windows\": {\n \"layerFolders\": [\n \"C:\\\\ProgramData\\\\containerd\\\\root\\\\io.containerd.snapshotter.v1.windows\\\\snapshots\\\\1\",\n \"C:\\\\ProgramData\\\\containerd\\\\root\\\\io.containerd.snapshotter.v1.windows\\\\snapshots\\\\14\"\n ],\n \"ignoreFlushesDuringBoot\": true\n }\n}\n</code></pre></div></div>\n\n<p>The container runs, but there is no network as we’d expect.</p>\n\n<pre><code>C:\\>ctr run --rm --config config.json my-container\n\nWindows IP Configuration\n\n\nPinging 1.1.1.1 with 32 bytes of data:\nPING: transmit failed. General failure.\nPING: transmit failed. General failure.\nPING: transmit failed. General failure.\nPING: transmit failed. General failure.\n</code></pre>\n\n<p>If we turn on CNI, it crypically tells us what we need to do:</p>\n\n<pre><code>C:\\>ctr run --rm --cni --config config.json my-container\nctr: plugin type=\"nat\" name=\"nat\" failed (add): required env variables [CNI_NETNS] missing\n</code></pre>\n\n<p>So we need to populate the <code>network.networkNamespace</code> with the name (ID) of the network we want to use. This should be a GUID, and I don’t know how to get the right value. I would have assumed that it was one of the many GUID’s returned by <code>Get-HnsNetwork</code> but it isn’t.</p>\n\n<div><div><pre><code><span>PS</span><span> </span><span>C:\\</span><span>></span><span> </span><span>Get-HnsNetwork</span><span>\n\n\n</span><span>ActivityId</span><span> </span><span>:</span><span> </span><span>92018CF0-6DCB-4AAF-A14E-DC61120FC958</span><span>\n</span><span>AdditionalParams</span><span> </span><span>:</span><span>\n</span><span>CurrentEndpointCount</span><span> </span><span>:</span><span> </span><span>0</span><span>\n</span><span>Extensions</span><span> </span><span>:</span><span> </span><span>{@{</span><span>Id</span><span>=</span><span>E7C3B2F0</span><span>-</span><span>F3C5</span><span>-</span><span>48</span><span>DF</span><span>-</span><span>AF2B</span><span>-</span><span>10</span><span>FED6D72E7A</span><span>;</span><span> </span><span>IsEnabled</span><span>=</span><span>False</span><span>;</span><span> </span><span>Name</span><span>=</span><span>Microsoft</span><span> </span><span>Windows</span><span> </span><span>Filtering</span><span> </span><span>Platform</span><span>},</span><span>\n </span><span>@{</span><span>Id</span><span>=</span><span>F74F241B</span><span>-</span><span>440</span><span>F</span><span>-</span><span>4433</span><span>-</span><span>BB28</span><span>-</span><span>00</span><span>F89EAD20D8</span><span>;</span><span> </span><span>IsEnabled</span><span>=</span><span>False</span><span>;</span><span> </span><span>Name</span><span>=</span><span>Microsoft</span><span> </span><span>Azure</span><span> </span><span>VFP</span><span> </span><span>Switch</span><span> </span><span>Filter</span><span> </span><span>Extension</span><span>},</span><span>\n </span><span>@{</span><span>Id</span><span>=</span><span>430</span><span>BDADD</span><span>-</span><span>BAB0</span><span>-</span><span>41</span><span>AB</span><span>-</span><span>A369</span><span>-</span><span>94</span><span>B67FA5BE0A</span><span>;</span><span> </span><span>IsEnabled</span><span>=</span><span>True</span><span>;</span><span> </span><span>Name</span><span>=</span><span>Microsoft</span><span> </span><span>NDIS</span><span> </span><span>Capture</span><span>}}</span><span>\n</span><span>Flags</span><span> </span><span>:</span><span> </span><span>8</span><span>\n</span><span>Health</span><span> </span><span>:</span><span> </span><span>@{</span><span>LastErrorCode</span><span>=</span><span>0</span><span>;</span><span> </span><span>LastUpdateTime</span><span>=</span><span>133943927149605101</span><span>}</span><span>\n</span><span>ID</span><span> </span><span>:</span><span> </span><span>3EB2B18B-A1DD-46A8-A425-256F6B3DF26D</span><span>\n</span><span>IPv6</span><span> </span><span>:</span><span> </span><span>False</span><span>\n</span><span>LayeredOn</span><span> </span><span>:</span><span> </span><span>20791F67-012C-4C9B-9C93-530FDA5DE4FA</span><span>\n</span><span>MacPools</span><span> </span><span>:</span><span> </span><span>{@{</span><span>EndMacAddress</span><span>=</span><span>00</span><span>-</span><span>15</span><span>-</span><span>5</span><span>D</span><span>-</span><span>C3</span><span>-</span><span>DF</span><span>-</span><span>FF</span><span>;</span><span> </span><span>StartMacAddress</span><span>=</span><span>00</span><span>-</span><span>15</span><span>-</span><span>5</span><span>D</span><span>-</span><span>C3</span><span>-</span><span>D0</span><span>-</span><span>00</span><span>}}</span><span>\n</span><span>MaxConcurrentEndpoints</span><span> </span><span>:</span><span> </span><span>1</span><span>\n</span><span>Name</span><span> </span><span>:</span><span> </span><span>nat</span><span>\n</span><span>NatName</span><span> </span><span>:</span><span> </span><span>NATAC317D6D-8A2E-4E4E-9BCF-33435FE4CD8F</span><span>\n</span><span>Policies</span><span> </span><span>:</span><span> </span><span>{@{</span><span>Type</span><span>=</span><span>VLAN</span><span>;</span><span> </span><span>VLAN</span><span>=</span><span>1</span><span>}}</span><span>\n</span><span>State</span><span> </span><span>:</span><span> </span><span>1</span><span>\n</span><span>Subnets</span><span> </span><span>:</span><span> </span><span>{@{</span><span>AdditionalParams</span><span>=</span><span>;</span><span> </span><span>AddressPrefix</span><span>=</span><span>172.20.0.0</span><span>/</span><span>16</span><span>;</span><span> </span><span>Flags</span><span>=</span><span>0</span><span>;</span><span> </span><span>GatewayAddress</span><span>=</span><span>172.20.0.1</span><span>;</span><span> </span><span>Health</span><span>=</span><span>;</span><span>\n </span><span>ID</span><span>=</span><span>5</span><span>D56CE8D</span><span>-</span><span>1</span><span>AD2</span><span>-</span><span>47</span><span>FF</span><span>-</span><span>85</span><span>A7</span><span>-</span><span>A0E6D530565D</span><span>;</span><span> </span><span>IpSubnets</span><span>=</span><span>System</span><span>.</span><span>Object</span><span>[];</span><span> </span><span>ObjectType</span><span>=</span><span>5</span><span>;</span><span> </span><span>Policies</span><span>=</span><span>System</span><span>.</span><span>Object</span><span>[];</span><span> </span><span>State</span><span>=</span><span>0</span><span>}}</span><span>\n</span><span>SwitchGuid</span><span> </span><span>:</span><span> </span><span>3EB2B18B-A1DD-46A8-A425-256F6B3DF26D</span><span>\n</span><span>TotalEndpoints</span><span> </span><span>:</span><span> </span><span>2</span><span>\n</span><span>Type</span><span> </span><span>:</span><span> </span><span>NAT</span><span>\n</span><span>Version</span><span> </span><span>:</span><span> </span><span>64424509440</span><span>\n</span><span>Resources</span><span> </span><span>:</span><span> </span><span>@{</span><span>AdditionalParams</span><span>=</span><span>;</span><span> </span><span>AllocationOrder</span><span>=</span><span>2</span><span>;</span><span> </span><span>Allocators</span><span>=</span><span>System</span><span>.</span><span>Object</span><span>[];</span><span> </span><span>CompartmentOperationTime</span><span>=</span><span>0</span><span>;</span><span> </span><span>Flags</span><span>=</span><span>0</span><span>;</span><span> </span><span>Health</span><span>=</span><span>;</span><span>\n </span><span>ID</span><span>=</span><span>92018</span><span>CF0</span><span>-</span><span>6</span><span>DCB</span><span>-</span><span>4</span><span>AAF</span><span>-</span><span>A14E</span><span>-</span><span>DC61120FC958</span><span>;</span><span> </span><span>PortOperationTime</span><span>=</span><span>0</span><span>;</span><span> </span><span>State</span><span>=</span><span>1</span><span>;</span><span> </span><span>SwitchOperationTime</span><span>=</span><span>0</span><span>;</span><span> </span><span>VfpOperationTime</span><span>=</span><span>0</span><span>;</span><span>\n </span><span>parentId</span><span>=</span><span>71</span><span>FB2758</span><span>-</span><span>F714</span><span>-</span><span>4838</span><span>-</span><span>8764</span><span>-</span><span>7079378</span><span>D6CB6</span><span>}</span><span>\n</span></code></pre></div></div>\n\n<p>I ran <code>ctr run --rm --cni mcr.microsoft.com/windows/nanoserver:ltsc2022 my-container cmd /c \"ping 1.1.1.1 && pause\"</code> in one window and ran <code>ctr c info my-container</code> in another, which revealed a GUID was <code>5f7d467c-3011-48bc-9337-ce78cf399345</code>.</p>\n\n<p>Adding this to my <code>config.json</code></p>\n\n<div><div><pre><code>{\n \"ociVersion\": \"1.1.0\",\n \"process\": {\n \"terminal\": false,\n \"user\": { \"uid\": 0, \"gid\": 0 },\n \"args\": [\n \"cmd\", \"/c\",\n \"ipconfig && ping 1.1.1.1\"\n ],\n \"cwd\": \"c:\\\\\"\n },\n \"root\": { \"path\": \"\", \"readonly\": false },\n \"hostname\": \"builder\",\n \"windows\": {\n \"layerFolders\": [\n \"C:\\\\ProgramData\\\\containerd\\\\root\\\\io.containerd.snapshotter.v1.windows\\\\snapshots\\\\1\",\n \"C:\\\\ProgramData\\\\containerd\\\\root\\\\io.containerd.snapshotter.v1.windows\\\\snapshots\\\\14\"\n ],\n \"ignoreFlushesDuringBoot\": true,\n \"network\": {\n \"allowUnqualifiedDNSQuery\": true,\n \"networkNamespace\": \"5f7d467c-3011-48bc-9337-ce78cf399345\"\n }\n }\n}\n</code></pre></div></div>\n\n<p>And now I have a network!</p>\n\n<pre><code>C:\\>ctr run --rm --cni --config config.json my-container\n\nWindows IP Configuration\n\n\nEthernet adapter vEthernet (default-my-container2_nat):\n\n Connection-specific DNS Suffix . : Home\n Link-local IPv6 Address . . . . . : fe80::921d:1ce7:a445:8dfa%49\n IPv4 Address. . . . . . . . . . . : 172.20.95.58\n Subnet Mask . . . . . . . . . . . : 255.255.0.0\n Default Gateway . . . . . . . . . : 172.20.0.1\n\nPinging 1.1.1.1 with 32 bytes of data:\nReply from 1.1.1.1: bytes=32 time=5ms TTL=58\nReply from 1.1.1.1: bytes=32 time=6ms TTL=58\nReply from 1.1.1.1: bytes=32 time=6ms TTL=58\nReply from 1.1.1.1: bytes=32 time=6ms TTL=58\n\nPing statistics for 1.1.1.1:\n Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),\nApproximate round trip times in milli-seconds:\n Minimum = 5ms, Maximum = 6ms, Average = 5ms\n</code></pre>",
9 "content_type": "html",
10 "author": {
11 "name": "Mark Elvers",
12 "email": "mark.elvers@tunbury.org",
13 "uri": null
14 },
15 "categories": [
16 "containerd",
17 "tunbury.org"
18 ],
19 "source": "https://www.tunbury.org/atom.xml"
20}