How to Connect to MongoDB from Ruby
MongoDB is not a SQL database and there is no ADO.NET-style layer here: Ruby talks to it through one official driver, the mongo gem, a pure-Ruby client. Mongoid, the popular ODM (object-document mapper) used in Rails, sits on top of that same driver. This guide shows the raw mongo interface first, then where Mongoid fits, and covers the things that actually cause MongoDB-from-Ruby incidents: client reuse, lazy connections, the whole-document replace trap, SRV URIs, authSource, and pool sizing.
The options
| Library | Layer | When to use it | Version (as of July 2026) |
|---|---|---|---|
mongo | Official driver (pure Ruby) | Scripts, services, or when you want direct collection access | 2.24.1 |
| Mongoid | ODM built on the driver | Rails apps, or when you want models, validations, and associations | 9.1.0 |
Mongoid depends on the mongo gem, so the driver-level concerns below apply whether you write against the driver directly or through Mongoid.
Installing the driver
gem install mongoThe mongo gem is pure Ruby with no C extension, so there are no system headers to install and no compile step. For DNS SRV connection strings (the mongodb+srv:// form Atlas hands you), you also need a DNS resolver gem; add resolv if your environment does not already provide it.
A minimal connection
require 'mongo'
client = Mongo::Client.new(
['localhost:27017'],
database: 'shop'
)
puts client.database.command(ping: 1).documents.first
client.closeMongo::Client.new takes either an array of host strings plus options (above) or a single connection URI:
client = Mongo::Client.new('mongodb://app:secret@localhost:27017/shop')Reuse one client -- do not create per request
A Mongo::Client owns a connection pool and background monitoring threads. Create it once at process startup and reuse it for the life of the app. Building a new client per web request exhausts connections and adds handshake latency on every call. In Rails this is what the Mongoid initializer or a global constant is for; in a plain service, store it in a constant or a container.
Close it on shutdown:
CLIENT = Mongo::Client.new(ENV['MONGODB_URI'])
at_exit { CLIENT.close }Connections are lazy
Mongo::Client.new does not connect. It returns immediately and the first real network contact happens on your first operation. That means a wrong host or bad credentials surfaces as a Mongo::Error::NoServerAvailable on your first query, not at client creation. To fail fast at startup, ping explicitly:
client.database.command(ping: 1)CRUD basics
Collections come off the client and documents are plain Ruby hashes:
customers = client[:customers]
# insert
customers.insert_one(name: 'Acme', region: 'EMEA', active: true)
# find
customers.find(region: 'EMEA').each do |doc|
puts doc['name']
end
# update a subset of fields
customers.update_one({ name: 'Acme' }, { '$set' => { active: false } })
# delete
customers.delete_one(name: 'Acme')Query filters are hashes; there is no SQL string and therefore no SQL-injection surface in the usual sense. Do still be careful passing raw user input as operators (for example letting a user supply a $where), which is the MongoDB equivalent risk.
The replace-vs-update data-loss trap
replace_one swaps the entire document for the one you pass. If you meant to change one field and used replace_one with just that field, every other field is gone:
# WRONG -- this wipes every field except status
customers.replace_one({ name: 'Acme' }, { status: 'inactive' })
# RIGHT -- change only the field you name
customers.update_one({ name: 'Acme' }, { '$set' => { status: 'inactive' } })Reach for update_one/update_many with $set (and the other update operators) for partial changes. Use replace_one only when you genuinely want to overwrite the whole document.
Connecting to Atlas: SRV URIs and gotchas
Atlas gives you a mongodb+srv:// connection string:
client = Mongo::Client.new(
'mongodb+srv://app:secret@cluster0.abcde.mongodb.net/shop?retryWrites=true&w=majority'
)Three things trip people up:
mongodb+srv://implies TLS. The SRV scheme turns on TLS by default and resolves the seed list from DNS, so you do not list hosts yourself.- Percent-encode credentials. A password containing
@,/,:, or#must be URL-encoded in the URI, or parsing breaks in confusing ways. authSource. By default the driver authenticates against the database in the URI path. If your user was created inadmin(the common case), you needauthSource=admin, or authentication fails even with correct credentials.
For Atlas specifically, the client's public IP must be on the project's IP access list, or the connection times out with no clear auth error.
Connection pooling
Each client keeps a pool per server it knows about. The size is controlled by max_pool_size (default 20):
client = Mongo::Client.new(
ENV['MONGODB_URI'],
max_pool_size: 20,
min_pool_size: 5
)The number that matters is max_pool_size × number of app processes × number of instances against the server's connection limit. A handful of Puma workers each with a 20-connection pool adds up quickly on a small cluster. Size the pool to your real concurrency, not to a big round number.
Mongoid
Mongoid configures the same driver through config/mongoid.yml:
production:
clients:
default:
uri: <%= ENV['MONGODB_URI'] %>
options:
max_pool_size: 20Models then map to collections:
class Customer
include Mongoid::Document
field :name, type: String
field :region, type: String
field :active, type: Boolean
end
Customer.where(region: 'EMEA').to_aMongoid gives you validations, associations, and query composition; underneath, every call goes through the mongo driver, so the pooling and authSource notes above still apply.
Common errors
| Error | Cause |
|---|---|
Mongo::Error::NoServerAvailable | Wrong host/port, server down, or Atlas IP not allow-listed (surfaces on first op, not on client create) |
Mongo::Auth::Unauthorized | Wrong credentials, or missing authSource=admin for an admin-created user |
Connection to localhost refused | Client resolving to IPv6 ::1 while mongod listens on IPv4; use 127.0.0.1 |
| URI parse errors | Special characters in the password not percent-encoded |
| Every field but one disappeared after an update | replace_one used where update_one + $set was meant |
| Connections exhausted under load | A new client per request instead of one reused client; or pool size × processes over the server cap |
Querying without writing connection code
If your goal is to explore a MongoDB database rather than build a service, a GUI client skips all of the above. Mako connects to MongoDB with AI-assisted queries -- see our MongoDB GUI client guide for how it compares to Compass and Studio 3T. Connecting from another language? See How to Connect to MongoDB from Python and from Node.js.
Mako connects to MongoDB with AI-powered autocomplete. Try it free at mako.ai.
Skip the terminal. Use Mako.
Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.