← Back to Blog
DevOps & Systems

How to Set Up Automated Daily Backups for MongoDB to an External S3 Object Storage Bucket

By JustinPublished September 28, 2026Updated September 29, 2026
Automated MongoDB daily backup from a VPS to external S3-compatible object storage

I run MongoDB on the same droplet as the apps that use it β€” which is fine for normal operation, and a genuinely bad plan for backups. Every database I had lived on exactly one disk, on exactly one server. I'd already dealt with one server-level incident on this same box (a cryptomining compromise I've written about separately), and that was the point where "the backup is on the same machine as the thing it's backing up" stopped being an acceptable gap. If that disk fails, gets corrupted, or the server itself has a serious problem, a local-only backup is no protection at all β€” it goes down with everything else.

The fix is straightforward in concept: back up on a schedule, and send that backup somewhere physically and logically separate from the server it came from. Here's the actual setup.

Step 1: Taking a Backup With mongodump

MongoDB's own mongodump utility produces a backup of a database (or the whole instance) that can be restored with mongorestore. The --archive flag bundles everything into a single file rather than a directory of files, and --gzip compresses it β€” both make the next step (uploading it somewhere) simpler:

bash
mongodump --uri="mongodb://127.0.0.1:27017/mydb" --gzip --archive=/tmp/mydb-backup.gz

This produces one compressed file containing a full logical backup of the database. For a small-to-medium sized MongoDB instance, this approach is simple and sufficient β€” very large deployments sometimes need filesystem-level snapshotting instead for speed, but that's a different scale of problem than most self-managed setups actually have.

Step 2: An S3-Compatible Bucket (Doesn't Have to Be AWS)

"S3" here means the S3 API, not necessarily Amazon specifically β€” DigitalOcean Spaces, Backblaze B2, and several other object storage providers all speak the same S3-compatible API, which means the same tooling works against any of them by pointing at a different endpoint. Since my droplet is already on DigitalOcean, using Spaces keeps the backup traffic on the same provider's network rather than introducing another vendor, though the setup below works identically against any S3-compatible provider.

The AWS CLI works against any of these with an --endpoint-url override:

bash
aws s3 cp /tmp/mydb-backup.gz s3://my-backup-bucket/mongodb/ \
  --endpoint-url https://nyc3.digitaloceanspaces.com

MongoDB mongodump backup uploaded from a production VPS to an external S3-compatible object storage bucket

Step 3: A Script That Actually Does the Full Job

Individually running a dump command and an upload command isn't automation β€” the actual script needs a timestamped filename (so each day's backup doesn't overwrite the last one), and cleanup of the local temporary file once it's safely uploaded:

bash
#!/bin/bash
set -e  # exit immediately if any command fails, rather than continuing on a broken state

DATE=$(date +%Y-%m-%d_%H-%M-%S)
BACKUP_FILE="/tmp/mongo-backup-${DATE}.gz"
BUCKET="s3://my-backup-bucket/mongodb"
ENDPOINT="https://nyc3.digitaloceanspaces.com"

echo "Starting backup: ${DATE}"

mongodump --uri="$MONGO_URI" --gzip --archive="$BACKUP_FILE"

aws s3 cp "$BACKUP_FILE" "$BUCKET/" --endpoint-url "$ENDPOINT"

rm "$BACKUP_FILE"

echo "Backup complete and uploaded: mongo-backup-${DATE}.gz"

Automated MongoDB backup workflow using cron mongodump S3 upload error handling and temporary file cleanup

set -e matters more than it looks like it should β€” without it, if mongodump fails partway through for any reason, the script keeps going and attempts to upload a partial or missing file, then deletes the local temp file regardless, potentially leaving you with neither a valid local backup nor a valid remote one. Failing loudly and immediately is much better than silently producing a broken backup.

Step 4: Scheduling It With Cron

A daily backup at a fixed, low-traffic time (early morning, in whatever timezone your actual traffic is lightest) minimizes the performance impact of the dump running against a live database:

bash
crontab -e
text
0 3 * * * /path/to/backup-script.sh >> /var/log/mongo-backup.log 2>&1

This runs at 3 AM daily, and β€” importantly β€” redirects both stdout and stderr to a log file. Without that redirect, a cron job's output goes nowhere you'll ever see it, which means a script that's been silently failing every night for weeks can look identical to one that's working, right up until you actually need a backup and discover there isn't one.

Step 5: Retention β€” Don't Keep Every Backup Forever

Storage costs aside, an unbounded pile of daily backups going back years isn't actually more useful than a sensible retention window β€” you're very unlikely to need to restore from a backup that's two years old, and every provider's object storage has a lifecycle policy feature built for exactly this. On DigitalOcean Spaces (and most S3-compatible providers), this is configured on the bucket itself rather than in your script:

  • Set a lifecycle rule that automatically deletes objects under a given prefix after a set number of days
  • This runs entirely on the storage provider's side β€” your backup script doesn't need any deletion logic of its own
Handling retention at the storage layer rather than scripting it yourself means one less thing that can go wrong in your own automation, and it keeps working correctly even if your backup script itself has a bug.

Step 6: The Part Almost Everyone Skips β€” Actually Testing a Restore

A backup you have never restored from is a backup you don't actually know works. This is the step that's easy to skip because everything up to here already gives you a comforting green checkmark β€” the script ran, the file uploaded, the log says success. None of that confirms the backup file is actually valid or complete.

Periodically (not necessarily every day, but on a real recurring schedule), pull down a recent backup and restore it somewhere that isn't your production database β€” a local machine, a throwaway test instance:

bash
aws s3 cp s3://my-backup-bucket/mongodb/mongo-backup-2026-09-20.gz . \
  --endpoint-url https://nyc3.digitaloceanspaces.com

mongorestore --gzip --archive=mongo-backup-2026-09-20.gz --uri="mongodb://127.0.0.1:27017/restore-test"

MongoDB backup restore testing workflow using an S3 backup and a separate test database

Then actually check that the restored data looks correct and complete β€” right document counts, recent data present, nothing obviously missing. This is the difference between "I have a backup process" and "I have a backup process I've verified actually works," and only one of those is something you can rely on in an actual emergency.

Securing the Credentials

The AWS CLI needs credentials to authenticate against your bucket, and those shouldn't be full-account keys with access to everything in your storage account. Create a scoped API key or access key limited specifically to the one backup bucket, with only the permissions the script actually needs (write access, and read access if you're scripting restore verification too) β€” not broader account-level access. If that key were ever exposed, the blast radius should be "this one backup bucket," not your entire cloud account.

