A server selection timeout means the driver found no reachable server: wrong URI, network egress blocked, or Atlas IP allowlist. The error text lists the attempted endpoints — that's the diagnosis.
Atlas rejects at the network layer — which reads as a timeout, not auth. Home/CI IPs rotate: 0.0.0.0/0 is the tempting workaround and the security hole to avoid.
Typo in the host, a missing mongodb+srv:// (SRV record needed for Atlas), or the environment blocks outbound 27017. The error's attempted-endpoints list exposes all three.
Serverless or function environments need the driver's serverless mode and have distinct egress rules; traditional connect strings behave inconsistently there.
# error text: [host1:27017, host2:27017] — compare against your Atlas cluster's hostname list
nc -zv cluster0.xxxxx.mongodb.net 27017 # or curl -s https://cloud.mongodb.com/ping
# Atlas console → Network Access → Add IP: your CI NAT IP or a dedicated egress IP; never 0.0.0.0/0 with username/password exposed
# mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net/db?retryWrites=true&w=majority (srv for Atlas)
Timeout (network/allowlist) vs AuthenticationFailed (credential) vs bad-URI parse: the error family names the layer before you waste a debugging cycle on the wrong one. If you must allow broad access temporarily, pair 0.0.0.0/0 with short-lived rotated credentials and revert the allowlist the same day — public Atlas endpoints are scanned continuously.
Different egress IP: your laptop's IP is allowlisted, the server's isn't. Add the server's IP (or a static egress IP) — the timeout is Atlas dropping the handshake at the network layer.
Most CI providers publish ranges, or give static egress IPs on paid tiers. Alternatively run a job that curls an IP-echo service once, then allowlist that — remembering shared runners change.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.