How can I connect to a postgres database in a bridge docker network from a nix flakes devshell?

I’m trying to gradually migrate from using docker for development to using nix flakes devshells.

One problem I have is I can’t immediately replace our custom postgres docker container with a nix alternative and I have to be able to connect to it from the devshell.

It would be fine if the port were exposed to the host directly, but instead it’s using it’s own custom bridged network with a number of services.

I need to figure out how to I guess “bridge” or patch the devshell into the bridged network created by docker but I don’t know a lot about networking honestly.

Here’s a flake/devshell that demonstrates the failing version I want to make work when you run nix develop.

.nix
{
  description = "A very basic flake";

  outputs = { self, nixpkgs }:

    let
      pkgs = import nixpkgs { system = "x86_64-linux"; };
    in
    {
      devShells.x86_64-linux.default = pkgs.mkShell {
        packages = [
          pkgs.postgresql
          pkgs.docker
        ];
        # Note that `shellHook` still uses bash syntax.
        # This starts fish, then exists the bash shell when fish exits.
        shellHook = ''
        docker stop $(docker ps -q)
        docker network rm my-bridge-network
        docker network create -d bridge my-bridge-network
        docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
        
        echo "gateway of container:"
        gatewayIp=$(docker inspect --format '{{ (index (.IPAM.Config) 0).Gateway }}' my-bridge-network)
        echo "gateway ip is: $gatewayIp"
        
        psql -U postgres -h $gatewayIp

        '';
      };
    };
    
}

I’m continuing to try and figure this out after posting this, so will post back with any updates.

Thanks in advance for any help!

I figured out how to do it by getting the container IP:

{
  description = "A very basic flake";

  outputs = { self, nixpkgs }:

    let
      pkgs = import nixpkgs { system = "x86_64-linux"; };
    in
    {
      devShells.x86_64-linux.default = pkgs.mkShell {
        packages = [
          pkgs.postgresql
          pkgs.docker
        ];
        # Note that `shellHook` still uses bash syntax.
        # This starts fish, then exists the bash shell when fish exits.
        shellHook = ''
        docker stop $(docker ps -q)
        docker rm some-postgres
        docker network rm my-bridge-network
        docker network create -d bridge my-bridge-network
        docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
        containerIp=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' some-postgres)
        echo "container IP: $containerIp"
        sleep 5
        psql postgres://postgres:mysecretpassword@$containerIp:5432 -c "select now()"

        '';
      };
    };
    
}

I suppose the next level where I won’t have to update any configuration files involves one of the methods from:

Or maybe redirecting certain ports with iptables would be most convenient…

And here’s something that I thought would work to make “hitting localhost” forward to the docker container but it doesn’t seem to be working.

Maybe existing iptable rules in nix make it not work as I expect:

{
  description = "A very basic flake";

  outputs = { self, nixpkgs }:

    let
      pkgs = import nixpkgs { system = "x86_64-linux"; };
    in
    {
      devShells.x86_64-linux.default = pkgs.mkShell {
        packages = [
          pkgs.postgresql
          pkgs.docker
        ];
        # Note that `shellHook` still uses bash syntax.
        # This starts fish, then exists the bash shell when fish exits.
        shellHook = ''
        docker stop $(docker ps -q)
        docker rm some-postgres
        docker network rm my-bridge-network
        docker network create -d bridge my-bridge-network
        docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
        containerIp=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' some-postgres)
        echo "container IP: $containerIp"
        sleep 5
        sudo iptables -t nat -A PREROUTING -p tcp --dport 5432 -j DNAT --to-destination $containerIp:5432
        sudo iptables -t nat -A POSTROUTING -j MASQUERADE
        psql postgres://postgres:mysecretpassword@localhost:5432 -c "select now()"
        '';
      };
    };
}