Migrating from IBM Cloudant to managed Apache CouchDB: a zero-downtime guide
Short version: replicate one database as a smoke test, replicate the rest, move design documents and rebuild any Cloudant Search indexes as Mango indexes, switch to continuous replication, then cut the application over and verify document counts. Because CouchDB replication is symmetric, rollback is the same call with the endpoints swapped, which is what makes the cutover genuinely zero-downtime rather than nominally so. The things that do not come across are Cloudant Search indexes, IAM keys, and anything Cloudant-specific in your access setup.
CouchDB's replication protocol makes leaving any host straightforward in a way that almost no other database can match. There is no pg_dump, no mongoexport, no extraction step at all. You point one CouchDB at another, hit POST /_replicator, and the data flows. The same protocol works between Cloudant and any Apache CouchDB instance, in either direction, which means a Cloudant migration can run as a zero-downtime cutover with rollback that is just as one-sided.
This guide walks the full migration from IBM Cloudant to a managed Apache CouchDB on Layerbase Cloud. It covers the replication setup, what transfers and what does not, how to verify, how to cut over with zero downtime, and how to roll back if something goes sideways.
If you are still deciding whether to leave, the related posts cover the why: Cloudant deprecations in 2025 and 2026 lists what has changed, and Cloudant vs Layerbase Cloud is the head-to-head.
Contents
- Before you start
- Step 1: Provision the target CouchDB
- Step 2: Replicate one database as a smoke test
- Step 3: Replicate every database
- Step 4: Migrate design documents and indexes
- Step 5: Switch to continuous replication for zero downtime
- Step 6: Cut over the application
- Step 7: Verify and decommission Cloudant
- What does not transfer
- Rollback plan
- Migration with
couchbackupas a fallback - FAQ
Before you start
Gather the following:
- Cloudant credentials with read access to every database you want to migrate. Legacy username and password works, IAM API keys work, the only requirement is that the credentials can read the source databases and
_all_dbs. - An inventory of databases.
GET /_all_dbson Cloudant returns the full list. Save it, you will reuse it. - An inventory of design documents.
GET /{db}/_design_docs?include_docs=trueper database. Design docs replicate normally, but it is worth knowing up front whether any of them use_show,_list,_update, or_rewritehandlers (deprecated and unsupported on Cloudant, but still functional on Apache CouchDB; see the deprecations post). - A list of Cloudant Search indexes. These are Lucene-backed and Cloudant-only. They will not replicate to vanilla Apache CouchDB. See What does not transfer for the workaround.
- A list of IAM API keys your application uses. They will not work against the new instance, and you will need to rotate.
Document counts for each source database, captured before the migration starts, give you a clean before/after to verify against:
for db in $(curl -s "https://$CLOUDANT_AUTH@$CLOUDANT_HOST/_all_dbs" | jq -r '.[]'); do
count=$(curl -s "https://$CLOUDANT_AUTH@$CLOUDANT_HOST/$db" | jq '.doc_count')
echo "$db,$count"
done > cloudant-doc-counts.csvStep 1: Provision the target CouchDB
Create a CouchDB instance on Layerbase Cloud. CouchDB runs on the Pro plan at $15/mo, which covers up to 10 databases and 25 GB of storage, adds no database-count cap inside the instance itself, and runs the upstream Apache CouchDB binary (so design documents, Mango queries, MapReduce views, and the replicator all behave the same as on any other Apache CouchDB host). Pro includes a 7-day trial, which is usually enough to rehearse the whole migration before you commit.
Grab the connection URL from the instance page. It looks like:
https://admin:GENERATED_PASSWORD@your-host.cloud.layerbase.devConfirm it responds:
curl https://admin:GENERATED_PASSWORD@your-host.cloud.layerbase.dev/{"couchdb":"Welcome","version":"3.5.0","features":["access-ready","partitioned","reshard","scheduler"],"vendor":{"name":"The Apache Software Foundation"}}Export it for the rest of the steps:
export TARGET="https://admin:GENERATED_PASSWORD@your-host.cloud.layerbase.dev"
export SOURCE="https://APIKEY:PASSWORD@your-cloudant.cloudantnosqldb.appdomain.cloud"Step 2: Replicate one database as a smoke test
Pick the smallest database first. The goal of this step is to flush out any auth or networking issues before you commit to a long-running batch.
curl -X POST $TARGET/_replicator \
-H "Content-Type: application/json" \
-d "{
\"_id\": \"migrate-smoketest\",
\"source\": \"$SOURCE/smalldb\",
\"target\": {
\"url\": \"$TARGET/smalldb\",
\"headers\": {}
},
\"create_target\": true,
\"continuous\": false
}"Watch progress:
curl $TARGET/_scheduler/docs/_replicator/migrate-smoketestWhen state returns "completed", compare counts:
curl -s $SOURCE/smalldb | jq '.doc_count'
curl -s $TARGET/smalldb | jq '.doc_count'These should match. If they do not, the most common causes are documents with conflicts (replicated, but counted differently) or partial reads against Cloudant Search-backed views (which are not part of _all_docs and therefore not a discrepancy, just confusing).
Step 3: Replicate every database
Once the smoke test passes, do the rest. For a small fleet, a loop over your saved _all_dbs list works:
while read -r db; do
curl -X POST $TARGET/_replicator \
-H "Content-Type: application/json" \
-d "{
\"_id\": \"migrate-$db\",
\"source\": \"$SOURCE/$db\",
\"target\": {\"url\": \"$TARGET/$db\"},
\"create_target\": true,
\"continuous\": false
}"
done < db-list.txtFor larger fleets (dozens or hundreds of databases), use IBM's couchreplicate CLI. It parallelises with a bounded concurrency, retries on failure, and renders a TUI progress bar. The same tool runs unmodified against any source-target pair, not just Cloudant-to-Cloudant.
npm install -g couchreplicate
couchreplicate -s "$SOURCE" -t "$TARGET" -c 4The -c 4 flag is concurrency. Four parallel replications is a safe starting point. Bump it if Cloudant tolerates it (watch for 429: too many requests from the source side; that is your signal to back off).
Step 4: Migrate design documents and indexes
Design documents replicate as regular documents, so they come across as part of step 3. There is no separate "index" migration step in CouchDB, the indexes are derived from the design documents and rebuild on first query against the new host.
Two things to be aware of:
- First-query latency is real. A freshly-replicated database has design docs but no built indexes. The first Mango query that hits a new index pays the build cost. For a large database this can be seconds to minutes. The fix is to warm the indexes after replication and before cutover:
for view in mango-index-1 mango-index-2; do
curl -X POST $TARGET/yourdb/_find \
-H "Content-Type: application/json" \
-d "{\"selector\": {\"_id\": {\"\$gt\": null}}, \"limit\": 1, \"use_index\": \"$view\"}"
done- Cloudant Search indexes do not transfer. These are the Lucene-backed full-text indexes you create through Cloudant's
_searchendpoint. They live outside the CouchDB replication protocol and are a Cloudant-only feature. Migration options are:
- Rebuild as Mango indexes if the queries are structured rather than full-text.
- Rebuild as full-text in a separate engine (Meilisearch is a good fit and is also available on Layerbase Cloud). Stream document changes from CouchDB into the search engine through the
_changesfeed. - Self-host an Apache CouchDB build that ships with Lucene (some 3.x packagings include it; the standard Apache binary does not).
Step 5: Switch to continuous replication for zero downtime
The one-shot replications from step 3 leave you with two databases that are point-in-time identical. For a zero-downtime cutover, switch them to continuous mode so the target keeps catching up while the application is still writing to Cloudant.
For each database, post a new replicator doc with continuous: true:
curl -X POST $TARGET/_replicator \
-H "Content-Type: application/json" \
-d "{
\"_id\": \"sync-$db\",
\"source\": \"$SOURCE/$db\",
\"target\": {\"url\": \"$TARGET/$db\"},
\"continuous\": true
}"Verify everything is in running state:
curl -s $TARGET/_scheduler/docs/_replicator | jq '.docs[] | {id: .id, state: .state}'You can leave continuous replication running for hours or days. It is idempotent, restartable, and survives target restarts.
Step 6: Cut over the application
When you are ready:
- Quiesce writes to Cloudant. Either deploy a maintenance-mode flag in your app, route writes to the new endpoint, or accept a short read-only window.
- Wait for the replicator to drain. Watch the
_scheduler/docsoutput untilinfo.changes_pending(or equivalent) is zero for every replication. - Stop the continuous replications.
DELETE /_replicator/sync-$db. - Verify final document counts match the source one more time, including any documents written in the last minute.
- Flip the application's connection string to the new host, redeploy, and resume writes.
Total downtime if you do this carefully is whatever your deploy pipeline takes, often seconds.
Step 7: Verify and decommission Cloudant
Run the count script from the Before you start section again, against the Layerbase target. Diff against the pre-migration CSV:
diff cloudant-doc-counts.csv layerbase-doc-counts.csvFor deeper verification, spot-check a few documents by _id:
for id in $(shuf -n 20 sample-doc-ids.txt); do
cloudant_doc=$(curl -s "$SOURCE/yourdb/$id")
target_doc=$(curl -s "$TARGET/yourdb/$id")
diff <(echo "$cloudant_doc") <(echo "$target_doc")
doneThe _rev values should match exactly. If they do not, the source had writes the replicator did not pick up, which means you cut over before the continuous replication caught up.
Once you are satisfied, leave the Cloudant instance running for a week or two as a hot fallback. You are still paying for it during that window, but the cost of an unplanned rollback during the first week post-cutover usually exceeds the marginal Cloudant bill. After that, delete the Cloudant databases (carefully, ideally one at a time with at least a day between each in case the application surfaces a missed dependency) and finally the instance.
What does not transfer
A short list of things that will not come across automatically and need a per-case decision:
| Item | Reason | Action |
|---|---|---|
| Cloudant Search indexes | Lucene, Cloudant-only feature | Rebuild as Mango or as a separate full-text engine. |
| IAM API keys | Cloudant-account-scoped | Generate new credentials on the target, update app config. |
| Cloudant-specific monitoring and dashboards | Tied to IBM Cloud | Re-create on your new host's observability stack. |
| Per-account capacity and rate-limit configuration | Cloudant Standard plan concept, does not exist on Apache CouchDB | Drop. Apache CouchDB does not throttle the same way. |
CouchApp _show, _list, _update, _rewrite handlers using QuickJS-incompatible JS | The design docs replicate, but the JS engine on Apache CouchDB may still be SpiderMonkey | Test on the target. Most CouchApps work unchanged. See the deprecations post for context. |
Things that do transfer cleanly, in case you were wondering:
- All JSON documents and their full revision history.
- Design documents and the MapReduce views they define.
- Mango indexes (the JSON-only ones, not Lucene-backed
_search). - Document attachments (
_attachments), via the standard replicator. - Conflicts, with both winning and losing revisions preserved.
Rollback plan
The same protocol that gets you onto Layerbase gets you back to Cloudant if something is wrong. Two options:
- Continuous replication in reverse. Before cutover, also set up continuous replication from Layerbase back to Cloudant. After cutover, Cloudant stays warm with the new writes. If you need to roll back, flip the connection string and the data is already there.
curl -X POST $SOURCE/_replicator \
-H "Content-Type: application/json" \
-d "{
\"_id\": \"reverse-$db\",
\"source\": \"$TARGET/$db\",
\"target\": \"$SOURCE/$db\",
\"continuous\": true
}"- Catch-up replication on demand. If you skipped the reverse-replication and need to roll back, run a one-shot from Layerbase to Cloudant before flipping the connection string. This costs a few minutes of replication time, which is fine for an unplanned rollback.
Both options exist because CouchDB's replication is symmetric. The "source" and "target" are conventions, not roles.
Migration with couchbackup as a fallback
If your network policy does not allow direct CouchDB-to-CouchDB replication (for example, a corporate proxy that blocks one of the endpoints), you can fall back to IBM's couchbackup tool. It dumps a Cloudant database to a local JSON-Lines file, which you then restore against any CouchDB.
npm install -g @cloudant/couchbackup
# Backup
couchbackup --url "$SOURCE" --db yourdb > yourdb-backup.txt
# Restore against the target
cat yourdb-backup.txt | couchrestore --url "$TARGET" --db yourdbA few caveats:
couchbackupdoes not back up attachments. If your databases use attachments, replication is the only sensible path.- Document conflicts are preserved by default. The
--mode shallowflag drops them for speed. - The output is large but compresses well. Pipe through
gzipif you are storing the dump.
Direct replication is faster, simpler, and lossless. Use couchbackup only if direct replication is blocked.
FAQ
Is this really zero downtime?
It can be, and the mechanism is continuous replication rather than optimism. Once continuous replication is running, both sides stay current, so the cutover is a connection-string change in your application rather than a window where writes have nowhere to go. What you should still plan for is the moment after cutover, which is why the guide leaves Cloudant running as a hot fallback.
How do I roll back?
Run the replication in the other direction. That is the whole answer, and it is unusual: with almost any other database, rollback after a cutover means reversing a migration you built one-way. CouchDB's protocol is symmetric between any two instances, so the fallback path is the same call with source and target swapped.
Does document revision history survive?
Yes, along with attachments and conflicts, both winning and losing revisions included. Design documents and the MapReduce views they define come across too, as do JSON Mango indexes. What does not is anything Lucene-backed, which on Cloudant means _search.
What do I have to rebuild by hand?
Four things, and none of them are the data. Cloudant Search indexes get rebuilt as Mango indexes or handed to a dedicated full-text engine. IAM keys are account-scoped, so you generate new credentials and update your app config. Cloudant-specific monitoring and dashboards are re-created on your new observability stack. And per-account capacity and rate-limit configuration simply has no counterpart, because Apache CouchDB does not throttle that way.
Will my CouchApp design docs still work?
Usually, and it is worth testing rather than assuming. The design documents replicate fine; what varies is the JavaScript engine evaluating them, since Cloudant is moving to QuickJS while an Apache CouchDB host may still be on SpiderMonkey. Most CouchApps work unchanged. The deprecations post covers what actually differs between the two engines.
Where to go from here
- Provision the target in the next 60 seconds: Create a CouchDB on Layerbase Cloud.
- Read about what changed in Cloudant that prompted this migration: IBM Cloudant deprecations in 2025 and 2026.
- See a feature-by-feature comparison: Cloudant vs Layerbase Cloud.
- Browse other managed CouchDB options: Cloudant alternatives.
Migrating off Cloudant is mostly a matter of running POST /_replicator once per database and verifying the doc counts. Continuous replication makes the cutover zero-downtime, and the same protocol works in reverse if you need to roll back. There is no other database where the migration story is this clean.
Keep reading
- IBM Cloudant deprecations in 2025 and 2026: database caps, QuickJS, and removed featuresA complete, dated rundown of what IBM Cloudant has changed, deprecated, or removed in the last 18 months, including the new 20-database Lite-plan cap, the SpiderMonkey to QuickJS cutover on October 6, 2026, and the removed CouchApp design-doc handlers. With migration paths if any of these break your app.
- IBM Cloudant vs Layerbase Cloud: managed CouchDB compared in 2026A direct head-to-head between IBM Cloudant and Layerbase Cloud for managed Apache CouchDB hosting. Compares entry pricing, database-count caps, the billing model, JavaScript engine, migration path, region coverage, and operational ergonomics, with a recommendation for each common workload.
- Serverless CouchDB in 2026: scale-to-zero managed Apache CouchDBWhat "serverless CouchDB" actually means in 2026, why the category is small (one long-running hosted option, IBM Cloudant, plus flat-rate managed CouchDB on Layerbase Cloud), and how to ship an offline-first or document-store app without managing the database yourself.
- Cloudant alternatives: managed CouchDB hosting in 2026IBM Cloudant has been the default managed CouchDB for over a decade, and the Lite free tier has quietly gotten less generous. Here are the modern managed CouchDB hosting options, including a flat-rate managed CouchDB, serverless scale-to-zero, and one-step migration off Cloudant.