-1

I have 2 docker containers as shown below. I can connect to PostgresSQL in docker by calling Test.Main() method without exception through a net6.0 C# Console App. But when I try to call Test.Main() from ASP.NET docker container I am unable to connect to PostgresSQL and get the exception Failed to connect to 127.0.0.1:5432:

{Npgsql.NpgsqlException (0x80004005): Failed to connect to 127.0.0.1:5432
 ---> System.Net.Sockets.SocketException (111): Connection refused
   at Npgsql.Internal.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
   at Npgsql.Internal.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
   at Npgsql.Internal.NpgsqlConnector.RawOpen(SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
   at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|215_1(NpgsqlConnector conn, SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
   at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
   at Npgsql.PoolingDataSource.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
   at Npgsql.PoolingDataSource.<Get>g__RentAsync|28_0(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlConnection.<Open>g__OpenAsync|45_0(Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlConnection.Open()
   at APIDA.DBModels.Test.Main() in Version.cs:line 34}
C:\Users\HT>docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                                           NAMES
bdc65451aeda   gb3api:dev   "tail -f /dev/null"      17 minutes ago   Up 16 minutes   0.0.0.0:49160->80/tcp, 0.0.0.0:49159->443/tcp   GB3API
95b6a289f4a8   postgres     "docker-entrypoint.s…"   47 minutes ago   Up 47 minutes   0.0.0.0:5432->5432/tcp                          gb-postgres

ASP.NET DockerFile is

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["GB3API/GB3API.csproj", "GB3API/"]
RUN dotnet restore "GB3API/GB3API.csproj"
COPY . .
WORKDIR "/src/GB3API"
RUN dotnet build "GB3API.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "GB3API.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "GB3API.dll"]

Test.Main



public class Test
{
    public static void Main()
    {
        string connectionString = "Host=127.0.0.1;Database=postgres;Username=postgres;Password=xxxxx";

        // Create a new PostgreSQL connection
        using (NpgsqlConnection connection = new NpgsqlConnection(connectionString))
        {
            try
            {
                // Open the connection
                connection.Open();

                // Check if the connection is open
                if (connection.State == System.Data.ConnectionState.Open)
                {
                    Console.WriteLine("Connected to PostgreSQL!");

                    // Perform your database operations here

                    // Close the connection when you're done
                    connection.Close();
                    Console.WriteLine("Connection closed.");
                }
                else
                {
                    Console.WriteLine("Failed to open the connection.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
        }

        Console.ReadLine();
    }
}
Syed Rafey Husain
  • 153
  • 1
  • 1
  • 7
  • `127.0.0.1` is the container itself - the container itself does not run a postgres process. We need to communicate with the postgres container. For this, we need to set the postgres host to the name of the postgres container (i.e. `postgres`). – Turing85 Jul 21 '23 at 17:18
  • 1
    This is btw a perfect example of why we want the [3rd factor](https://12factor.net/config) of the [twelve factors](https://12factor.net/): Store configuration in the environment, so it can be modified in, for example, containerized applications without code changes.in the given code, we should store and load everything related to the database connection in/form the environment. – Turing85 Jul 21 '23 at 17:25
  • @Turing85, `Host=postgres` gives exception `System.Net.Internals.SocketExceptionFactory+ExtendedSocketException (00000005, 0xFFFDFFFF): Name or service not known` – Syed Rafey Husain Jul 21 '23 at 17:27
  • oh sorry. The container-name is `gb-postgres`, not `postgres`. – Turing85 Jul 21 '23 at 17:30

2 Answers2

0

As it is mentioned in the error message, you application tries to connect to localhost:

{Npgsql.NpgsqlException (0x80004005): Failed to connect to 127.0.0.1:5432
 ---> System.Net.Sockets.SocketException (111): Connection refused
   at Npgsql.Internal.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
   at Npgsql.Internal.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
   at Npgsql.Internal.NpgsqlConnector.RawOpen(SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
   at Npgsql.Internal.NpgsqlConnector.<Open>g__OpenCore|215_1(NpgsqlConnector conn, SslMode sslMode, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken, Boolean isFirstAttempt)
   at Npgsql.Internal.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
   at Npgsql.PoolingDataSource.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
   at Npgsql.PoolingDataSource.<Get>g__RentAsync|28_0(NpgsqlConnection conn, NpgsqlTimeout timeout, Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlConnection.<Open>g__OpenAsync|45_0(Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlConnection.Open()
   at APIDA.DBModels.Test.Main() in Version.cs:line 34}

The database in a different docker, which has an IP different from 127.0.0.1. You have to use the url:

gb-postgres:5432

to connect to your database, where gb-postgres is the name of the docker where postgresql is running.

Jens
  • 67,715
  • 15
  • 98
  • 113
  • 1
    @Turing85 Thanks for the hint. Good that I explained it :D – Jens Jul 21 '23 at 17:34
  • On using `gb-postgres:5432`, I am getting exception `System.Net.Internals.SocketExceptionFactory+ExtendedSocketException (00000005, 0xFFFDFFFF): Name or service not known` – Syed Rafey Husain Jul 21 '23 at 17:36
0

For app running in the container 127.0.0.1 will be the container itself which obviously does not have the database instance running. One way to handle this is to run both containers inside the same docker network and use the name resolution (i.e. Host=gb-postgres;Port=5432;), the easiest way would be by using docker-compose - see this answer.

If your host OS (the one running the docker) Windows you can use host.docker.internal to reach outside the container i.e. Host=host.docker.internal;Port=5432; (not sure about out of the box support on Linux but there are surely workarounds)

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • I can not use any of suggested solution in VS2022 debugging. Instead `172.17.0.1` worked. Why using static IP `172.17.0.1` worked? https://stackoverflow.com/questions/48546124/what-is-linux-equivalent-of-host-docker-internal/62431165#62431165. – Syed Rafey Husain Jul 22 '23 at 03:45
  • @SyedRafeyHusain 1) you should not actually hardcode connection strings and should use configuration for stuff like that (and possibly launchSettings.json) 2) that worked because, if I remember correctly, this is some default IP for default network, or something like that. – Guru Stron Jul 22 '23 at 06:13
  • `172.17.0.1` works because it is the default internal network created by docker where all containers are connected. If you want to access it through `gb-postgres` hostname, then you can create a custom bridge network and connect your app and postgres containers to that network. https://docs.docker.com/network/drivers/bridge/#manage-a-user-defined-bridge – anand Jul 22 '23 at 08:45