podmanpodman-networking

Podman container cannot connect to Windows host


TL;DR

Application running in a Podman container within WSL 2 cannot connect to a service running on the Windows host.

Details

Environment

I have got the following minimalistic setup:

The container belongs to the default bridge network.

[
     {
          "name": "podman",
          "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9",
          "driver": "bridge",
          "network_interface": "podman0",
          "created": "2024-10-17T15:56:07.008133925+02:00",
          "subnets": [
               {
                    "subnet": "10.88.0.0/16",
                    "gateway": "10.88.0.1"
               }
          ],
          "ipv6_enabled": false,
          "internal": false,
          "dns_enabled": false,
          "ipam_options": {
               "driver": "host-local"
          },
          "containers": {
               "2f61f8752f703ac1ca87109713928b7861bfe4fd09fa2f1afd24fea08d37fde0": {
                    "name": "stupefied_davinci",
                    "interfaces": {
                         "eth0": {
                              "subnets": [
                                   {
                                        "ipnet": "10.88.0.6/16",
                                        "gateway": "10.88.0.1"
                                   }
                              ],
                              "mac_address": "fe:c9:60:df:2d:14"
                         }
                    }
               }
          }
     }
]

Problem

The issue is, I cannot reach the web app running on the Windows machine from the podman container. Whenever I try to call curl http://host.containers.internal/weatherforecast (from within the container) I get the error curl: (7) Failed to connect to host.docker.internal port 80 after 0 ms: Could not connect to server.

Yet I am able to access the web app from the Windows machine without any issues. I am also able to access internet from within the Podman container.

What did not work

Sidenote

The funny thing is, the very same setup works with Docker. It is only Podman that is having some issues (for no apparent reason). Unfortunately, I have to go with Podman - company policy.


Solution

  • What seems to be the problem

    Well, it turns out, Podman is not as smart in domain name resolution as Docker. Despite Podman is supposed to route the domain host.containers.internal to the host machine, this does not seem to be automatic (at least not on Windows).

    Solutions

    There seem to be a few ways to solve the problem - I decided to go for Solution 2.

    Solution 1

    Add an extra host to the container when starting the container.

    1. Open (PowerShell) Terminal in Windows
    2. Find out the virtual IP address that is used by WSL2 to communicate with Windows by running the following command (the IP address will be needed later):
    Get-NetIpAddress | where { $_.InterfaceAlias -Like '*WSL*' -and $_.AddressFamily -EQ "IPv4" } |select -ExpandProperty IPAddress
    
    1. Start your container with an extra --add-host argument - however replace the IP address (172.22.208.1 is just an example) with the IP address from step 2:
    podman run --add-host=host.containers.internal:172.22.208.1 --rm -it alpine sh
    

    Solution 2 (preferred)

    Add an extra host to the container when starting the container but the host IP address is taken from a config file (/etc/containers/containers.conf) on the Podman Linux machine (podman-machine-default).

    1. Open (PowerShell) Terminal in Windows
    2. Find out the virtual IP address that is used by WSL2 to communicate with Windows by running the following command (the IP address will be needed later):
    Get-NetIpAddress | where { $_.InterfaceAlias -Like '*WSL*' -and $_.AddressFamily -EQ "IPv4" } |select -ExpandProperty IPAddress
    
    1. Open session on the Podman Linux machine by typing:
    wsl --distribution podman-machine-default
    
    1. Add the following lines to /etc/containers/containers.conf under the [containers] section - however replace the IP address (172.22.208.1 is just an example) with the IP address from step 2:
    host_containers_internal_ip = "172.22.208.1"
    
    1. Leave the Linux session by pressing CTRL+D twice.
    2. Restart Podman:
    podman machine stop
    podman machine start
    
    1. Start your container with an extra --add-host=host.containers.internal:host-gateway argument:
    podman run --add-host=host.containers.internal:host-gateway --rm -it alpine sh
    

    This time, the extra host points to the IP defined in host_containers_internal_ip from containers.conf.

    Solution 3

    (This solution worked for some cases but NOT always.)

    Manually adjust hosts configuration of the WSL2 instance of the Podman Linux machine (podman-machine-default). In order to do so, follow these steps:

    1. Open (PowerShell) Terminal in Windows
    2. Find out the virtual IP address that is used by WSL2 to communicate with Windows by running the following command (the IP address will be needed later):
    Get-NetIpAddress | where { $_.InterfaceAlias -Like '*WSL*' -and $_.AddressFamily -EQ "IPv4" } |select -ExpandProperty IPAddress
    
    1. Open session on the Podman Linux machine by typing:
    wsl --distribution podman-machine-default
    
    1. Add the following lines to /etc/wsl.conf so the /etc/hosts file won't be auto-generated by Windows anymore:
    [network]
    generateHosts = false
    
    1. Add the following line to /etc/hosts - however replace the IP address (172.22.208.1 is just an example) with the IP address from step 2:
    172.22.208.1 host.containers.internal
    
    1. Restart your Podman container

    Result

    Now it is possible to call services running on the Windows machine from container by using the domain name host.containers.internal.

    .NET debug in Visual Studio

    Visual Studio uses some clever tricks to make debugging of containerized .NET applications fast. Also Visual Studio starts the container automatically when debugging. Therefore we need to set up MSBuild so it uses the extra argument --add-host=host.containers.internal:host-gateway (from Solution 2) when starting the container.

    In order to achieve that, add a property to the corresponding csproj file:

    <Project Sdk="Microsoft.NET.Sdk.Web">
        <PropertyGroup>
            <DockerfileRunArguments>--add-host=host.containers.internal:host-gateway</DockerfileRunArguments>
        </PropertyGroup>
    </Project>