21

I have am trying to follow this tutorial and set up a postgresql container.

I have the following script:

#!/bin/bash
# wait-for-postgres.sh

set -e

host="$1"
shift
cmd="$@"

until psql -h "$host" -U "postgres" -c '\l'; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"
exec $cmd

And the following docker-compose.yml:

version: '2'
services:
  server:
    build: .
    ports:
      - 3030:3030
    depends_on:
      - database
    command: ["./setup/wait-for-postgres.sh", "localhost:5432", "--", "node", "src"]
  database:
    image: postgres
    environment:
      - "POSTGRES_USER=postgres"
      - "POSTGRES_PASSWORD=postgres"
      - "POSTGRES_DB=tide_server"
    ports:
      - 5432:5432

The problem is that when I run docker-compose up I get the following error:

server_1    | Postgres is unavailable - sleeping
server_1    | psql: could not translate host name "192.168.64.2:5432" to address: Name or servi
ce not known
server_1    | Postgres is unavailable - sleeping
server_1    | psql: could not translate host name "192.168.64.2:5432" to address: Name or servi
ce not known
server_1    | Postgres is unavailable - sleeping
server_1    | psql: could not translate host name "192.168.64.2:5432" to address: Name or servi
ce not known

Now I have tried setting the host as database, localhost, 0.0.0.0, and even the containers IP but nothing works, I have no idea what it should be or how to debug it, I am not 100% sure how docker-compose links the containers.

Otis Wright
  • 1,980
  • 8
  • 31
  • 53
  • Passing in the command argument like `host:port` is a feature of the wait-for-it.sh script found here: https://github.com/vishnubob/wait-for-it/blob/master/wait-for-it.sh#L74 it's not something psql splits out for you, psql expects you to pass a port to the `-p` option. That said, 5432 is the default so not compulsory. In your example you've used `database` as the host so within the docker compose network that name should resolve. The errors posted above all show the usage of "192.168.64.2:5432", what happens if you use "database" ? I suspect psql will fail waiting for a password. – Davos Nov 20 '19 at 14:56
  • Yes it will fail with `psql: fe_sendauth: no password supplied`, I've provided a full solution in an answer. – Davos Nov 20 '19 at 16:31
  • 1
    If you use 5433:5432 it won't expose 5433 port, and you will get connection refused. As soon as you change it to "5433:5432" it will work. Tested with the official postgres:13.2 image(13 as well). – John Mar 08 '21 at 23:22

4 Answers4

4

do not use depends_on. try it with "links"

    version: '2'
    services:
      server:
        build: .
        ports:
          - 3030:3030
        links:
          - database
        #environment could be usefull too
        environment:
            DATABASE_HOST: database
        command: ["./setup/wait-for-postgres.sh", "localhost:5432", "--", "node", "src"]
      database:
        image: postgres
        environment:
          - "POSTGRES_USER=postgres"
          - "POSTGRES_PASSWORD=postgres"
          - "POSTGRES_DB=tide_server"
        ports:
          - 5432:5432

for more informations https://docs.docker.com/compose/compose-file/#links

Gabbax0r
  • 1,696
  • 3
  • 18
  • 31
  • Thank you for that, I have tried links and depends_on and both and still had no luck, cheers. – Otis Wright May 16 '17 at 23:49
  • 2
    `links` is deprecated https://docs.docker.com/compose/compose-file/#links , the advice is to use user defined networks instead. `depends_on` is fine, but it doesn't wait for the db to be ready, only for the container to have started https://docs.docker.com/compose/compose-file/#depends_on. It is intentional as per the docs "tutorial" link provided by the OP in this question, which is why they suggest `wait-for-it` and other ways to explicitly handle robust connection and reconnection from your application, not the container infrastructure. – Davos Nov 20 '19 at 16:30
3

May be an old thread to answer but I have been using depends_on with the following docker-compose file

version: '3.4'

volumes:
  postgres_data:
      driver: local

services:
  postgres:
      image: postgres
      volumes:
        - ./postgres_data:/var/lib/postgresql:rw
        - ./deployments:/opt/jboss/wildfly/standalone/deployments:rw
      environment:
        POSTGRES_DB: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: password
      ports:
        - 5432:5432
   keycloak:
      image: jboss/keycloak
      environment:
        POSTGRES_ADDR: postgres
        POSTGRES_DATABASE: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: password
        KEYCLOAK_USER: admin
        KEYCLOAK_PASSWORD: Pa55w0rd
      ports:
        - 8080:8080
        - 9990:9990
      depends_on:
        - postgres
