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:
https://stairsfordesign-com.ngontinh24.com/article/four-ways-to-connect-a-docker-container-to-a-local-network-the-odd-bit
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()"
'';
};
};
}