Automate database security best practices, both locally and in the cloud, including user management, networking, and more, all with the Atlas CLI. The standard journey for almost any software project starts as a POC on our local machines. It may quickly evolve into needing to plug into cloud services, and before long, we're pushing our v0.x variants onto the cloud. We break this up into three major development phases: local, hybrid, and cloud. The key part is to maintain a zero-friction experience along the way as developers iterate. At the same time, a brewing reality is preventing the software from being subject to a bad actor-i.e., let's not get hacked. Thus, basic security is non-optional.
With the MongoDB Atlas CLI, you can automate everything-cluster creation, user management, IP allowlisting, and backups-securely and reproducibly.
Security Checklist for Cloud-Based Services
Here's undeniably the most basic security checklist when running a database (or microservice) on a public cloud:
- Never expose your database (or service) directly to the public internet.
- Enforce strong authentication.
- Require TLS/SSL encryption in transit.
- Encrypt data at rest.
- Follow the Principle of Least Privilege.
- Manage IP allowlists/firewall rules.
- Use private networking.
- Configure monitoring, audit logs, and alerts.
- Keep the database (or service) updated.
Historically, security and productivity are often inversely proportional. It makes sense as we now have at least nine new checklist items to handle. However, productivity doesn't have to take a big dip as we can adopt the above security practices using the Atlas CLI.
What is the Atlas CLI?
It's a command-line interface for MongoDB, both locally and in Atlas, a MongoDB SaaS offering. It allows programmatic control of resources right from the terminal. The tool makes it easy to script away standard configurations, such as *hint hint* those related to security.
Here's how the Atlas CLI can help secure each of the three major development journey phases: local, hybrid, and cloud development:
- Local machine: Start now and script a local, secured environment that is cloud-ready. One way to achieve this is by scripting Atlas CLI commands in a Makefile.
- Hybrid (virtual space): The project becomes cloud-compatible, and we script the MongoDB setup using the Atlas CLI.
- Cloud (automating CI/CD): We leverage the Atlas CLI and GitHub Actions to quickly bring up and tear down secured MongoDB cluster(s), ensuring consistent compliance with defined best practices.
Below, we provide examples on how to achieve a secure environment by leveraging the Atlas CLI for each of the above development phases. Go ahead and jump right into the one that best resonates with your current project.
Atlas CLI for Local Development Via Makefile Targets and Recipes
Security doesn't have to wait until you're somewhere in the public-facing world. Many of the checklist items above, such as database credentials, remain standard best practices even in local-only environments. The Atlas CLI allows you to create local clusters (and Vector Search indexes). The full sample solution can be found in the GitHub repository.
Makefile target to Create a Local MongoDB Cluster
$ make local-up
atlas deployments setup my-secured-local-backend \
--type local \
--username root \
--password r00tr00t \
--force
[Default Settings]
Deployment Name my-secured-local-backend
MongoDB Major Version 8 (latest minor version)
Creating your cluster my-secured-local-backend
1/3: Starting your local environment...
2/3: Downloading the latest MongoDB image to your local environment...
3/3: Creating your deployment my-secured-local-backend...
Deployment created!
Connection string: "mongodb://root:r00tr00t@localhost:58991/?directConnection=true"
One key advantage of using the Atlas CLI to handle the database cluster creation is that it's bound to be a straightforward migration to the cloud when you're ready to make that move.
Makefile for Local MongoDB Replica Set and Vector Search
The Makefile for setting up a secured MongoDB replica set locally, and the steps to create a specified vector search index, are below:
# Makefile -- Local MongoDB Replica Set + Vector Search
.PHONY: help
help: ## Show available commands
@grep -E '^[a-zA-Z0-9_-]+:.*?## ' $(MAKEFILE_LIST) | \
awk 'BEGIN{FS=":.*?## "}{printf " %-22s %s\n", $$1, $$2}'
.PHONY: local-up
local-up: ## Create a secured local Atlas replica set
atlas deployments setup $(MONGODB_CLUSTER) \
--type LOCAL \
--username $(MONGODB_USERNAME) \
--password $(MONGODB_PASSWORD) \
--force
.PHONY: local-connect
local-connect: ## Open a mongosh session connected to the local cluster
atlas deployments connect $(MONGODB_CLUSTER) \
--type LOCAL \
--connectWith mongosh \
--username $(MONGODB_USERNAME) \
--password $(MONGODB_PASSWORD)
.PHONY: vector-index
vector-index: ## Create or update the vector search index
atlas deployments search indexes create $(VECTOR_INDEX_NAME) \
--deploymentName $(MONGODB_CLUSTER) \
--file $(VECTOR_INDEX_FILE) \
--type LOCAL \
--username $(MONGODB_USERNAME) \
--password $(MONGODB_PASSWORD)
Behind the scenes, the Atlas CLI uses a default image, mongodb/mongodb-atlas-local, for all local deployments. If you're looking for something custom and more advanced, you may also provide your custom image name via the MONGODB_ATLAS_LOCAL_DEPLOYMENT_IMAGE variable. For more information, visit the private registry documentation page.
The Atlas CLI for Secured, Cloud-Based MongoDB Clusters
In this phase, we explore using the Atlas CLI to manage a cloud-hosted database. MongoDB Atlas already handles many of the security items we listed earlier, thereby ensuring developer productivity remains frictionless. Only a couple of security options regarding authentication and networking are left up to us-the minimum required to thoroughly check every time in our list.
Let's review what MongoDB Atlas offers from a security perspective:
- No public exposure: MongoDB Atlas clusters are never exposed by default and can be isolated using IP allowlists and private networking.
- Strong authentication: Atlas enforces authentication for all access, supporting SCRAM, IAM integrations, and federated identity providers.
- Encryption in transit: All connections to Atlas require TLS/SSL encryption, and it cannot be disabled.
- Encryption at rest: MongoDB Atlas encrypts all data at rest by default, with support for customer-managed keys (BYOK) and Client-Side Field Level Encryption (CSFLE).
- Least privilege access: Atlas provides fine-grained role-based access control (RBAC) at the project, cluster, and database levels.
- Network access controls: MongoDB Atlas requires explicit IP allowlisting or private endpoints before any client can establish a connection.
- Private networking: Atlas supports VPC peering and cloud-native Private Link to keep traffic off the public internet.
- Monitoring and auditing: Atlas includes built-in monitoring, default alerts, activity tracking, and configurable audit logs.
- Secure updates: Atlas automatically applies minor patches and provides controlled workflows for major version upgrades.
With MongoDB Atlas, we can choose to deploy a MongoDB cluster on any of the three major cloud providers or opt for a multi-cloud deployment. For our case, we'll decide to deploy a small, free-forever cluster on GCP.
Below are the commands to complete all the steps from the earlier phase, albeit in a GCP environment rather than a local one.
1. Log in and Create a new Atlas Project
A project is a virtual space within your Atlas account that can contain one or more MongoDB deployments. Many of the security practices listed above are applicable at the project level. It is therefore common to use environment-specific projects, such as development (dev), staging, and production (prod).
You will need an Atlas account (a free one is sufficient) and credentials with the proper permissions (i.e., Organization Owner and Organization Project Creator) to log in.
Do you already have an Atlas project you'd like to use? Set ATLAS_PROJECT_ID from the âatlas projects list --output plainâ command.
Let's Create a Development Atlas Project:
# choose a login method from the list shown
atlas login
# list all the orgs you can access
atlas organizations list --output plain
# pick one organization ID from the list and save it in the variable below
ATLAS_ORG_ID=aaaa4d480111122223333999
# pick a name for your new Atlas project
ATLAS_PROJECT_NAME=dev-project
# create the atlas project via the CLI
atlas projects create $ATLAS_PROJECT_NAME --orgId $ATLAS_ORG_ID
# look at the output above and save the Atlas project ID as
ATLAS_PROJECT_ID=1111f64b9a111117a4561111
2. Set up networking access
Set up an IP allowlist for your Atlas project (a minimal requirement). At the very least, to communicate with an Atlas-hosted MongoDB cluster, you will need to add the IP(s) of your application(s) to the IP allowlist. And of course, we should avoid using 0.0.0.0/0.
MY_CURRENT_IP=$(curl ifconfig.me)
atlas accessLists create $MY_CURRENT_IP \
--type ipAddress \
--projectId $ATLAS_PROJECT_ID \
--comment "My local current IP address."
If your application is also running in one of the major cloud providers, then, though not required, private networking is a security best practice that we can configure. You can add private networking via MongoDB Atlas, but you will then need to accept the configuration from your cloud provider. You may establish peering or private link connectivity. Note that each of these has additional prerequisites of its own that must be met. This feature is not available for our free cluster.
3. Create the cluster, vector index, and database user
We have a secure Atlas project in place. Now, we can create our MongoDB cluster, database user, and, optionally, a vector search index for a given collection.
Create a free (M0) MongoDB cluster on the GCP US Central region with the Atlas CLI. I chose to name my MongoDB cluster knowledgeBase.
Note: Only one free M0 cluster is allowed per Atlas project.
MONGODB_CLUSTER=knowledgeBase
atlas clusters create $MONGODB_CLUSTER \
--projectId $ATLAS_PROJECT_ID \
--provider GCP \
--region CENTRAL_US \
--tier M0
It may take up to 10 minutes for the cluster to provision. You'll see a stateName of IDLE once it's fully up.
atlas clusters describe $MONGODB_CLUSTER \
--projectId "$ATLAS_PROJECT_ID"
Create a database user using built-in RBAC roles to maintain the Principle of Least Privilege. In our case, our application needs read and write access only to the âiotâ database within the knowledgeBase cluster. Thus, we added SCRAM (user/password) authentication to access our database. Other options are also available, including X.509 certificates, AWS IAM, and OIDC.
atlas dbusers create \
--username $MONGODB_USERNAME \
--password $MONGODB_PASSWORD \
--projectId $ATLAS_PROJECT_ID \
--role "readWrite@iot" \
--scope $MONGODB_CLUSTER
Obtain the SRV connection string (standardSrv) so your application can reach the MongoDB cluster:
- (info bubble) Why use a MongoDB SRV connection string?
- Using an SRV connection string (mongodb+srv://...) gives you a shorter, cleaner URI that automatically discovers the full seed list from DNS, so you don't have to list individual hosts or ports and your connection string stays the same even if the cluster topology changes (nodes added/removed, hostnames changed), typically without requiring application config changes or restarts.
- Because mongodb+srv:// uses DNS SRV/TXT records, drivers can transparently pick up the current set of servers, and they also default TLS to true.
atlas clusters connectionStrings describe $MONGODB_CLUSTER \
--projectId $ATLAS_PROJECT_ID
Test the Connection and Create the Iphone Collection.
atlas deployments connect $MONGODB_CLUSTER \
--connectWith mongosh \
--username $MONGODB_USERNAME \
--password $MONGODB_PASSWORD \
--projectId $ATLAS_PROJECT_ID
Current Mongosh Log ID: 695403e681552ce2b6fe0134
Connecting to: mongodb+srv://<credentials>@knowledgebase.mvjl04p.mongodb.net/?appName=mongosh+2.5.10
Using MongoDB: 8.0.17
Using Mongosh: 2.5.10
For mongosh info, see: https://www.mongodb.com/docs/mongodb-shell/
Atlas atlas-q78wrw-shard-0 [primary] test> use iot
switched to db iot
Atlas atlas-q78wrw-shard-0 [primary] iot> db.createCollection("iphone");
{ ok: 1 }
Atlas atlas-q78wrw-shard-0 [primary] iot> exit
Optionally, Create a Vector Search Index.
atlas clusters search indexes create \
--clusterName $MONGODB_CLUSTER \
--projectId $ATLAS_PROJECT_ID \
--file "vectorIndex.json"
What about Auditing?
We're on a free-forever cluster. Thus, this feature is not available. We can, however, enable auditing so that when we upgrade the cluster at a later time, it will be readily available.
atlas auditing update \
--projectId $ATLAS_PROJECT_ID \
--enabled \
--auditFilter '{}' \
--auditAuthorizationSuccess
Atlas CLI for CI/CD Pipelines via GitHub Actions
At this point, we can begin to refine other aspects of the development, such as the CI/CD pipeline to ensure a successful first launch and, of course, avoid potential security vulnerabilities along the way. A common automation decision may be to Dockerize everything, use Makefiles, and perhaps resort to a coding agent and MCPs to get us started. Yet, with each change, we need to ensure (1) we haven't broken anything existing and (2) pushing new features requires minimal to no manual processes. The idea is to script as much as possible to ease the burden of environment setup and ensure consistency across prod and non-prod environments.
Here's how we can use the Atlas CLI GitHub action to provision and deprovision automatically secured MongoDB deployments. In the example below, whenever a new feature branch is created, a dedicated test environment is provisioned for it. Consequent pushes to the feature branch can leverage the test sandbox environment to test the feature. Similarly, once the feature branch is closed, we can tear down the MongoDB Atlas test cluster with the custom delete job.
Store your Atlas credentials and default settings as GitHub Secrets and expose them as Atlas CLI environment variables. From your current GitHub repository, run the following:
source .env
gh secret set ATLAS_PUBLIC_API_KEY --body "$ATLAS_PUBLIC_API_KEY"
gh secret set ATLAS_PRIVATE_API_KEY --body "$ATLAS_PRIVATE_API_KEY"
gh secret set ATLAS_ORG_ID --body "$ATLAS_ORG_ID"
gh secret set MONGODB_CLUSTER --body "$MONGODB_CLUSTER"
gh secret set MONGODB_DATABASE --body "$MONGODB_DATABASE"
gh secret set MONGODB_COLLECTION --body "$MONGODB_COLLECTION"
gh secret set MONGODB_USERNAME --body "$MONGODB_USERNAME"
gh secret set MONGODB_PASSWORD --body "$MONGODB_PASSWORD"
Add the following GitHub Workflow:
# filename: feature-atlas-env.yml
name: Manage Feature Branch Atlas Environments
on:
create:
delete:
# permissions:
# contents: read
# actions: write
env:
# Atlas CLI auth/context
MONGODB_ATLAS_PUBLIC_API_KEY: ${{ secrets.ATLAS_PUBLIC_API_KEY }}
MONGODB_ATLAS_PRIVATE_API_KEY: ${{ secrets.ATLAS_PRIVATE_API_KEY }}
MONGODB_ATLAS_ORG_ID: ${{ secrets.ATLAS_ORG_ID }}
MONGODB_ATLAS_OUTPUT: json
# App-facing MongoDB settings (from GitHub Secrets)
MONGODB_CLUSTER: ${{ secrets.MONGODB_CLUSTER }}
MONGODB_DATABASE: ${{ secrets.MONGODB_DATABASE }}
MONGODB_COLLECTION: ${{ secrets.MONGODB_COLLECTION }}
MONGODB_USERNAME: ${{ secrets.MONGODB_USERNAME }}
MONGODB_PASSWORD: ${{ secrets.MONGODB_PASSWORD }}
jobs:
create-feature-env:
name: Create Atlas env for new feature branch
runs-on: ubuntu-latest
# Only run when a *branch* is created, and it starts with "feature/"
if: >
github.event_name == 'create' &&
github.event.ref_type == 'branch' &&
startsWith(github.event.ref, 'feature/')
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Derive feature env name from branch
id: naming
run: |
# github.event.ref is like "feature/my-new-thing"
BRANCH_REF="${{ github.event.ref }}"
SUFFIX="${BRANCH_REF#feature/}" # my-new-thing
SAFE_SUFFIX="$(echo "$SUFFIX" | sed 's/[^A-Za-z0-9-]/-/g')"
ENV_NAME="feature-${SAFE_SUFFIX}"
echo "env_name=$ENV_NAME" >> "$GITHUB_OUTPUT"
echo "Computed env name: $ENV_NAME"
- name: Setup Atlas CLI
uses: mongodb/atlas-github-action@v0.2.0
- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq
- name: Create Atlas project for this feature branch
id: create_project
shell: bash
run: |
ENV_NAME="${{ steps.naming.outputs.env_name }}"
echo "Creating project '$ENV_NAME' in org $MONGODB_ATLAS_ORG_ID"
PROJECT_JSON="$(atlas projects create "$ENV_NAME" \
--orgId "$MONGODB_ATLAS_ORG_ID" \
--output json)"
echo "$PROJECT_JSON"
PROJECT_ID="$(echo "$PROJECT_JSON" | jq -r '._id // .id')"
if [ -z "$PROJECT_ID" ] || [ "$PROJECT_ID" = "null" ]; then
echo "Failed to extract project ID from response"
exit 1
fi
echo "project_id=$PROJECT_ID" >> "$GITHUB_OUTPUT"
echo "Created project with ID: $PROJECT_ID"
- name: Create M0 cluster in project (GCP us-central)
shell: bash
run: |
ENV_NAME="${{ steps.naming.outputs.env_name }}"
PROJECT_ID="${{ steps.create_project.outputs.project_id }}"
echo "Creating M0 cluster '$MONGODB_CLUSTER' in project $PROJECT_ID (GCP CENTRAL_US)"
atlas clusters create "$MONGODB_CLUSTER" \
--projectId "$PROJECT_ID" \
--provider GCP \
--region CENTRAL_US \
--tier M0
atlas clusters watch "$MONGODB_CLUSTER" --projectId "$PROJECT_ID"
- name: Create database user for app
shell: bash
run: |
PROJECT_ID="${{ steps.create_project.outputs.project_id }}"
CLUSTER_NAME="${MONGODB_CLUSTER}"
echo "Creating DB user '$MONGODB_USERNAME' on db '$MONGODB_DATABASE' scoped to cluster '$CLUSTER_NAME'"
atlas dbusers create \
--username "$MONGODB_USERNAME" \
--password "$MONGODB_PASSWORD" \
--projectId "$PROJECT_ID" \
--role "readWrite@$MONGODB_DATABASE" \
--scope "$CLUSTER_NAME"
delete-feature-env:
name: Delete Atlas env for deleted feature branch
runs-on: ubuntu-latest
# Only run when a *branch* is deleted, and it starts with "feature/"
if: >
github.event_name == 'delete' &&
github.event.ref_type == 'branch' &&
startsWith(github.event.ref, 'feature/')
steps:
- name: Derive feature env name from deleted branch
id: naming
run: |
BRANCH_REF="${{ github.event.ref }}" # e.g. feature/my-new-thing
SUFFIX="${BRANCH_REF#feature/}"
SAFE_SUFFIX="$(echo "$SUFFIX" | sed 's/[^A-Za-z0-9-]/-/g')"
ENV_NAME="feature-${SAFE_SUFFIX}"
echo "env_name=$ENV_NAME" >> "$GITHUB_OUTPUT"
echo "Computed env name to delete: $ENV_NAME"
- name: Setup Atlas CLI
uses: mongodb/atlas-github-action@v0.2.0
- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq
- name: Find project by name
id: find_project
shell: bash
run: |
ENV_NAME="${{ steps.naming.outputs.env_name }}"
echo "Looking up project '$ENV_NAME' in org $MONGODB_ATLAS_ORG_ID"
PROJECTS_JSON="$(atlas projects list --output json)"
PROJECT_ID="$(echo "$PROJECTS_JSON" | jq -r --arg NAME "$ENV_NAME" \
'.results[] | select(.name == $NAME) | .id // .projectId // ."_id"' | head -n1)"
if [ -z "$PROJECT_ID" ] || [ "$PROJECT_ID" = "null" ]; then
echo "No project found for '$ENV_NAME'. Nothing to delete."
echo "project_id=" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Found project ID: $PROJECT_ID"
echo "project_id=$PROJECT_ID" >> "$GITHUB_OUTPUT"
- name: Delete cluster (if present)
if: steps.find_project.outputs.project_id != ''
shell: bash
run: |
PROJECT_ID="${{ steps.find_project.outputs.project_id }}"
ENV_NAME="${{ steps.naming.outputs.env_name }}"
echo "Checking for cluster '$MONGODB_CLUSTER' in project $PROJECT_ID"
if atlas clusters describe "$MONGODB_CLUSTER" --projectId "$PROJECT_ID" >/dev/null 2>&1; then
echo "Deleting cluster '$MONGODB_CLUSTER'..."
atlas clusters delete "$MONGODB_CLUSTER" \
--projectId "$PROJECT_ID" \
--force \
--watch
else
echo "No cluster named '$MONGODB_CLUSTER' found; skipping."
fi
- name: Delete project
if: steps.find_project.outputs.project_id != ''
shell: bash
run: |
PROJECT_ID="${{ steps.find_project.outputs.project_id }}"
ENV_NAME="${{ steps.naming.outputs.env_name }}"
echo "Deleting project '$ENV_NAME' (ID: $PROJECT_ID)"
atlas projects delete "$PROJECT_ID" \
--force
Create a Feature Branch.
git switch -c feature/itworks
git push -u origin feature/itworks
Delete the Feature Branch from Remote.
git push origin --delete feature/itworksWhat about Setting up Private Networking?
Because private networking requires back-and-forth synchronization between your cloud provider and MongoDB Atlas, you can set up this connectivity outside of your CI/CD. Alternatively, if you genuinely need to start with a clean slate each time, consider using the Terraform MongoDB Atlas provider to completely automate the lifecycle of your cloud resources and their interdependencies.
Key Takeaways
The MongoDB Atlas CLI brings automation, consistency, and security to every stage of deployment. We reviewed how to leverage the Atlas CLI to help maintain security best practices while not hindering productivity throughout a software project, from its early stages to production. In particular, we learned that:
- You can script secure local MongoDB environments with the Atlas CLI (for example, via Makefile targets) so the development environment is reproducible and cloud-ready from day one.
- MongoDB Atlas plus the Atlas CLI lets you automate core security controls for cloud deployments, such as networking (IP allowlists, private networking options), authentication, RBAC-based least privilege, and encrypted connections.
- Integrating the Atlas CLI into CI/CD (such as GitHub Actions) allows you to automatically provision and deprovision secured, environment-specific test clusters for feature branches, ensuring consistency and reducing manual security drift.
Many security best practices can be implemented as a one-time effort with Atlas CLI. After testing it, this can then become a template applied to all future projects and subsequent development environments.
Install the Atlas CLI and use the provided example GitHub repo to script your local, cloud, and CI/CD database setups, ensuring your next project launch is secure and reproducible.
Related Questions and Answers
1. What is the primary benefit of using the Atlas CLI for security?
The main benefit is achieving security best practices through automation and scripting, which ensures consistency, reduces manual configuration errors, and maintains developer productivity across local, hybrid, and cloud environments.
2. Why should I use an SRV connection string?
An SRV connection string (e.g., mongodb+srv://...) is recommended because it is shorter, automatically discovers the cluster topology from DNS, and ensures that connections inherently default to using TLS/SSL encryption.
3. How does the Atlas CLI help enforce the Principle of Least Privilege?
A: The CLI allows you to create database users and explicitly define their permissions using built-in RBAC roles (e.g., --role "readWrite@mydatabase") and scope them to a specific cluster, ensuring the application only has the minimum access required.
4. What is the solution for the "IP_ADDRESS_NOT_ON_ACCESS_LIST" error?
This error indicates that the IP address from which you are trying to connect (or use the API) is not in the organization's or project's IP allowlist. You must update your API Access List with the allowed IP address(es).
5. What is needed to create a Vector Search index with the Atlas CLI?
You must first ensure that the target database collection exists before attempting to create the Vector Search index. If the collection does not exist, you will receive an ATLAS_SEARCH_COLLECTION_NOT_FOUND error
Connect to the MongoDB cluster via mongosh and run:
```test> use iot
switched to db iot
iot> db.createCollection("iphone");
{ ok: 1 }```