docker-compose, postgres and connection refused!

One of the biggest benefits of using docker for me has been the fact that it makes developing code with databases much simpler.

Whenever I’m building an app, I tend to use postgres as the database. Pre-docker, this meant installing postgres locally on my machine, running the server, creating the database, adding the required users and persmissions and finally running the app locally to test and develop. I’ve spent hours going through this.

Post-docker, things became much much simpler, especially with docker-compose. With docker-compose now I can have a postgres server up and running and configured as I want within 5-10min and I don’t have to worry about installing it and running it on my machine anymore. This is was magical. I could now focus spending my time on more important things. Like looking at cat gifs.

cat gif

Today however, wasn’t one of those days :(

Scouring the internet for the initial config to go on, I wrote my docker-compose.yml as shown.

services:
  postgres:
    image: postgres
    restart: always
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_PASSWORD=pass
      - POSTGRES_USER=user
      - POSTGRES_DB=db
  server:
    build: ./
    ports:
      - "8000:8000"
    depends_on:
      - postgres
    networks:
      - default

And used the following string for the postgres connection in the app

postgres://user:pass@127.0.0.1:5432/db?sslmode=disable

Only to be met with the unexpected error, when running both the containers

server_1    | 2020/12/13 19:42:16 couldn't get driver: failed to connect to `host=127.0.0.1 user=postgres database=postgres`: dial error (dial tcp 127.0.0.1:5432: connect: connection refused)
server_1    | exit status 1

And while I’ve done this a couple of times before I couldn’t wrap my head around why this error was popping up.

Debug time

Step 1: Typo

Without much resolute my first instinct was to check for typos, after cross-verifying, I found everything to be ok.

Step 2: Brute force

Not particularly proud of this, but I did try swapping 127.0.0.1 with localhost to get the same error. Hint: I was spot on as to where the error was, but just totally wrong about how to go about it.

Step 3: Bash

This I like. To diagnose the problem, I ran ❯ docker-compose exec server bash. This dropped me right into the shell of the server app. From here these are the following debugging steps I followed

  1. Install psql client
  2. Try to manually connect using the psql client. This failed.
  3. Check if the port is being used using netstat. No, it wasn’t.
  4. Realize that this is a network issue. Ahaa!

Docker Compose Network

Putting it shortly, docker compose isolates each of the containers network. This is done by adding them to the <myapp>_default which is a newly created network, where <myapp> is the name of the directory. Under this each of the containers is added via it’s name. In our case that would be postgres and server. These are like DNS A-records, which means using postgres would resolve to the IP of the postgres container and so on. Each of the container can use these names to connect to the other containers.

Solution

Now that we know the exact problem, all we need to do is change our host in the postgres connection string to postgres from 127.0.0.1.

postgres://user:pass@postgres:5432/db?sslmode=disable

Alternate Solution

Another way is to add network_mode: bridge to each container to put them all under the same network. While this is a topic in itself, this bridges the network between the two containers so that they co-exist on the same network.