This question already has answers here:
ECONNREFUSED for Postgres on nodeJS with dockers
(7 answers)
Closed 2 years ago.
I get an ECONNREFUSED when trying to connect to a postgres server in docker from a NodeJS app in docker when running both via docker-compose. However I can connect from my host machine. Here is my docker-compose.yml:
version: "2.4"
services:
api:
build:
context: .
target: dev
depends_on:
- postgres
ports:
- "8080:8080"
- "9229:9229"
networks:
- backend
environment:
- NODE_ENV=development
- PGHOST=postgres
- PGPASSWORD=12345678
- PGUSER=test
- PGDATABASE=test
- PGPORT=5433
volumes:
- .:/node/app
- /node/app/node_modules # Use empty volume to hide the node_modules from the host os
postgres:
image: postgres:11
restart: always
ports:
- "5433:5432"
networks:
- backend
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: 12345678
POSTGRES_USER: test
POSTGRES_DB: test
networks:
backend:
volumes:
db-data:
The nodeJS code:
const client = new Client({
user: process.env.PGUSER,
host: process.env.PGHOST,
database: process.env.PGDATABASE,
password: process.env.PGPASSWORD,
port: Number(process.env.PGPORT),
});
client.connect();
The error:
{ Error: connect ECONNREFUSED 172.22.0.2:5433
api_1 | at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1106:14)
api_1 | errno: 'ECONNREFUSED',
api_1 | code: 'ECONNREFUSED',
api_1 | syscall: 'connect',
api_1 | address: '172.22.0.2',
api_1 | port: 5433 }
At the same time I can connect from the host OS to the database server without any problems. Is there any problems with networking?
Edit: The dB server is ready to accept connections before the nodejs app tries that (I also tried with retrying the connection from within the nodejs app).
No, there is nothing wrong with networking. Just because you're connecting on the wrong port.
Inside compose network, your postgres container exposed 5432 port so it only accept the request via that port inside the compose network. So just need to change PGPORT=5433 to PGPORT=5432.
The reason why you can access from your host OS is because docker-compose mapped your port 5433:5432 so all request to 5433 from outside (host OS) will be pass to 5432 inside your compose network.
Hope that clear enough for you to solve the issue.
Related
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 | > efinternal#1.0.1 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: [],
});
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
I have a docker-composer.yml that is setting up two services: server and db. The Node.js server, which is the server service, uses pg to connect to the PostgreSQL database; and the db service is a PostgreSQL image.
On the server startup, it tries to connect to the database but gets a timeout.
docker-compose.yml
version: '3.8'
services:
server:
image: myapi
build: .
container_name: server
env_file: .env
environment:
- PORT=80
- DATABASE_URL=postgres://postgres:postgres#db:15432/mydb
- REDIS_URL=redis://redis
ports:
- 3000:80
depends_on:
- db
command: node script.js
restart: unless-stopped
db:
image: postgres
container_name: db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
ports:
- 15432:15432
volumes:
- db-data:/var/lib/postgresql/data
command: -p 15432
restart: unless-stopped
volumes:
db-data:
Edit: code above changed to remove links and expose.
db service output:
db |
db | PostgreSQL Database directory appears to contain a database; Skipping initialization
db |
db | 2020-11-05 20:18:15.865 UTC [1] LOG: starting PostgreSQL 13.0 (Debian 13.0-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
db | 2020-11-05 20:18:15.865 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 15432
db | 2020-11-05 20:18:15.865 UTC [1] LOG: listening on IPv6 address "::", port 15432
db | 2020-11-05 20:18:15.873 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.15432"
db | 2020-11-05 20:18:15.880 UTC [25] LOG: database system was shut down at 2020-11-05 20:18:12 UTC
db | 2020-11-05 20:18:15.884 UTC [1] LOG: database system is ready to accept connections
script.js - used by the command from the server service.
const pg = require('pg');
console.log(process.env.DATABASE_URL);
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
connectionTimeoutMillis: 5000,
});
pool.connect((err, _, done) => {
if (err) {
console.error(err);
done(err);
}
done();
});
pool.query('SELECT NOW()', (err, res) => {
console.log(err, res);
pool.end();
});
const client = new pg.Client({
connectionString: process.env.DATABASE_URL,
connectionTimeoutMillis: 5000,
});
client.connect(console.error);
client.query('SELECT NOW()', (err, res) => {
console.log(err, res);
client.end();
});
server service output:
NOTE: The first line is the output of the first console.log call from script.js.
NOTE: Since the server service is set up with restart: unless-stopped, it will repeat this output forever.
server | postgres://postgres:postgres#db:15432/mydb
server | Error: Connection terminated due to connection timeout
server | at Connection.<anonymous> (/home/node/app/node_modules/pg/lib/client.js:255:9)
server | at Object.onceWrapper (events.js:421:28)
server | at Connection.emit (events.js:315:20)
server | at Socket.<anonymous> (/home/node/app/node_modules/pg/lib/connection.js:78:10)
server | at Socket.emit (events.js:315:20)
server | at emitCloseNT (net.js:1659:8)
server | at processTicksAndRejections (internal/process/task_queues.js:79:21)
server | at runNextTicks (internal/process/task_queues.js:62:3)
server | at listOnTimeout (internal/timers.js:523:9)
server | at processTimers (internal/timers.js:497:7)
server | Error: Connection terminated due to connection timeout
server | at Connection.<anonymous> (/home/node/app/node_modules/pg/lib/client.js:255:9)
server | at Object.onceWrapper (events.js:421:28)
server | at Connection.emit (events.js:315:20)
server | at Socket.<anonymous> (/home/node/app/node_modules/pg/lib/connection.js:78:10)
server | at Socket.emit (events.js:315:20)
server | at emitCloseNT (net.js:1659:8)
server | at processTicksAndRejections (internal/process/task_queues.js:79:21)
server | at runNextTicks (internal/process/task_queues.js:62:3)
server | at listOnTimeout (internal/timers.js:523:9)
server | at processTimers (internal/timers.js:497:7) undefined
server | Error: timeout expired
server | at Timeout._onTimeout (/home/node/app/node_modules/pg/lib/client.js:95:26)
server | at listOnTimeout (internal/timers.js:554:17)
server | at processTimers (internal/timers.js:497:7)
server | Error: Connection terminated unexpectedly
server | at Connection.<anonymous> (/home/node/app/node_modules/pg/lib/client.js:255:9)
server | at Object.onceWrapper (events.js:421:28)
server | at Connection.emit (events.js:315:20)
server | at Socket.<anonymous> (/home/node/app/node_modules/pg/lib/connection.js:78:10)
server | at Socket.emit (events.js:315:20)
server | at emitCloseNT (net.js:1659:8)
server | at processTicksAndRejections (internal/process/task_queues.js:79:21) undefined
server | postgres://postgres:postgres#db:15432/mydb
...
From the host computer, I can reach the PostgreSQL database at the db service, connecting successfully, using the same script from the server service.
The output of the script running from the host computer:
➜ node script.js
postgres://postgres:postgres#localhost:15432/mydb
null Client { ... }
undefined Result { ... }
null Result { ... }
This output means the connection succeeded.
In summary:
I can't reach the db container from the server container, getting timeouts on the connection, but I can reach the db container from the host computer, connecting successfully.
Considerations
First, thanks for the answer so far. Addressing some points raised:
Missing network:
It isn't required because docker-compose has a default network. A tested with a custom network but it didn't work either.
Order of initialization:
I'm using depends_on to ensure the db container is started first but I know it isn't ensuring the database is in fact initialized first then the server. It isn't the problem because the server breaks when a timeout happens and it runs again because it is set up with restart: unless-stopped. So if the database is still initializing on the first or second try to start the server, there is no problem because the server will continue to be restarted until it succeeds in the connection (which never happened.)
UPDATE:
From the server container, I could reach the database at the db service using psql. I still can't connect from the Node.js app there.
The DATABASE_URL isn't the problem because the URI I used in the psql command is the same URI used by the script.js and printed by the first console.log call there.
Command-line used:
docker exec -it server psql postgres://postgres:postgres#db:15432/mydb
Edit: Improved code by removing the dependency for Sequelize. Now it uses only pg and calls the script directly.
Thanks for providing the source to reproduce the issue.
No issues in the docker-compose file as you have already ruled out.
The problem lies between your Dockerfile and the version of node-pg that you are using.
You are using node:14-alpine and pg: 7.18.2.
Turns out there is a bug on node 14 and earlier versions of node-pg.
Solution is either downgrade to node v12 or use latest version of node-pg which is currently 8.4.2 (fix went in on v8.0.3).
I have verified both these solutions on the branch you provided and they work.
This isn't a complete answer; I don't have your code handy so I can't actually test the compose file. However, there are a few issues there I'd like to point out:
The links directive is deprecated.
The links is a legacy option that was used before Docker introduced user-defined networks and automatic DNS support. You can just get rid of it. Containers in a compose file are able to refer to each other by name without.
The expose directive does nothing. It can be informative in for example a Dockerfile as a way of saying, "this image will expose a service on this port", but it doesn't actually make anything happen. It's almost entirely useless in a compose file.
The depends_on directive is also less useful than you would think. It will indeed cause docker-compose to bring up the database container first, but it the container is considered "up" as soon as the first process has started. It doesn't cause docker-compose to wait for the database to be ready to service requests, which means you'll still run into errors if your application tries to connect before the database is ready.
The best solution to this is to built database re-connection logic into your application so that if the database ever goes down (e.g. you restart the postgres container to activate a new configuration or upgrade the postgres version), the app will retry connections until it is successful.
An acceptable solution is to include code in your application startup that blocks until the database is responding to requests.
The problem has nothing to do with docker. To test that, perform following actions :
By using this docker-compose.yml file:
version: '3.8'
services:
app:
image: ubuntu
container_name: app
command: sleep 8h
db:
image: postgres
container_name: db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
expose:
- '15432'
ports:
- 15432:15432
volumes:
- db-data:/var/lib/postgresql/data
command: -p 15432
restart: unless-stopped
volumes:
db-data:
Perform a docker exec -it app bash to go into container app then install postgresql-client with apt install -y postgresql-client`.
Command psql -h db -p 15432 -U postgres -W succeeded !
Check pg configuration
You say that pg use environment variable DATABASE_URL to reach postgresql. I'm not sure :
From https://node-postgres.com/features/connecting, we can found this example :
$ PGUSER=dbuser \
PGHOST=database.server.com \
PGPASSWORD=secretpassword \
PGDATABASE=mydb \
PGPORT=3211 \
node script.js
And this sentence :
node-postgres uses the same environment variables as libpq to connect to a PostgreSQL server.
In libpq documentation, no DATABASE_URL.
To adapt example provided in pg documentation with your docker-compose.yml file, try with following file (I only changed environments variables of app service) :
version: '3.8'
services:
server:
image: myapi
build: .
container_name: server
env_file: .env
environment:
- PORT=80
- PGUSER=postgres
- PGPASSWORD=postgres
- PGHOST=db
- PGDATABASE=mydb
- PGPORT=15432
- REDIS_URL=redis://redis
ports:
- 3000:80
depends_on:
- db
command: node script.js
restart: unless-stopped
db:
image: postgres
container_name: db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mydb
ports:
- 15432:15432
volumes:
- db-data:/var/lib/postgresql/data
command: -p 15432
restart: unless-stopped
volumes:
db-data:
I have my nodejs code running on my host machine(MACOS) and which is trying to connect to the mongodb replicaset which is running in the docker.
version: "3"
services:
redis_master:
image: redis:2.8
volumes:
- "/Users/hiteshbaldaniya/docker-redis/master:/data/"
ports:
- "6379:6379"
networks:
- database
mongodb_primary:
build:
context: ./
dockerfile: DockerfileDB
command: mongod --replSet "hdbrs" --dbpath "/data/27017/" --port 27017
ports:
- "27017:27017"
volumes:
- "/Users/hiteshbaldaniya/docker-mongodb/:/data/"
networks:
- database
mongodb_secondary1:
build:
context: ./
dockerfile: DockerfileDB
command: mongod --replSet "hdbrs" --dbpath "/data/27018/" --port 27018
ports:
- "27018:27018"
volumes:
- "/Users/hiteshbaldaniya/docker-mongodb/:/data/"
networks:
- database
mongodb_secondary2:
build:
context: ./
dockerfile: DockerfileDB
command: mongod --replSet "hdbrs" --dbpath "/data/27019/" --port 27019
ports:
- "27019:27019"
volumes:
- "/Users/hiteshbaldaniya/docker-mongodb/:/data/"
networks:
- database
hdb_nginx:
build:
context: ./nginx/
dockerfile: DockerfileNginx.dev
ports:
- "8081:80"
volumes:
- "/Users/hiteshbaldaniya/logs/docker-nginx/:/var/log/nginx/"
networks:
- backend
networks:
backend:
driver: bridge
database:
driver: bridge
All 3 ports are open on my host machine and I tried using telnet and I am able to connect to all the ports as well.
My nodejs application using mongodb-node-driver and using following configuration.
module.exports = {
servers: [{
host: 'localhost',
port: 27017,
},
{
host: 'localhost',
port: 27018,
},
{
host: 'localhost',
port: 27019,
},
],
database: 'mydatabase',
options: {
"raw": false,
"poolSize": 5,
"readPreference": "primaryPreferred",
"w": 1,
"wtimeout": 12000,
"replicaSet": "hdbrs"
}
};
While connecting to mongodb my application throws following error can someone help me over here?
{ MongoNetworkError: failed to connect to server [mongodb_primary:27017] on first connect [MongoNetworkError: getaddrinfo ENOTFOUND mongodb_primary mongodb_primary:27017]
at Pool.<anonymous> (/Users/hiteshbaldaniya/Applications/contentstack-migration/node_modules/mongodb-core/lib/topologies/server.js:505:11)
at Pool.emit (events.js:198:13)
at Connection.<anonymous> (/Users/hiteshbaldaniya/Applications/contentstack-migration/node_modules/mongodb-core/lib/connection/pool.js:329:12)
at Object.onceWrapper (events.js:286:20)
at Connection.emit (events.js:198:13)
at Socket.<anonymous> (/Users/hiteshbaldaniya/Applications/contentstack-migration/node_modules/mongodb-core/lib/connection/connection.js:245:50)
at Object.onceWrapper (events.js:286:20)
at Socket.emit (events.js:198:13)
at emitErrorNT (internal/streams/destroy.js:91:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
name: 'MongoNetworkError',
message:
'failed to connect to server [mongodb_primary:27017] on first connect [MongoNetworkError: getaddrinfo ENOTFOUND mongodb_primary mongodb_primary:27017]' }
thanks,
I was able to figure out solution. By adding the host entries in my /etc/hosts file.
127.0.0.1 mongodb_primary
127.0.0.1 mongodb_secondary1
127.0.0.1 mongodb_secondary2
I am still not sure what is wrong with localhost while connecting to mongodb running inside docker from host machine, I would still need to understand the behaviour of this.
Just for the node I have also tried adding the --bind_ip_all in command line during command for mongodb run.
Do let me know if someone know it. thanks
I'm trying to switch over to docker, for some reason my Node.js application won't connect to redis running on docker.
This is my docker compose:
version: "3"
services:
chatty-backend:
container_name: chatty-backend
build: ./
volumes:
- ./:/usr/src/chatty-backend
command: npm start
working_dir: /usr/src/chatty-backend
ports:
- "5000:5000"
links:
- redis
- mongo
depends_on:
- redis
- mongo
mongo:
image: mongo:bionic
ports:
- "27017:27017"
redis:
image: redis
ports:
- "6379:6379"
command:
redis-server
This is where I'm connecting to redis:
import * as Redis from 'ioredis';
const redis = new Redis({ host: 'redis', port: 6379 });
export default redis;
Also tried new Redis('redis://redis:6379')
I'm getting this error currently:
Error: connect ECONNREFUSED 127.0.0.1:6379
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1056:14) {
errno: 'ECONNREFUSED',
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 6379
}
The issue caused by graphql-redis-subscriptions.
I forgot to pass the updated confifg.
I have created my react app with Node.js and postgresql and I deployed in google cloud. I created a docker image of postgres and nodejs and I uploaded images to docker hub. From gcloud I accessing Those images.
This is my docker-compose-production.yml file.
version: '2.0'
services:
postgres:
image : mycompany/myapp:pglatest
restart : always
volumes:
- ./backdata/databackups:/var/lib/postgresql/backdata
ports:
- "5433:5432"
backend:
image: mycompany/myapp:nodelatest7
command: npm run start
ports:
- "5001:5000"
depends_on:
- postgres
environment:
POSTGRES_URL: postgresql://postgres:root#postgres:5432/db_mydb
DEBUG: hms-backend:*
when I run command
sudo docker-compose -f docker-compose-production.yml up --build -d
2 images are created.
after that I have run tail command
sudo docker-compose -f docker-compose-production.yml logs -t backend
I'm getting error as
backend_1 | 2018-09-15T09:12:18.926001351Z REST API listening on port 5000
backend_1 | 2018-09-15T09:12:18.937246598Z error { Error: connect ECONNREFUSED 192.168.80.2:5432
backend_1 | 2018-09-15T09:12:18.937268668Z at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1191:14)
backend_1 | 2018-09-15T09:12:18.937280934Z errno: 'ECONNREFUSED',
backend_1 | 2018-09-15T09:12:18.937283960Z code: 'ECONNREFUSED',
backend_1 | 2018-09-15T09:12:18.937286817Z syscall: 'connect',
backend_1 | 2018-09-15T09:12:18.937289488Z address: '192.168.80.2',
backend_1 | 2018-09-15T09:12:18.937292260Z port: 5432 }
How to solve this problem
For me your postgres url is wrong : postgresql://postgres:root#postgres:5432/db_mydb
It should be postgresql://postgres:root#postgres:5433/db_mydb since the postgres "exposed" port in 5433
Hum, by i think you should add "container_name" in the docker-compose
services:
postgres:
container_name: my_postgres
and use this name for the "adress" of your postgres
postgresql://my_postgres:root#postgres:5433/db_mydb