Rodel
  • 147
  • 1
  • 6
  • This appears to be a reply/comment on someone else's answer, not an answer to the OP's question in itself. Probably worth combining this (useful) point with your other answer. – Davos Nov 20 '19 at 16:33
3

The tutorial skips over a few things, and is confusing in that it mentions the wait-for-it.sh script, but then shows a much simplified version that doesn't work if you pass hostname:port as one argument to it.

I had a crack at getting this to work and both for future me and others I will add the steps below. I did this on MacOS, and have both docker and docker-compose installed as well as nodejs.

I don't have your node app handy so I used the one as described here https://nodejs.org/de/docs/guides/nodejs-docker-webapp/

I have the following directory structure:

/src/package.json
/src/server.js
/.pgpass
/docker-compose.yml
/Dockerfile
/wait-for-postgres.sh

The contents of these files is listed below.

Steps

  1. From the ./src directory run $ npm install (creates package-lock.json)
  2. Fix pgpass permissions with $ chmod 600 .pgpass
  3. Make the script executable $ chmod +x wait-for-postgres.sh
  4. From the root directory $ docker-compose up

It will pull the postgres image and build the node app container. When that's done it will wait for postgres and when postgres is up you'll see it ready.

Files

The src files are exactly as per the node js dockerize link above

/src/package.json

{
    "name": "docker_web_app",
    "version": "1.0.0",
    "description": "Node.js on Docker",
    "author": "First Last <first.last@example.com>",
    "main": "server.js",
    "scripts": {
      "start": "node server.js"
    },
    "dependencies": {
      "express": "^4.16.1"
    }
  }

/src/server.js

'use strict';

const express = require('express');

// Constants
const PORT = 8080;
const HOST = '0.0.0.0';

// App
const app = express();
app.get('/', (req, res) => {
  res.send('Hello world\n');
});

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

.pgpass

This uses the username:password postgres:postgres and is purely for development demo purposes. In the wild you will use some other method of secrets management and never ever commit a pgpass file to version control

#host:port:db:user:pass
db:5432:*:postgres:postgres

docker-compose.yml

  • I have added the wait-for-postgres.sh script as a managed volume, in the original question it was bundling it in with the app src which was weird.
  • I have also mounted the .pgpass file in the root user's home directory, which psql will look in for auto-password completion. If you don't have some method of supplying this then you'll get an error:

    psql: fe_sendauth: no password supplied

  • Notice the command for the server container is referring to database which is a valid docker-compose internal dns name for the postgres container.

version: '2'
services:

  server:
    build: .
    ports:
      - 3030:3030
    depends_on:
      - database
    volumes:
      - ./wait-for-postgres.sh:/usr/app/setup/wait-for-postgres.sh
      - ./.pgpass:/Users/root/.pgpass
    command: ["/usr/app/setup/wait-for-postgres.sh", "database", "--", "node", "src"]

  database:
    image: postgres
    environment:
      - "POSTGRES_USER=postgres"
      - "POSTGRES_PASSWORD=postgres"
      - "POSTGRES_DB=tide_server"
    ports:
      - 5432:5432

Dockerfile

  • I have modified this from the node js tutorial, pinning it to the Debian "buster" version and also installing psql which it needs for that script.
FROM node:10-buster

RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8

RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \
    wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -


RUN apt-get -y update - && \
    apt-get -y install libpq-dev && \
    apt-get -y install postgresql-client-11

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install
# If you are building your code for production
# RUN npm ci --only=production

# Bundle app source
COPY . .

EXPOSE 8080
CMD [ "node", "server.js" ]

wait-for-postgres.sh

  • I have modified the script very slightly because I ran the "shellcheck" linter and it complained about a few things. I realise this script is from the docker tutorial page.
#!/bin/bash
# wait-for-postgres.sh

set -e

host="$1"
shift
cmd="$*"

export PGPASSFILE=./pgpass

until psql -h "$host" -U "postgres" -c '\l'; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"
exec "$cmd"
Davos
  • 5,066
  • 42
  • 66
2

The problem here is the host itself.

psql -h **"$host"** -U "<USER>" -c '\l'

You are passing a wrong HOSTNAME "localhost:5432" / "192.168.64.2:5432"

What I did is setup a ~/.pgpass that has

localhost:5432:DB:USER:PASSWORD

and instead of passing "localhost:5432", omit the port. Just use "localhost"

This works for me ...

snoob dogg
  • 2,491
  • 3
  • 31
  • 54
Rodel
  • 147
  • 1
  • 6