Store the credentials as environment variables or in a restricted-permission credentials file, not hardcoded directly in the backup script itself, especially if that script ever ends up in a git repository.

Frequently Asked Questions

Is mongodump sufficient, or do I need a more advanced backup method?

For most self-managed, small-to-medium MongoDB deployments, mongodump/mongorestore is genuinely sufficient and simple to reason about. Larger deployments with strict uptime requirements sometimes use filesystem or volume-level snapshots for faster backup and restore at scale, but that's solving a different problem (backup speed and minimal downtime at large data volumes) than most single-server setups actually have.

How often should backups actually run?

Daily is a reasonable default for most use cases. If your data changes rapidly and losing a day's worth would be genuinely costly, more frequent backups (every few hours) are worth the additional storage cost β€” the right frequency depends on how much data loss between backups would actually be acceptable to you, not a fixed universal number.

Should backups be encrypted?

Given that a database backup often contains everything sensitive your application stores, encrypting backups at rest is worth doing if your storage provider supports server-side encryption (most S3-compatible providers do, often as a simple bucket-level setting) or by encrypting the file yourself before upload. This is separate from bucket access control β€” encryption protects the data even if the storage bucket's access controls are ever misconfigured.

What's the actual cost of running this?

Object storage pricing for backups is typically low relative to compute costs β€” a compressed daily database dump for a small-to-medium site is usually a small fraction of overall hosting cost, and retention policies (Step 5) keep that from growing unbounded over time. The cost of not having backups, if something does go wrong, is categorically different from a storage bill.

Further Reading

Tags:MongoDBbackupsS3cron jobsDevOpsdata safetyobject storage

Justin is a self-taught developer who builds and runs DeelCart himself β€” from the articles to the server it runs on. He manages his own Linux infrastructure and writes guides based on tools and workflows he actually uses day to day.

πŸŽ“ Find Free Udemy Courses

Browse thousands of 100% OFF coupons updated daily.

Browse Free Courses β†’