Search code examples
node.jspostgresqldockerdocker-composetypeorm

How do I set up docker-compose.yml, Express and TypeORM to connect my Express server to a Postgres db inside the container?


I am new to writing my own docker-compose.yml files because I previously had coworkers around to write them for me. Not the case now.

I am trying to connect an Express server with TypeORM to a Postgres database inside of Docker containers.

I see there are two separate containers running: One for the express server and another for postgres.

I expect the port 5432 to be sufficient to direct container A to connect to container B. I am wrong. It's not.

I read somewhere that 0.0.0.0 refers to the host machine's actual network, not the internal network of the containers. Hence I am trying to change 0.0.0.0 in the Postgres output to something else.

Here's what I have so far: docker-compose.yml

version: "3.1"

services:
    app:
        container_name: express_be
        build:
            context: .
            dockerfile: Dockerfile.dev
        volumes:
            - ./src:/app/src
        depends_on:
            - postgres_db
        ports:
            - "8000:8000"
        networks:
            - efinternal
    postgres_db:
        container_name: postgres_be
        image: postgres:15.1
        restart: always
        environment:
            - POSTGRES_USERNAME=postgres
            - POSTGRES_PASSWORD=postgres
        ports:
            - "5432:5432"
        volumes:
            - postgresDB:/var/lib/postgresql/data
        networks:
            - efinternal
volumes:
    postgresDB:
        driver: local
networks:
    efinternal:
        driver: bridge

Here's my console log output

ostgres_be  | PostgreSQL Database directory appears to contain a database; Skipping initialization
postgres_be  | 
postgres_be  | 2022-12-13 04:09:51.628 UTC [1] LOG:  starting PostgreSQL 15.1 (Debian 15.1-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
postgres_be  | 2022-12-13 04:09:51.628 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432 // look here
postgres_be  | 2022-12-13 04:09:51.628 UTC [1] LOG:  listening on IPv6 address "::", port 5432
postgres_be  | 2022-12-13 04:09:51.636 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
postgres_be  | 2022-12-13 04:09:51.644 UTC [28] LOG:  database system was shut down at 2022-12-13 04:09:48 UTC
postgres_be  | 2022-12-13 04:09:51.650 UTC [1] LOG:  database system is ready to accept connections
express_be   | 
express_be   | > [email protected] dev
express_be   | > nodemon ./src/server.ts
express_be   | 
express_be   | [nodemon] 2.0.20
express_be   | [nodemon] starting `ts-node ./src/server.ts`
express_be   | /task ... is running
express_be   | /committee ... is running
express_be   | App has started on port 8000
express_be   | Database connection failed Failed to create connection with database
express_be   | Error: getaddrinfo ENOTFOUND efinternal  // look here
express_be   |     at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:107:26) {
express_be   |   errno: -3008,
express_be   |   code: 'ENOTFOUND',
express_be   |   syscall: 'getaddrinfo',
express_be   |   hostname: 'efinternal'
express_be   | }

Not sure what to say about it. What I'm trying to do is to change the line " listening on IPv4 address "0.0.0.0", port 5432" to say "listening on ... efinternal, port 5432" so that "Error: getaddrinfo ENOTFOUND efinternal" will have somewhere to point to.

This link seemed to have the answer but I couldn't decipher it.

Same story here, I can't decipher what I'm doing wrong.

A helpful person told me about "external_links" which sounds like the wrong tool for the job: "Link to containers started outside this docker-compose.yml or even outside of Compose"

I suspect this is The XY Problem where I'm trying to Y (create an "efinternal" network in my containers) so I can X (connect express to postgres) when in fact Y is the wrong solution to X.

In case it matters, here's my TypeORM Postgres connection settings:

export const AppDataSource = new DataSource({
    type: "postgres",
    host: "efinternal",
    port: 5432,
    username: "postgres",
    password: "postgres",
    database: "postgres",
    synchronize: true,
    logging: true,
    entities: [User, Committee, Task, OnboardingStep, RefreshToken, PasswordToken],
    subscribers: [],
    migrations: [],
});

Solution

  • The special IPv4 address 0.0.0.0 means "all interfaces", in whichever context it's invoked in. In a Docker container you almost always want to listen to 0.0.0.0, which will listen to all "interfaces" in the isolated Docker container network environment (not the host network environment). So you don't need to change the PostgreSQL configuration here.

    If you read through Networking in Compose in the Docker documentation, you'll find that each container is addressable by its Compose service name. The network name itself is not usable as a host name.

    I'd strongly suggest making your database location configurable. Even in this setup, it will have a different hostname in your non-Docker development environment (localhost) vs. running in a container (postgres_db).

    export const AppDataSource = new DataSource({
      host: process.env.PGHOST || 'localhost',
      ...
    });
    

    Then in your Compose setup, you can specify that environment-variable setting.

    version: '3.8'
    services:
      app:
        build: .          # and name the Dockerfile just "Dockerfile"
        depends_on: [postgres_db]
        ports: ['8000:8000']
        environment:      # add
          PGHOST: postgres_db
      postgres_db: {...}
    

    Compose also provides you a network named default, and for simplicity you can delete all of the networks: blocks in the entire Compose file. You also do not need to manually specify container_name:, and I'd avoid overwriting the image's code with volumes:.

    This setup also supports using Docker for your database but plain Node for ordinary development. (Also see near-daily SO questions about live reloading not working in Docker.) You can start only the database, and since the code defaults the database location to a developer-friendly localhost, you can just develop as normal.

    docker-compose up -d postgres_db
    yarn dev