MongoDB 8 represents the most significant leap forward in document database technology in years – combining battle-tested reliability with cutting-edge AI capabilities that made it the dominant NoSQL platform through most of 2026, right up until MongoDB 9.0 became generally available on September 29, 2026 with up to 2x higher throughput on large instances compared with MongoDB 8.0. This mongodb tutorial walks you through everything from a blank server to a production-ready deployment on the still widely-deployed MongoDB 8 series (even though MongoDB 8.0 is no longer the newest major release), covering installation, CRUD operations, aggregation pipelines, indexing, Atlas cloud setup, and the new vector search features that are reshaping how developers build intelligent applications.
Whether you are migrating from a relational database or building your first NoSQL project, this guide gives you the practical skills and real-world context to use MongoDB confidently at scale. By the end, you will have a working task management API data layer and a clear path to deploying on MongoDB Atlas.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
Why MongoDB 8 Still Matters in 2026, Even with MongoDB 9.0 Now the Newest Release
The NoSQL landscape has never been more competitive, and MongoDB answered with its biggest release of the year: MongoDB 9.0, which became generally available on September 29, 2026 with MongoDB-reported gains of up to 2x higher throughput on large instances, reads up to 35% faster, updates up to 30% faster, and up to 20% higher transactional-workload throughput compared with MongoDB 8.0, plus a new Intelligent Workload Management layer that automatically tunes resource allocation under mixed workloads. For teams still standardized on the 8.x line covered in this tutorial, MongoDB continues to actively support three release lines, 8.3, 8.2, and 8.0, with the original 8.0 branch now at patch release 8.0.34, a fixes-only release from October 14, 2026 that MongoDB recommends for all 8.0 users. The newer MongoDB 8.3 rapid release launched in May 2026 with a dedicated performance focus, reached general availability on June 3, 2026, and has already progressed to build 8.3.11 as of September 17, 2026, carrying forward many of the same WiredTiger-level gains. MongoDB also reportedly said on September 30, 2026 that nearly 80% of its Atlas fleet had adopted MongoDB 8.0, a company-reported figure.
The strategic move that changed the calculus for 2026 was MongoDB’s acquisition of Voyage AI in February 2025. Voyage AI brought world-class embedding models and retrieval-augmented generation (RAG) expertise into the MongoDB ecosystem. The result is Automated Embedding in Vector Search, now in public preview, which lets developers store, index, and query semantic embeddings without ever leaving the MongoDB driver.
Critically, vector search is no longer an Atlas-exclusive feature. Community Edition users can now run vector search workloads locally, making MongoDB the first major document database to democratize semantic search at the infrastructure level. This matters enormously for teams that need to prototype AI features before committing to cloud spend.
On the operational side, MongoDB 8.0 brings measurable write throughput improvements through WiredTiger engine optimizations, smarter checkpoint handling that reduces latency spikes under heavy write load, and expanded time series collection capabilities including secondary indexes on measurement fields and configurable granularity windows. These are not incremental tweaks – they directly affect the economics of running MongoDB at scale.
One development worth noting for anyone running older versions: MongoDB 6.0 reached end-of-life on July 31, 2025, and the short-lived MongoDB 8.2 rapid release (first shipped August 16, 2025) has itself already reached end-of-life on July 31, 2026, since rapid releases stay supported only until the next one ships. If your infrastructure still runs 6.0 or an unsupported rapid release, upgrading is not optional – security patches and bug fixes are no longer issued for those branches. The safer long-term target is MongoDB 8.0, which MongoDB designated a Long-Term Support release that will keep receiving patches until October 31, 2029. The good news is that the upgrade path from 6.0 through 7.0 to 8.0 is well-documented and the MongoDB team has published tooling to help automate feature compatibility checks.
MongoDB also demonstrated its commitment to security responsiveness well into 2026. When CVE-2025-14847 was disclosed in 2025, MongoDB Atlas patched tens of thousands of customer clusters rapidly, with most clusters receiving the fix before the public advisory was widely circulated. More recently, a coordinated security release on July 22, 2026 patched 25 CVEs in a single pass across 8.3.7, 8.2.12, 8.0.28, and 7.0.39 at once – a sign that MongoDB now treats every supported line as part of one synchronized patch cycle rather than staggering fixes branch by branch. That kind of managed security response is a strong argument for Atlas over self-hosted deployments for teams without dedicated database operations staff.
For a side-by-side technical comparison with the leading relational alternative, see our MongoDB vs PostgreSQL 2026 comparison. And if you are already comfortable with relational databases, our PostgreSQL tutorial provides useful context on where the two database philosophies diverge.
Prerequisites and Environment Setup
This mongodb tutorial assumes you are comfortable working in a terminal and have basic familiarity with JSON. You do not need prior database experience, but understanding key-value pairs and nested data structures will help concepts land faster. Here is what you need before proceeding.
Hardware requirements: MongoDB 8.0 runs comfortably on 2 CPU cores and 4 GB of RAM for development workloads. Production deployments should size according to your working set – the portion of your data that fits in RAM determines query performance more than any other single factor.
Software prerequisites: A 64-bit operating system (Linux, macOS 12+, or Windows Server 2019+), Python 3.10+ if you plan to follow the API data layer examples, and Docker if you want to run MongoDB in a container. Grab the latest tooling while you are at it – mongosh 2.12.0 shipped on September 18, 2026, and PyMongo 4.18 followed on September 3, 2026, so installing either today pulls in fixes that predate the versions most tutorials still reference. Our Docker tutorial covers container basics if you need a refresher.
Tools you will install during this guide:
- mongod – the MongoDB server daemon
- mongosh – the modern MongoDB Shell (replaces the legacy mongo shell)
- MongoDB Compass – the GUI client for visual data exploration
- pymongo / motor – Python drivers for synchronous and async access
Accounts to create: A free MongoDB Atlas account at mongodb.com/cloud/atlas. The free tier (M0 cluster) is sufficient for everything in this guide and does not require a credit card.
| Component | Minimum Version | Recommended | Notes |
|---|---|---|---|
| MongoDB Server | 8.0.0 | 8.0.34 | Latest 8.0 patch, released October 14, 2026 |
| mongosh | 2.3.0 | 2.4.x | Required for new shell APIs |
| Python | 3.10 | 3.12 | For pymongo / motor examples |
| pymongo | 4.6 | 4.10 | Includes BSON 2.0 support |
| Docker | 24.0 | 27.x | Optional, for containerized setup |
| MongoDB Compass | 1.42 | 1.45 | Recommended for visual exploration |
Step 1: Installing MongoDB 8 on Linux, macOS, and Windows
Installing on Ubuntu 24.04
Ubuntu remains the most common deployment target for MongoDB in production. The official APT repository ensures you receive signed packages and automatic security updates through your existing package manager workflow.
# Import the MongoDB GPG key
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc |
sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor
# Add the MongoDB 8.0 repository
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ]
https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" |
sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
# Update package list and install
sudo apt-get update
sudo apt-get install -y mongodb-org=8.0.17 mongodb-org-database=8.0.17
mongodb-org-server=8.0.17 mongodb-mongosh mongodb-org-mongos=8.0.17
mongodb-org-tools=8.0.17
# Pin the version to prevent unintended upgrades
echo "mongodb-org hold" | sudo dpkg --set-selections
echo "mongodb-org-database hold" | sudo dpkg --set-selections
echo "mongodb-org-server hold" | sudo dpkg --set-selections
# Start and enable the service
sudo systemctl start mongod
sudo systemctl enable mongod
# Verify the installation
mongosh --eval "db.adminCommand({ serverStatus: 1 }).version"
Installing on macOS with Homebrew
macOS developers should use the official MongoDB tap rather than the community Homebrew formula, as the official tap tracks patch releases reliably and is maintained by MongoDB Inc.
# Add the official MongoDB tap
brew tap mongodb/brew
brew update
# Install MongoDB Community Edition 8.0
brew install [email protected]
# Start MongoDB as a background service
brew services start mongodb/brew/[email protected]
# Connect with mongosh
mongosh
# Verify the version
db.version()
On macOS, MongoDB data is stored in /usr/local/var/mongodb and logs in /usr/local/var/log/mongodb/mongo.log by default. Both paths are configurable in the Homebrew service plist.
Running MongoDB with Docker
Docker is increasingly the preferred approach for development environments because it keeps MongoDB isolated from the host system and makes it trivial to run multiple versions side by side. See our Docker tutorial for a full walkthrough of the containerization concepts used here.
# Pull the official MongoDB 8.0 image
docker pull mongodb/mongodb-community-server:8.0-ubuntu2204
# Run with persistent storage and authentication enabled
docker run -d
--name mongodb8
-p 27017:27017
-e MONGODB_INITDB_ROOT_USERNAME=admin
-e MONGODB_INITDB_ROOT_PASSWORD=securepassword
-v mongodb_data:/data/db
mongodb/mongodb-community-server:8.0-ubuntu2204
# Connect via mongosh
mongosh "mongodb://admin:securepassword@localhost:27017/?authSource=admin"
# Verify connection
db.adminCommand("ping")
Windows users can follow the MSI installer from the official MongoDB documentation, which registers MongoDB as a Windows Service automatically. The Docker approach works equally well on Windows with WSL2 and is often preferred for its consistency with Linux production environments.
Step 2: Configuring MongoDB for Development
A fresh MongoDB installation runs with minimal security and default performance settings that are appropriate for getting started but require adjustment before you expose the database to any network traffic. Understanding the configuration file is the first step toward operational confidence.
The main configuration file on Linux is located at /etc/mongod.conf. It uses YAML format and controls every major aspect of the server’s behavior. Here is a well-commented development configuration that balances convenience with basic security awareness.
# /etc/mongod.conf – Development Configuration for MongoDB 8.0
# Storage settings
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
wiredTiger:
engineConfig:
# Cache size: set to 50-60% of available RAM for dedicated servers
cacheSizeGB: 2
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true
# System log settings
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
verbosity: 0
# Network settings
net:
port: 27017
bindIp: 127.0.0.1 # Bind only to localhost for development
tls:
mode: disabled # Enable in production with valid certificates
# Security settings
security:
authorization: enabled # Always enable in development to build good habits
# Operation profiling – log slow queries (over 100ms)
operationProfiling:
mode: slowOp
slowOpThresholdMs: 100
# Replication – uncomment for production replica sets
# replication:
# replSetName: "rs0"
After modifying the configuration, restart the daemon with sudo systemctl restart mongod. The key development settings to understand are cacheSizeGB, which controls how much RAM WiredTiger uses for its internal cache, and authorization: enabled, which enforces user-level access control. Create your first administrative user immediately after enabling authorization:
# Connect to mongosh (before authorization is fully enforced on first run)
mongosh
# Switch to the admin database
use admin
# Create the administrative user
db.createUser({
user: "dbAdmin",
pwd: passwordPrompt(),
roles: [
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "readWriteAnyDatabase", db: "admin" },
{ role: "dbAdminAnyDatabase", db: "admin" }
]
})
# Create an application-specific user with limited privileges
use taskmanager
db.createUser({
user: "taskapp",
pwd: passwordPrompt(),
roles: [
{ role: "readWrite", db: "taskmanager" }
]
})
Step 3: CRUD Operations – Creating and Reading Documents
MongoDB stores data as BSON documents – a binary superset of JSON that adds types like Date, ObjectId, Decimal128, and binary data. Understanding this data model is foundational to everything else in this mongodb tutorial for beginners and experienced developers alike.
We will build the data layer for a task management application throughout this guide. The domain is simple enough to keep examples focused but rich enough to demonstrate real modeling challenges. If you want a quick warm-up first, MongoDB’s 5-minute interactive tutorial connected learners to Atlas for 3 actions (insert, query, and delete), per MongoDB Docs in October 2026. Connect to MongoDB and set up the working database:
// Connect to your taskmanager database
use taskmanager
// Insert a single task document
db.tasks.insertOne({
title: "Set up MongoDB 8 development environment",
description: "Install mongod, configure auth, create application user",
status: "completed",
priority: "high",
assignee: {
userId: new ObjectId(),
name: "Alex Rivera",
email: "[email protected]"
},
tags: ["infrastructure", "database", "devops"],
dueDate: new Date("2026-04-10"),
createdAt: new Date(),
updatedAt: new Date(),
metadata: {
estimatedHours: 4,
actualHours: 3.5,
sprint: 12
}
})
// Insert multiple tasks at once
db.tasks.insertMany([
{
title: "Design task schema",
status: "in-progress",
priority: "high",
tags: ["architecture", "database"],
dueDate: new Date("2026-04-12"),
createdAt: new Date(),
updatedAt: new Date()
},
{
title: "Write aggregation pipeline for reporting",
status: "todo",
priority: "medium",
tags: ["backend", "reporting"],
dueDate: new Date("2026-04-20"),
createdAt: new Date(),
updatedAt: new Date()
},
{
title: "Configure Atlas cluster",
status: "todo",
priority: "high",
tags: ["cloud", "infrastructure"],
dueDate: new Date("2026-04-15"),
createdAt: new Date(),
updatedAt: new Date()
}
])
// Basic queries
db.tasks.find() // All documents
db.tasks.find({ status: "todo" }) // Filter by field
db.tasks.find({ priority: "high" }, { title: 1, status: 1, _id: 0 }) // Projection
db.tasks.findOne({ status: "completed" }) // Return single document
// Query with comparison operators
db.tasks.find({
dueDate: { $lte: new Date("2026-04-15") },
status: { $in: ["todo", "in-progress"] }
})
// Query nested fields and arrays
db.tasks.find({ "assignee.name": "Alex Rivera" })
db.tasks.find({ tags: "infrastructure" }) // Matches docs where array contains value
// Count, sort, paginate
db.tasks.countDocuments({ status: "todo" })
db.tasks.find({ status: "todo" })
.sort({ dueDate: 1, priority: -1 })
.skip(0)
.limit(10)
MongoDB’s query language supports a rich set of operators. The $in, $nin, $gt, $lt, $gte, $lte operators cover most comparison needs. Logical operators $and, $or, $not, and $nor combine conditions. Array operators like $elemMatch let you match specific conditions within array elements rather than across the full array – a distinction that matters when your array elements are subdocuments with multiple fields that must all match simultaneously.
Step 4: Updating and Deleting Documents
MongoDB’s update operators give you surgical precision – you can modify individual fields, push to arrays, increment counters, and set values conditionally, all without reading the document into your application first. This server-side mutation model is one of MongoDB’s strongest operational advantages over key-value stores and is a core concept in any thorough mongodb tutorial.
// Update a single document – change status and update timestamp
db.tasks.updateOne(
{ title: "Design task schema" },
{
$set: {
status: "completed",
updatedAt: new Date()
},
$inc: { "metadata.actualHours": 2 } // Increment without reading first
}
)
// Update multiple documents – bulk status transition
db.tasks.updateMany(
{ status: "todo", dueDate: { $lt: new Date() } },
{
$set: {
status: "overdue",
updatedAt: new Date()
},
$push: {
statusHistory: {
from: "todo",
to: "overdue",
changedAt: new Date()
}
}
}
)
// Upsert – insert if not found, update if found
db.taskCounters.updateOne(
{ _id: "daily_completed", date: new Date().toISOString().split("T")[0] },
{ $inc: { count: 1 } },
{ upsert: true }
)
// findOneAndUpdate – atomic read-modify pattern (ideal for state machines)
const task = db.tasks.findOneAndUpdate(
{ status: "todo", priority: "high" },
{
$set: { status: "in-progress", startedAt: new Date() },
$push: { assignees: "worker-node-42" }
},
{
returnDocument: "after", // Return the updated document
sort: { dueDate: 1 } // Pick the earliest due task
}
)
// Array update operators
db.tasks.updateOne(
{ title: "Configure Atlas cluster" },
{
$addToSet: { tags: "cloud" }, // Add to array only if not present
$pull: { tags: "old-tag" } // Remove matching elements
}
)
// Soft delete pattern (preferred in production)
db.tasks.updateOne(
{ _id: task._id },
{ $set: { deletedAt: new Date(), deletedBy: "admin" } }
)
// Hard delete when necessary
const result = db.tasks.deleteMany({
status: "archived",
archivedAt: { $lt: new Date("2025-01-01") }
})
print(`Purged ${result.deletedCount} archived tasks`)
The soft delete pattern – setting a deletedAt timestamp rather than physically removing the document – is preferred in most production systems because it preserves audit trails and allows recovery from accidental deletions. Combine it with a TTL index on the deletedAt field to automatically purge records after your retention period expires, keeping the collection from growing unbounded.
Step 5: Mastering the MongoDB Aggregation Pipeline
The mongodb aggregation framework is MongoDB’s answer to SQL’s GROUP BY, JOIN, and window functions – and in many respects it is more powerful because it operates natively on nested documents and arrays. Learning to think in pipeline stages is the skill that separates competent MongoDB users from experts. The pipeline processes documents through a sequence of stages, each transforming its input and passing the result forward.
The most important stages are $match (filter documents), $group (aggregate and summarize), $project (reshape documents), $sort, $limit, $unwind (deconstruct arrays), $lookup (join from another collection), and $facet (run multiple sub-pipelines simultaneously). Always place $match stages as early as possible – MongoDB can use indexes on $match stages but not on most others.
// Pipeline 1: Task status summary with efficiency metrics
db.tasks.aggregate([
// Stage 1: Filter out soft-deleted documents
{ $match: { deletedAt: { $exists: false } } },
// Stage 2: Group by status
{
$group: {
_id: "$status",
count: { $sum: 1 },
avgEstimatedHours: { $avg: "$metadata.estimatedHours" },
avgActualHours: { $avg: "$metadata.actualHours" },
highPriorityCount: {
$sum: { $cond: [{ $eq: ["$priority", "high"] }, 1, 0] }
},
earliestDue: { $min: "$dueDate" },
latestDue: { $max: "$dueDate" }
}
},
// Stage 3: Add computed efficiency field
{
$addFields: {
efficiencyRatio: {
$cond: [
{ $gt: ["$avgEstimatedHours", 0] },
{ $divide: ["$avgActualHours", "$avgEstimatedHours"] },
null
]
}
}
},
{ $sort: { count: -1 } },
// Stage 4: Final projection – reshape output
{
$project: {
status: "$_id",
_id: 0,
count: 1,
highPriorityCount: 1,
efficiencyRatio: { $round: ["$efficiencyRatio", 2] },
dueDateRange: { earliest: "$earliestDue", latest: "$latestDue" }
}
}
])
// Pipeline 2: Tag frequency analysis using $unwind
db.tasks.aggregate([
{ $match: { deletedAt: { $exists: false } } },
{ $unwind: "$tags" }, // Deconstructs array – one document per tag value
{
$group: {
_id: "$tags",
taskCount: { $sum: 1 },
statuses: { $addToSet: "$status" }
}
},
{ $sort: { taskCount: -1 } },
{ $limit: 10 }
])
// Pipeline 3: Sprint velocity report with $lookup join
db.tasks.aggregate([
{ $match: { "metadata.sprint": { $exists: true } } },
{
$group: {
_id: "$metadata.sprint",
totalTasks: { $sum: 1 },
completedTasks: {
$sum: { $cond: [{ $eq: ["$status", "completed"] }, 1, 0] }
},
totalEstimatedHours: { $sum: "$metadata.estimatedHours" },
totalActualHours: { $sum: "$metadata.actualHours" }
}
},
{
$lookup: {
from: "sprints",
localField: "_id",
foreignField: "sprintNumber",
as: "sprintDetails"
}
},
{ $unwind: { path: "$sprintDetails", preserveNullAndEmptyArrays: true } },
{
$project: {
sprint: "$_id",
_id: 0,
velocity: "$completedTasks",
completionRate: {
$multiply: [{ $divide: ["$completedTasks", "$totalTasks"] }, 100]
},
hoursVariance: { $subtract: ["$totalActualHours", "$totalEstimatedHours"] },
sprintName: "$sprintDetails.name"
}
},
{ $sort: { sprint: -1 } }
])
The $facet stage is particularly valuable for building search result pages – it runs multiple sub-pipelines simultaneously on the same input set, so you can return both paginated results and total count in a single database round trip. This pattern eliminates the dual-query overhead that makes pagination expensive in many NoSQL implementations.
Step 6: Indexing Strategies for Performance
Indexes are where MongoDB performance is won or lost. Every query that does not use an index results in a collection scan – reading every document in the collection. For small collections this is acceptable; for collections with millions of documents it is catastrophic. Building the right indexes is one of the highest-use activities in any MongoDB project, and MongoDB 8.0’s improved background index build performance makes it easier to add indexes to large production collections without significant downtime.
// Single field index – most common type
db.tasks.createIndex({ status: 1 })
db.tasks.createIndex({ dueDate: 1 })
// Compound index – follow ESR rule: Equality first, Sort second, Range last
// Supports queries on: { status }, { status, priority }, { status, priority, dueDate }
db.tasks.createIndex(
{ status: 1, priority: 1, dueDate: 1 },
{ name: "status_priority_duedate" }
)
// Sparse index – only indexes documents where the field exists
db.tasks.createIndex({ deletedAt: 1 }, { sparse: true })
// TTL index – automatically deletes documents after expiration
// Purges soft-deleted tasks 90 days after deletion
db.tasks.createIndex(
{ deletedAt: 1 },
{ expireAfterSeconds: 7776000, sparse: true }
)
// Text index – full-text search with weighted fields
db.tasks.createIndex(
{ title: "text", description: "text", tags: "text" },
{
weights: { title: 10, tags: 5, description: 1 },
name: "task_text_search"
}
)
// Text search query using the index
db.tasks.find(
{ $text: { $search: "database infrastructure" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
// Partial index – only indexes active tasks, reducing index footprint
db.tasks.createIndex(
{ dueDate: 1, assignee: 1 },
{
partialFilterExpression: { status: { $in: ["todo", "in-progress"] } },
name: "active_tasks_due_date"
}
)
// Analyze query execution plan
db.tasks.find({ status: "todo", dueDate: { $lt: new Date("2026-05-01") } })
.explain("executionStats")
// Monitor index usage (MongoDB 8.0 enhanced statistics)
db.tasks.aggregate([{ $indexStats: {} }])
// List all indexes
db.tasks.getIndexes()
| Index Type | Best Use Case | Write Overhead | MongoDB 8.0 Notes |
|---|---|---|---|
| Single Field | Equality and range queries on one field | Low | Default for most lookups |
| Compound | Multi-field queries with defined access patterns | Medium | Follow ESR rule for optimal coverage |
| Text | Full-text search with relevance scoring | High | One per collection; Atlas Search for advanced needs |
| Partial | Queries on a filtered subset of documents | Low | Significantly reduces index RAM footprint |
| Sparse | Optional fields present in minority of documents | Very Low | Smaller than standard index on same field |
| TTL | Auto-expiring session or log data | Low | Background deletion; combine with sparse |
| Wildcard | Dynamic schemas with unpredictable field names | High | Use selectively on large collections |
| Vector | Semantic similarity search with embeddings | Very High | Now available in Community Edition in 8.0 |
Step 7: Schema Design and Data Modeling
The biggest mistake developers make when learning MongoDB is treating it like a schemaless free-for-all. In reality, good MongoDB schema design is a deliberate discipline – one that trades the rigid normalization rules of relational databases for a set of principles optimized around your application’s access patterns. The central question is: should I embed or reference?
Embed when: The related data is always read together with the parent document, the related data is not shared across multiple parents, and the embedded array will not grow without bound. Keep documents under 16 MB and embedded arrays under a few hundred elements for reliable performance.
Reference when: The related data is large or unbounded, the related data is shared across multiple parent documents, or you need to query the related data independently. References in MongoDB are application-level joins performed with the $lookup aggregation stage.
// Production-grade task document – applies embed vs reference principles
// users collection – normalized; referenced from tasks
{
_id: ObjectId(),
email: "[email protected]",
name: "Alex Rivera",
role: "developer",
teams: [ObjectId("team1"), ObjectId("team2")],
preferences: {
timezone: "America/New_York",
notificationChannels: ["email", "slack"]
},
createdAt: ISODate("2025-01-15")
}
// tasks collection – core document with strategic embedding
{
_id: ObjectId(),
title: "Implement vector search feature",
description: "Add semantic search using MongoDB 8.0 vector indexes",
status: "in-progress",
priority: "high",
// Denormalized assignee snapshot – fast reads, no join needed
assignee: {
userId: ObjectId("user123"),
name: "Alex Rivera",
email: "[email protected]"
},
projectId: ObjectId("project456"), // Referenced – queried independently
tags: ["ai", "search", "mongodb"],
// Comments embedded – bounded array, always read with task
comments: [
{
commentId: new ObjectId(),
authorId: ObjectId("user789"),
authorName: "Jordan Kim",
body: "Should we use Voyage AI embeddings or a custom model?",
createdAt: ISODate("2026-04-01")
}
],
// Status audit trail – capped at last 20 entries in application logic
statusHistory: [
{
from: "todo",
to: "in-progress",
changedAt: ISODate("2026-04-02"),
by: ObjectId("user123")
}
],
metadata: { estimatedHours: 8, actualHours: 3, sprint: 14, storyPoints: 5 },
// Vector embedding for semantic search (MongoDB 8.0)
titleEmbedding: [0.023, -0.145, 0.891], // 1024 dimensions in practice
dueDate: ISODate("2026-04-20"),
createdAt: ISODate("2026-04-01"),
updatedAt: ISODate("2026-04-02")
}
// Apply JSON Schema validation to enforce document structure
db.createCollection("tasks", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["title", "status", "priority", "createdAt"],
properties: {
title: { bsonType: "string", minLength: 1, maxLength: 500 },
status: {
bsonType: "string",
enum: ["todo", "in-progress", "completed", "overdue", "archived"]
},
priority: {
bsonType: "string",
enum: ["low", "medium", "high", "critical"]
},
dueDate: { bsonType: ["date", "null"] },
tags: { bsonType: "array", items: { bsonType: "string" } }
}
}
},
validationLevel: "moderate", // Validate inserts/updates, skip existing docs
validationAction: "error"
})
Schema validation with $jsonSchema gives you structural guarantees without sacrificing the flexibility to add fields dynamically. Use validationLevel: "moderate" in production to avoid breaking existing documents during schema evolution, and tighten to "strict" once your data is fully migrated to the new structure.
Step 8: MongoDB Atlas – Cloud Deployment
MongoDB Atlas is the fully managed cloud database service that eliminates operational overhead – automated backups, scaling, monitoring, and the rapid security patching that protected customers from CVE-2025-14847 are all handled by the Atlas control plane. MongoDB pushed that managed experience further on September 29, 2026 with the public preview of Atlas Infinite, a new tier built around consumption-based pricing that charges for the resources a workload actually uses rather than a fixed cluster size. For most teams, Atlas is the right default choice for anything beyond local development. The service runs on AWS, Google Cloud, and Azure, enabling deployment in the region closest to your users. Regional providers have also caught up on the managed-MongoDB front: Alibaba Cloud’s ApsaraDB for MongoDB shipped minor version 9.0.13 on August 25, 2026, built on MongoDB 8.0.29 and adding AES-GCM support inside its transparent data encryption (TDE), giving teams with an Asia-Pacific footprint a compliance-friendly managed alternative to Atlas.
Getting started with Atlas follows a straightforward path. After creating a free account at mongodb.com/cloud/atlas, you create an organization, a project, and a cluster. The M0 free tier is permanent – it does not expire after a trial period – and provides 512 MB of storage on a shared cluster, sufficient for learning and small projects.
| Atlas Tier | RAM | Storage | Monthly Cost (AWS us-east-1) | Best For |
|---|---|---|---|---|
| M0 (Free) | Shared | 512 MB | $0 | Learning, prototypes |
| M10 | 2 GB | 10 GB | ~$57 | Development, small apps |
| M20 | 4 GB | 20 GB | ~$114 | Staging, medium apps |
| M30 | 8 GB | 40 GB | ~$228 | Production OLTP |
| M50 | 16 GB | 80 GB | ~$456 | High-throughput production |
| M80 | 32 GB | 750 GB | ~$1,040 | Large-scale production |
Connecting your application to Atlas requires whitelisting your IP address or CIDR range, creating a database user, and retrieving your connection string. The SRV connection string format handles replica set discovery automatically:
# Connect via mongosh to Atlas
mongosh "mongodb+srv://taskapp:[email protected]/taskmanager?retryWrites=true&w=majority"
# Python connection with pymongo – production-ready configuration
from pymongo import MongoClient
from pymongo.server_api import ServerApi
ATLAS_URI = "mongodb+srv://taskapp:[email protected]/?retryWrites=true&w=majority"
client = MongoClient(
ATLAS_URI,
server_api=ServerApi("1"), # Stable API – prevents breaking changes
connectTimeoutMS=5000,
serverSelectionTimeoutMS=5000,
maxPoolSize=50, # Connection pool size
minPoolSize=5
)
try:
client.admin.command("ping")
print("Connected to MongoDB Atlas successfully")
except Exception as e:
raise SystemExit(f"Atlas connection failed: {e}")
db = client.taskmanager
tasks_collection = db.tasks
Atlas provides a built-in Performance Advisor that analyzes your query patterns and recommends indexes automatically – an invaluable tool for teams that lack dedicated DBA expertise. For teams deploying microservices on Kubernetes, see our Kubernetes tutorial for patterns on managing Atlas connection strings as Kubernetes secrets. If you are building a REST API layer on top of MongoDB, our FastAPI REST API tutorial covers the full async stack with the Motor driver.
Step 9: Vector Search and AI Integration
The acquisition of Voyage AI in February 2025 was a watershed moment for MongoDB’s AI strategy. Vector search – the ability to find semantically similar documents using embedding vectors – was previously available only in MongoDB Atlas. With MongoDB 8.0, vector search indexes are now supported in Community Edition, making semantic search a first-class capability across the entire MongoDB ecosystem. This is the feature that makes the mongodb database directly relevant to every team building AI-powered applications in 2026.
Automated Embedding in Vector Search, currently in public preview, takes this further. Instead of generating embeddings in your application code and storing them manually, you configure MongoDB to automatically generate and maintain embeddings using a configured model provider – initially Voyage AI models. This dramatically simplifies the retrieval-augmented generation (RAG) pipeline for most applications by eliminating the dual-write problem that makes self-managed embedding pipelines brittle.
// Step 1: Create a vector search index (Atlas or Community Edition 8.0)
db.tasks.createSearchIndex({
name: "task_vector_index",
type: "vectorSearch",
definition: {
fields: [
{
type: "vector",
path: "titleEmbedding",
numDimensions: 1024, // Voyage AI voyage-3 produces 1024-dim vectors
similarity: "cosine"
},
{ type: "filter", path: "status" }, // Pre-filter support
{ type: "filter", path: "priority" }
]
}
})
# Python: Generate embeddings and run semantic search
# pip install voyageai motor
import voyageai
import asyncio
from motor.motor_asyncio import AsyncIOMotorClient
voyage_client = voyageai.Client(api_key="your-voyage-api-key")
motor_client = AsyncIOMotorClient(ATLAS_URI)
db = motor_client.taskmanager
async def embed_task(task_doc: dict) -> None:
"""Generate and store embedding for a task document."""
text = task_doc["title"] + " " + task_doc.get("description", "")
result = voyage_client.embed([text], model="voyage-3", input_type="document")
await db.tasks.update_one(
{"_id": task_doc["_id"]},
{"$set": {"titleEmbedding": result.embeddings[0]}}
)
async def semantic_task_search(query: str, status_filter: str = None, limit: int = 10):
"""Find semantically similar tasks using vector search pipeline."""
query_embedding = voyage_client.embed(
[query], model="voyage-3", input_type="query"
).embeddings[0]
pipeline = [
{
"$vectorSearch": {
"index": "task_vector_index",
"path": "titleEmbedding",
"queryVector": query_embedding,
"numCandidates": limit * 10, # Search wider pool for better recall
"limit": limit,
"filter": {"status": {"$eq": status_filter}} if status_filter else {}
}
},
{
"$project": {
"title": 1, "status": 1, "priority": 1, "dueDate": 1,
"assignee.name": 1,
"score": {"$meta": "vectorSearchScore"}
}
}
]
results = await db.tasks.aggregate(pipeline).to_list(limit)
return results
# Example: find tasks related to "performance optimization"
results = asyncio.run(semantic_task_search("database performance tuning", limit=5))
for task in results:
print(f"[{task['score']:.3f}] {task['title']} ({task['status']})")
The numCandidates parameter controls the accuracy-performance tradeoff in vector search. Setting it to 10 times the desired limit gives good recall for most use cases. Increasing it improves result quality at the cost of query latency. For teams building Python automation workflows around this kind of AI pipeline, see our Python automation tutorial for complementary scheduling and orchestration patterns.
Step 10: Security, Authentication, and Backup
Security is not an afterthought in MongoDB – it is a configuration discipline that must be established before your database handles any real data. The CVE-2025-14847 vulnerability that required MongoDB Atlas to patch tens of thousands of clusters in 2025 was a reminder that database security involves the entire stack: the server, the network, the authentication layer, and the application code. Understanding each layer is essential for any production mongodb database deployment.
Authentication and Role-Based Access Control
MongoDB supports multiple authentication mechanisms. SCRAM-SHA-256 is the default and is appropriate for most deployments. X.509 certificate authentication is preferred for machine-to-machine authentication in zero-trust environments. LDAP integration is available in MongoDB Enterprise for centralized identity management.
// Create a custom role with principle of least privilege
use taskmanager
db.createRole({
role: "taskReporter",
privileges: [
{
resource: { db: "taskmanager", collection: "tasks" },
actions: ["find", "aggregate"]
},
{
resource: { db: "taskmanager", collection: "sprints" },
actions: ["find"]
}
],
roles: []
})
// Create application service user with restricted network access
db.createUser({
user: "taskapp-service",
pwd: passwordPrompt(),
roles: [
{ role: "readWrite", db: "taskmanager" },
{ role: "taskReporter", db: "taskmanager" }
],
mechanisms: ["SCRAM-SHA-256"],
authenticationRestrictions: [
{
clientSource: ["10.0.1.0/24"], // Restrict to app server subnet
serverAddress: ["10.0.2.50"]
}
]
})
Backup and Point-in-Time Recovery
MongoDB Atlas provides continuous backup with point-in-time recovery (PITR) for M10 and above clusters – you can restore to any second within your configured retention window. For self-hosted deployments, mongodump provides logical backups while filesystem snapshots with WiredTiger checkpoints provide faster recovery for large databases.
# Logical backup with mongodump
mongodump
--uri="mongodb://dbAdmin:password@localhost:27017/?authSource=admin"
--db=taskmanager
--out=/backup/$(date +%Y%m%d_%H%M%S)
--gzip
--numParallelCollections=4
# Restore from backup
mongorestore
--uri="mongodb://dbAdmin:password@localhost:27017/?authSource=admin"
--db=taskmanager
--gzip
--drop
/backup/20260403_120000/taskmanager/
# Export a collection to JSON for data migration
mongoexport
--uri="mongodb://dbAdmin:password@localhost:27017/?authSource=admin"
--db=taskmanager
--collection=tasks
--query='{"status": "completed"}'
--out=completed_tasks_export.json
# Configure Atlas continuous backup via CLI
atlas backups schedule update --clusterName myCluster
--referenceHourOfDay 3
--referenceMinuteOfHour 0
--restoreWindowDays 7
Common Pitfalls and How to Avoid Them
These are the most costly mistakes that teams make when working with MongoDB – each observed repeatedly across real production systems. Understanding them before you encounter them is the difference between a smooth deployment and a 3 AM incident.
Pitfall 1: Running without authentication enabled. MongoDB’s out-of-the-box configuration does not enforce authentication. Exposed MongoDB instances have been repeatedly involved in mass data breaches. Always enable security.authorization: enabled in mongod.conf, even in development. Never bind to 0.0.0.0 unless firewall rules restrict access at the network layer.
Pitfall 2: Not planning for document growth. Documents with unbounded arrays – comment threads, activity logs, audit trails – grow indefinitely. A document that starts at 2 KB can eventually hit MongoDB’s 16 MB document size limit and cause your application to throw errors unexpectedly. Use the bucket pattern for time series data and cap embedded arrays at a known maximum, migrating older entries to a separate collection when the cap is reached.
Pitfall 3: Ignoring the working set. MongoDB performance depends heavily on the working set – the actively read and written portion of your data – fitting in RAM. When the working set exceeds available RAM, WiredTiger begins evicting pages to disk and performance degrades sharply. Monitor the wiredTiger.cache.bytes currently in the cache metric and provision accordingly. This is the most common cause of unexpected performance degradation in growing applications.
Pitfall 4: Using $where or server-side JavaScript execution. MongoDB supports JavaScript execution in queries via $where, but this bypasses indexes completely, runs in a single-threaded engine, and is a security risk. Every $where query is effectively a collection scan. Replace them with native query operators – there is almost always a native equivalent that is faster and safer.
Pitfall 5: Creating too many indexes. Indexes are not free. Each index consumes RAM from the WiredTiger cache and adds overhead to every write operation. A collection with 20 indexes will have noticeably slower write throughput than one with 5 targeted indexes. Audit index usage regularly with $indexStats and drop indexes that show zero usage in the past 30 days.
Pitfall 6: Not using connection pooling. Opening a new MongoDB connection for each request in a web application creates severe overhead – connection establishment involves authentication, TLS handshake, and server-side resource allocation. Reuse a single MongoClient instance across your application’s lifetime and configure maxPoolSize and minPoolSize to match your concurrency requirements.
Pitfall 7: Over-normalizing the schema. Developers familiar with SQL often model MongoDB data with full normalization – one collection per entity, references everywhere. This forces multiple round-trips for common queries and loses the performance benefits of embedding related data. Design your schema around your most frequent access patterns, not around abstract relational theory. The question to ask is: “Will my application always need this data together?” If yes, embed it.
Pitfall 8: Deferring version upgrades until end-of-life pressure. MongoDB 6.0 reached end-of-life on July 31, 2025. Teams that deferred upgrading lost access to security patches at a critical time. Track MongoDB’s release lifecycle calendar and schedule major version upgrades at least six months before EOL. The upgrade path from 6.0 to 8.0 requires two sequential steps (6.0 to 7.0, then 7.0 to 8.0); plan your maintenance windows accordingly.
Pitfall 9: Misconfigured write concern leading to data loss. MongoDB’s default write concern (w: 1) acknowledges a write after it is applied to the primary only. For data you cannot afford to lose, use w: "majority" to ensure the write is durable on a majority of replica set members before acknowledgment. The performance cost is small but the durability guarantee is significant.
Pitfall 10: Running large aggregations without allowDiskUse. Aggregation pipeline stages that process more than 100 MB of data in memory will fail with an error unless you set allowDiskUse: true. For large reporting pipelines, always add this option: db.collection.aggregate(pipeline, { allowDiskUse: true }). Better yet, add a $match stage at the beginning to reduce the data volume before any grouping or sorting stages.
Troubleshooting Guide
Production MongoDB issues tend to fall into predictable categories. Here are the most common problems encountered when using the mongodb database in production, along with their root causes and resolution steps.
Issue 1: mongod fails to start – “Data directory not found”
This typically occurs after a fresh installation or when the data directory was moved. Resolution: sudo mkdir -p /var/lib/mongodb && sudo chown -R mongodb:mongodb /var/lib/mongodb. Verify the path matches storage.dbPath in mongod.conf. Check sudo journalctl -u mongod -n 50 for the full error context.
Issue 2: Authentication fails – “Command failed: Authentication failed”
Verify you are specifying the correct authSource database in your connection string. Most administrative users live in the admin database. Test with: mongosh "mongodb://user:pass@host:27017/?authSource=admin". Also confirm the user was created with the correct role and the password has not been rotated since the connection string was last updated.
Issue 3: Queries are slow – COLLSCAN on large collections
Run db.collection.find(query).explain("executionStats") and look for COLLSCAN in the winning plan. A collection scan means no index matches your query. Identify the fields in your filter and sort, create a compound index following the ESR rule, and re-run explain to confirm IXSCAN is now the winning plan. Check the totalDocsExamined versus nReturned ratio – it should ideally be 1:1.
Issue 4: “WriteConflict” errors in high-concurrency applications
WiredTiger uses optimistic concurrency control. Write conflicts occur when two operations attempt to modify the same document simultaneously. MongoDB’s driver automatically retries these, but frequent conflicts indicate a hot document being updated by many concurrent operations. Redesign to distribute writes across multiple documents – for example, use a counter-per-shard approach rather than a single global counter document.
Issue 5: Memory usage growing – high WiredTiger cache evictions
Monitor wiredTiger.cache["pages evicted by application threads"]. High eviction rates mean the cache is undersized for the working set. Short-term: reduce cacheSizeGB so eviction is smoother rather than bursty. Long-term: add RAM or move cold data to an archive collection. On Atlas, upgrade to a tier with more RAM or enable Auto-Scaling.
Issue 6: Replica set election causing brief unavailability
Elections occur when the primary becomes unavailable. The election protocol in MongoDB 8.0 typically completes in under 5 seconds. Configure retryWrites: true in your connection string (the default in modern drivers) and implement retry logic with exponential backoff. Applications should handle NotPrimaryError and ServerSelectionTimeoutError exceptions explicitly.
Issue 7: Atlas cluster approaching storage limit
Atlas alerts when storage exceeds 80%. For immediate relief: run db.runCommand({ compact: "tasks" }) to reclaim space from deleted documents, and check for unintentional data accumulation in log collections. Enable Atlas Online Archive to offload cold data to object storage automatically, reducing primary cluster storage consumption. For capacity planning, enable Auto-Scaling in cluster settings.
Issue 8: Connection pool exhaustion under load
Applications that queue for connections beyond waitQueueTimeoutMS receive connection errors. Symptoms: increasing response latency followed by timeouts under load. Resolution: increase maxPoolSize (default 100), but also investigate slow queries holding connections longer than expected. Atlas’s real-time performance panel shows active connections, current operations, and which queries are slowest – start the investigation there.
Issue 9: Time series collection ingestion failures
MongoDB 8.0 time series collections require the timeField to be a proper Date type – not a string. Sending an ISO 8601 string instead of a Date object causes validation failures. In Python: use datetime.fromisoformat("2026-04-03T12:00:00"). Also check that your granularity setting matches the actual data frequency – mismatches cause poor compression and potential ingestion errors for out-of-order data.
Issue 10: Vector search returning irrelevant results
The most common causes are: mismatched embedding models between indexing time and query time (always use the same model for both); embedding documents with HTML markup or boilerplate that pollutes the semantic signal (clean text before embedding); numCandidates set too low (increase to at least 10x the limit); and the vector index not yet fully built (check db.tasks.getSearchIndexes() for status “READY” before querying).
Issue 11: mongodump failing on large collections – timeout errors
For large collections, increase the --numParallelCollections flag and consider using --oplog for a consistent point-in-time snapshot on replica sets. If running against Atlas, use Atlas’s built-in backup instead of mongodump for large datasets – Atlas snapshots are taken at the storage layer and are far faster for large data volumes.
Issue 12: Schema validation rejecting legitimate documents after schema change
When you tighten a JSON Schema validator on a collection that has existing documents, the validationLevel: "strict" setting will reject updates to existing documents that do not comply with the new schema. Temporarily set validationLevel: "moderate" during migration, update existing documents to comply, then re-enable strict validation once all documents are conformant.
Frequently Asked Questions
Is MongoDB 8.0 backward compatible with MongoDB 7.0 application drivers?
Yes, MongoDB maintains wire protocol compatibility across major versions. Applications built against MongoDB 7.0-compatible drivers connect to a MongoDB 8.0 server without modification. However, to use new 8.0 features – enhanced time series collections, vector search in Community Edition, new aggregation operators – you should upgrade to the latest driver version. MongoDB recommends using the Stable API (ServerApi("1") in pymongo) to get an explicit compatibility guarantee that prevents your application from inadvertently relying on features that may change behavior between server releases.
Who is this mongodb tutorial for beginners versus experienced developers?
Beginners should focus on Sections 1 through 6 – installation, CRUD, and basic aggregation – before moving to advanced topics. Experienced developers with SQL backgrounds will find Section 7 (schema design) and Section 8 (Atlas) most valuable for calibrating their mental model of NoSQL data modeling. The vector search section (Step 9) is genuinely new territory for most database practitioners regardless of experience level, since the technology matured only in 2025 and the Community Edition support is brand new in MongoDB 8.0.
Should I use MongoDB Atlas or self-host MongoDB 8?
For most teams, Atlas is the right default. Automated backups, zero-downtime scaling, managed security patching (demonstrated clearly with CVE-2025-14847), and built-in performance monitoring are worth the cost premium over raw infrastructure. Self-hosting makes sense when you have strict data residency requirements that Atlas regions cannot satisfy, when you have existing infrastructure and dedicated database operations expertise, or when your workload characteristics make the Atlas pricing model unfavorable at your scale. The free M0 tier makes it trivial to start on Atlas and evaluate before committing.
What is the difference between MongoDB Atlas Search and the built-in text index?
MongoDB’s built-in text index provides basic full-text search with stemming, stop word removal, and relevance scoring. It is quick to set up and sufficient for simple keyword search. Atlas Search, powered by Apache Lucene, adds faceted search, autocomplete, fuzzy matching, custom analyzers, geospatial search, and significantly better relevance tuning. For production search features visible to end users, Atlas Search is almost always the better choice. For internal admin queries and simple filtering, the built-in text index is adequate and requires no additional configuration.
How do I migrate from MongoDB 6.0 to 8.0 now that 6.0 is end-of-life?
The migration path requires two sequential upgrades: 6.0 to 7.0, then 7.0 to 8.0. You cannot skip a major version. Before each upgrade: set the feature compatibility version to the current major version with db.adminCommand({ setFeatureCompatibilityVersion: "6.0" }), verify replica set health with rs.status(), take a full backup, then upgrade the mongod binary. The official MongoDB documentation provides detailed upgrade checklists for each version pair. On Atlas, the upgrade is managed through the cluster settings UI with automated compatibility validation.
Is vector search in Community Edition as capable as Atlas Vector Search?
MongoDB 8.0 Community Edition supports the core vector search capability – creating vector indexes and running $vectorSearch aggregation queries. Atlas Vector Search adds managed index building at scale, automatic scaling for large vector workloads, the Automated Embedding feature in public preview (which generates and maintains embeddings without application-level code), and the Atlas UI for index management and monitoring. For production AI applications with large embedding corpora and high query volumes, Atlas Vector Search is the more operationally mature choice. For development and moderate-scale deployments, Community Edition vector search is fully functional.
How do MongoDB 8.0 time series collections differ from regular collections?
Time series collections use a specialized columnar storage format that compresses time-ordered data far more efficiently than standard row-oriented BSON – typically achieving 60 to 80 percent compression ratios on numeric measurement data. They are optimized for append-only workloads and range queries on the time field. MongoDB 8.0 adds secondary indexes on measurement fields, allowing you to filter time series data on sensor IDs, geographic regions, or other dimensions without a full collection scan. Use time series collections for IoT telemetry, application metrics, financial tick data, and any workload where you predominantly insert time-stamped measurements and query date ranges.
What authentication mechanism should I use for production deployments?
For application-to-database authentication, SCRAM-SHA-256 (the default) is appropriate for most deployments. For machine-to-machine authentication in microservice architectures, X.509 certificate-based authentication eliminates password management and integrates naturally with mTLS service meshes. For enterprise environments with centralized identity, MongoDB Enterprise’s LDAP integration allows MongoDB to delegate authentication to your existing directory service, enabling you to enforce password policies, rotation schedules, and multi-factor authentication at the identity provider level. On Atlas, database user passwords can be rotated via the Atlas API or CLI without application downtime, making regular rotation operationally practical.
Related Coverage
Expand your database and infrastructure knowledge with these related guides from Tech Insider:
- MongoDB vs PostgreSQL 2026: Which Database Should You Choose?
- PostgreSQL Tutorial: Complete Database Guide for 2026
- FastAPI Tutorial: Build Production REST APIs with Python
- Docker Tutorial: Containerization for Beginners (2026)
- Kubernetes and Helm Chart Tutorial: Deploy Applications at Scale
- Python Automation Tutorial: Automate Tasks and Workflows
- Cloud Computing in 2026: Complete Platform Guide
This mongodb tutorial was last updated in October 2026. MongoDB 9.0 launched on September 29, 2026 as the new flagship release, while the 8.x series this guide focuses on remains current at patch version 8.0.34 (October 14, 2026) and 8.3.11 (September 17, 2026), with mongosh 2.12.0 and PyMongo 4.18 as the latest matching tooling. For the latest release notes and version-specific documentation, refer to the official MongoDB documentation and the MongoDB Developer Center.





