I successfully set up MongoDB replica set with docker-compose using Docker hostnames db1
, db2
, db3
. However, these hostnames are of course not available outside of Docker unless I edit e.g. /etc/hosts
.
The MongoDB servers use port 27017 inside the container but are exposed on ports 27017, 27027, 27037, respectively, and MongoDB servers are started with --bind_ip_all
(I also tried --bind_ip localhost,db1,127.0.0.1
etc.).
Now I want to connect a non-Docker node.js application to MongoDB.
Connecting to my local replica set in Docker worked with the node.js mongodb@^3.7.3 driver but now fails with mongodb@^4, including 4.0.0.
I'm using this snippet adapted from the MongoDB driver documentation page, adding only the useUnifiedTopology
flag to be able to connect:
const { MongoClient } = require("mongodb");
// Connection URI
const uri = "mongodb://user:pass@localhost/db";
// Create a new MongoClient
const client = new MongoClient(uri, { useUnifiedTopology: true });
async function run() {
try {
// Connect the client to the server
await client.connect();
// Establish and verify connection
await client.db("admin").command({ ping: 1 });
console.log("Connected successfully to server");
} finally {
// Ensures that the client will close when you finish/error
await client.close();
}
}
run().catch(console.dir);
useUnifiedTopology
seems to have disappeared with mongodb driver version 4.
I tried different single server connection strings such as only with localhost
as in the example (i.e. connecting to the primary only), 127.0.0.1
, with and without port spec :27017
as well as replica set connection strings with localhost:27017,localhost:27027,localhost:27037
and appended ?replicaSet=rs0
and/or specified replicaSet: "rs0"
, readPreference: "primary"
(or "nearest"
etc.) in the connection options but all fail like this:
MongoServerSelectionError: getaddrinfo ENOTFOUND db1
at Timeout._onTimeout (/dev/x/m/node_modules/mongodb/lib/sdam/topology.js:310:38)
at listOnTimeout (internal/timers.js:557:17)
at processTimers (internal/timers.js:500:7) {
reason: TopologyDescription {
type: 'ReplicaSetNoPrimary',
servers: Map(3) {
'db1:27017' => [ServerDescription],
'db2:27017' => [ServerDescription],
'db3:27017' => [ServerDescription]
},
stale: false,
compatible: true,
heartbeatFrequencyMS: 10000,
localThresholdMS: 15,
setName: 'rs0',
maxSetVersion: 2,
maxElectionId: ObjectId { [Symbol(id)]: [Buffer [Uint8Array]] },
commonWireVersion: 9,
logicalSessionTimeoutMinutes: undefined
}
}
How can I connect to a Dockerized MongoDB replica set that is using Docker-internal hostnames?
Do I really need "public" hostnames that are valid both inside and outside of Docker for this to work?
Looking at the question Connect to MongoDB Replica Set running inside Docker with Java (Windows), this seems to be an unsupported case, however, it was working with the old mongodb 3 driver.
From that question:
In conclusion, to support key features of replica sets, we require that the hostnames used in a replica set config are reachable from the client.
It seems specifying directConnection: true
in the connection options makes it work, preventing server delegation / redirection:
// Connection URI
const uri = "mongodb://user:pass@localhost/db";
// Create a new MongoClient
const client = new MongoClient(uri, { directConnection: true });