# Managed MongoDB on Cloider

> Use managed MongoDB 8 on Cloider. Connect with mongosh, Node.js, Mongoose, Python, Go, PHP, Java and Ruby over TLS, and learn what its single-member replica set means for drivers.

Cloider runs managed **MongoDB 8**, the document database that stores JSON-like documents in collections. Each MongoDB database is its own server with its own storage, a database and a user created for you, backups with point-in-time restore and optional TLS. This page covers what is specific to MongoDB on Cloider and how to connect from the shell and from code.

## At a glance

| | |
| --- | --- |
| **Version** | MongoDB 8 |
| **Topology** | A single-member replica set, so transactions and change streams work |
| **Port** | Assigned per database (not 27017) |
| **Database and user** | A database named after your Cloider database (for example `catalog-db`) and the user you chose (for example `catalog_db_user`), created in that database |
| **Admin access** | Not given out. The `admin` user and the `admin`, `local` and `config` databases are reserved |
| **SSL** | On by default. The server accepts TLS and, for compatibility, unencrypted connections too |
| **Client** | `mongosh` |
| **Plans** | Hobby (0.25 vCPU, 0.5 GB RAM, 2 GB storage) to Pro (3 vCPU, 6 GB RAM, 24 GB storage), see [plans](/databases/overview#mongodb-plans) |
| **Backups** | A full backup every 14 days plus every change recorded, restored into a new database as of now or any second of the last 14 days |

## Create a MongoDB database

```bash
cloider db create catalog-db --engine mongodb --plan basic --wait
```

Or in the dashboard, choose **MongoDB v8** under **Database Engine**. See [create a database](/databases/create-a-database).

## Connection details

| Detail | Example |
| --- | --- |
| Public host | `catalog-db-m3v8rt.db.cloider.com` |
| Private host (Cloider apps and clusters) | `catalog-db-m3v8rt.db.cloider.internal` |
| Port | `24309` |
| Database | `catalog-db` |
| User | `catalog_db_user` |
| Password | `cloider db password catalog-db` |

The connection string, with SSL on:

```text
mongodb://catalog_db_user:PASSWORD@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true
```

Keep `directConnection=true`: the server is a [single-member replica set](#a-single-member-replica-set), and without it a driver connects to the set's member under the public host, whatever host you gave it. The user is created in the `catalog-db` database, and a driver authenticates against the database in the path by default, so no `authSource` parameter is needed. Use `mongodb://`, not `mongodb+srv://`: Cloider databases don't publish SRV records.

## Connect with mongosh

Download the CA certificate first. MongoDB clients verify the server's certificate whenever TLS is on, and Cloider's CA isn't in your system's trust store.

```bash
cloider db cert catalog-db -f ca.crt

mongosh "mongodb://catalog_db_user:$(cloider db password catalog-db)@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true" \
  --tlsCAFile ca.crt
```

`cloider db connect` does both steps for you, with `mongosh` installed:

```bash
cloider db connect catalog-db
```

Or run `mongosh` inside Cloider, without installing anything:

```bash
cloider db shell catalog-db                                        # interactive mongosh
cloider db exec catalog-db "db.products.countDocuments()"          # one expression
cloider db exec -i catalog-db < seed.js                            # a script
```

### Useful mongosh commands

| Command | What it does |
| --- | --- |
| `show collections` | List the collections of the current database |
| `db.products.find().limit(5)` | Show five documents |
| `db.products.countDocuments({ inStock: true })` | Count matching documents |
| `db.products.getIndexes()` | List indexes |
| `db.stats()` | Size of the database |
| `db.currentOp()` | Operations in progress |
| `exit` | Quit |

## Connect from code

From a Cloider app or cluster, use the **private** host, see [connect from a Cloider app or cluster](/databases/connect#connect-from-a-cloider-app-or-kubernetes-cluster). Every example passes the CA certificate from `cloider db cert catalog-db -f ca.crt`.

**Node.js**

With the [MongoDB Node.js driver](https://www.mongodb.com/docs/drivers/node/current/) (`npm install mongodb`):

```js title="db.js"
import { MongoClient } from "mongodb";

// mongodb://catalog_db_user:PASSWORD@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true
const client = new MongoClient(process.env.MONGODB_URI, {
  tlsCAFile: "ca.crt",
  maxPoolSize: 10,
});

await client.connect();
const products = client.db().collection("products");
console.log(await products.findOne({ sku: "ACME-001" }));
```

`client.db()` without a name uses the database in the connection string.

**Mongoose**

With [Mongoose](https://mongoosejs.com/) (`npm install mongoose`):

```js title="db.js"
import mongoose from "mongoose";

await mongoose.connect(process.env.MONGODB_URI, { tlsCAFile: "ca.crt" });

const Product = mongoose.model("Product", new mongoose.Schema({
  sku: { type: String, unique: true },
  name: String,
  price: Number,
}));

console.log(await Product.findOne({ sku: "ACME-001" }));
```

**Python**

With [PyMongo](https://pymongo.readthedocs.io/) (`pip install pymongo`):

```python title="db.py"
import os
from pymongo import MongoClient

# mongodb://catalog_db_user:PASSWORD@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true
client = MongoClient(os.environ["MONGODB_URI"], tlsCAFile="ca.crt")
db = client.get_default_database()

print(db.products.find_one({"sku": "ACME-001"}))
```

**Go**

With the [MongoDB Go driver](https://www.mongodb.com/docs/drivers/go/current/) (`go get go.mongodb.org/mongo-driver/v2/mongo`):

```go title="main.go"
package main

import (
	"context"
	"fmt"
	"os"

	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

func main() {
	ctx := context.Background()

	// mongodb://catalog_db_user:PASSWORD@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true&tlsCAFile=ca.crt
	client, err := mongo.Connect(options.Client().ApplyURI(os.Getenv("MONGODB_URI")))
	if err != nil {
		panic(err)
	}
	defer client.Disconnect(ctx)

	var product bson.M
	err = client.Database("catalog-db").Collection("products").
		FindOne(ctx, bson.D{{Key: "sku", Value: "ACME-001"}}).Decode(&product)
	if err != nil {
		panic(err)
	}
	fmt.Println(product)
}
```

**PHP**

With the [MongoDB PHP library](https://www.mongodb.com/docs/php-library/current/) (`composer require mongodb/mongodb`):

```php title="db.php"
<?php
require 'vendor/autoload.php';

$client = new MongoDB\Client(getenv('MONGODB_URI'), ['tlsCAFile' => __DIR__ . '/ca.crt']);
$products = $client->selectCollection('catalog-db', 'products');

var_dump($products->findOne(['sku' => 'ACME-001']));
```

**Java**

The Java driver reads trusted CAs from a Java trust store, so import the CA certificate into one first:

```bash
keytool -importcert -alias cloider-ca -file ca.crt -keystore cloider.jks -storepass changeit -noprompt
```

Then start the JVM with `-Djavax.net.ssl.trustStore=cloider.jks -Djavax.net.ssl.trustStorePassword=changeit`, and connect with the [MongoDB Java driver](https://www.mongodb.com/docs/drivers/java/sync/current/):

```java title="Main.java"
import com.mongodb.client.*;
import org.bson.Document;

public class Main {
    public static void main(String[] args) {
        // mongodb://catalog_db_user:PASSWORD@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true
        try (MongoClient client = MongoClients.create(System.getenv("MONGODB_URI"))) {
            MongoCollection<Document> products = client.getDatabase("catalog-db").getCollection("products");
            System.out.println(products.find(new Document("sku", "ACME-001")).first());
        }
    }
}
```

With Spring Boot, set `spring.data.mongodb.uri` to the same connection string and pass the same trust store properties.

**Ruby**

With the [mongo gem](https://www.mongodb.com/docs/ruby-driver/current/) (`gem install mongo`):

```ruby title="db.rb"
require "mongo"

# mongodb://catalog_db_user:PASSWORD@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true
client = Mongo::Client.new(ENV.fetch("MONGODB_URI"), ssl_ca_cert: "ca.crt")
puts client[:products].find(sku: "ACME-001").first
```

## MongoDB features and limits on Cloider

### A single-member replica set

A Cloider MongoDB database runs as a replica set named `rs0` with one member, the primary. Its oplog is what makes [point-in-time restore](/databases/backups-and-restore#restore-a-backup-into-a-new-database) possible, and features that MongoDB only offers on replica sets work:

- **Multi-document transactions** (`session.withTransaction()`, `startTransaction`).
- **Change streams** (`collection.watch()`).
- Libraries that require a replica set, such as Prisma's MongoDB connector.

There are no secondaries, so every read goes to the primary and `majority` means that one member.

The member's address is the public host, `catalog-db-m3v8rt.db.cloider.com:24309`. A driver that connects to a replica set looks up its members and switches to their addresses, so a connection string with the private host and without `directConnection=true` ends up on the public host. Keep `directConnection=true` in the connection string, as the ones Cloider shows have it, and don't add `replicaSet=`.

### The database and the user

Cloider creates one database, named after your Cloider database, and one user in it. Your application works in that database. The user can't create or manage other users, and the `admin` user, which administers the server, is reserved for Cloider.

### TLS mode

With SSL on, the server presents a certificate signed by Cloider's CA and accepts both TLS and unencrypted connections. Always put `tls=true` in your connection string so your client encrypts, and pass the CA file. With SSL off, the server only accepts unencrypted connections, so remove `tls=true`. See [SSL/TLS](/databases/ssl-tls).

### Memory

MongoDB's storage engine, WiredTiger, keeps frequently used data and indexes in RAM. When the [Insights](/databases/insights) charts show memory close to the plan's RAM together with high disk reads, your working set no longer fits and a [larger plan](/databases/resize) will help.

### Backups and restore

Cloider takes a full backup of every MongoDB database every 14 days and records every change in between from its oplog. A [restore](/databases/backups-and-restore) creates a new database with your data as it is now, or as it was at [any second of the last 14 days](/databases/backups-and-restore#how-far-back-can-i-restore), for example right before a bad `deleteMany`. It brings back the whole server, every database, user and role included.

## Common tasks

```js
// Create an index, unique on sku
db.products.createIndex({ sku: 1 }, { unique: true })

// Explain how a query runs
db.products.find({ category: "tools", price: { $lt: 20 } }).explain("executionStats")

// Size of each collection, in MB
db.getCollectionNames().forEach(c => print(c, (db[c].stats().size / 1024 / 1024).toFixed(1)))

// Remove documents older than 30 days
db.events.deleteMany({ createdAt: { $lt: new Date(Date.now() - 30 * 24 * 3600 * 1000) } })
```

Run any of them with `cloider db exec catalog-db '<expression>'`. Single quotes keep your shell from expanding `$lt`.

## Import an existing MongoDB database

```bash
# Dump one database from the old server
mongodump --uri "mongodb://app:PASSWORD@old-db.example.com:27017/catalog" --gzip --archive=catalog.archive

# Restore it into the Cloider database, renaming the namespace
mongorestore --uri "mongodb://catalog_db_user:$(cloider db password catalog-db)@catalog-db-m3v8rt.db.cloider.com:24309/catalog-db?directConnection=true&tls=true" \
  --tlsCAFile ca.crt --gzip --archive=catalog.archive \
  --nsFrom 'catalog.*' --nsTo 'catalog-db.*'
```

`--nsFrom` and `--nsTo` load the old `catalog` database into `catalog-db`. Leave users and roles out of the dump: they can't be restored with the database user. See [migrate to Cloider](/guides/migrate-to-cloider).

## Troubleshooting

| Error | Cause and fix |
| --- | --- |
| `MongoServerSelectionError: connection timed out` or `Server selection timed out after 30000 ms` | Your IP isn't trusted. `cloider db trusted-sources catalog-db --add-my-ip` |
| `MongoServerError: Authentication failed.` | Wrong password or user, or the wrong database in the path. The path must be `/catalog-db` |
| `unable to get local issuer certificate` or `self signed certificate in certificate chain` | The client doesn't trust Cloider's CA. Pass `tlsCAFile=ca.crt` |
| `getaddrinfo ENOTFOUND catalog-db-m3v8rt.db.cloider.com` or a timeout from a Cloider app on the private host | The connection string lacks `directConnection=true`, so the driver switched to the replica set member's public host. Add `directConnection=true` |
| `querySrv ENOTFOUND` | The URI uses `mongodb+srv://`. Use `mongodb://` with the host and port |
| Connection works but data is missing | Your code writes to another database. Use the database from the connection string (`client.db()`) |

More in [database troubleshooting](/databases/troubleshooting).

## Next steps

- [SSL/TLS](/databases/ssl-tls)
- [Backups and restore](/databases/backups-and-restore)
- [Insights](/databases/insights)
