{"id":48120,"date":"2026-11-05T08:21:00","date_gmt":"2026-11-05T07:21:00","guid":{"rendered":"https:\/\/www.dbi-services.com\/blog\/?p=48120"},"modified":"2026-10-25T15:23:16","modified_gmt":"2026-10-25T14:23:16","slug":"applyops-and-manual-oplog-replay-for-mongodb-pitr","status":"publish","type":"post","link":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/","title":{"rendered":"applyOps and manual oplog replay for MongoDB PITR"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><code>mongorestore --oplogReplay<\/code> gives you point-in-time recovery for a whole instance, replayed from the captured oplog of a single backup. It has no equivalent for a single database or a single collection. Say you need \u201crestore just this one collection to 4:35pm\u201d instead of the whole instance. The supported tooling has nothing to offer for in-place namespace point-in-time recovery (PITR). The only way to build it yourself is with <code class=\"nowrap\">applyOps<\/code>, the server command <code class=\"nowrap\">mongorestore<\/code> uses to replay oplog entries.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In fact, <code class=\"nowrap\">mongorestore<\/code> refuses to combine <code class=\"nowrap\">--oplogReplay<\/code> with any filter, like <code class=\"nowrap\">--db<\/code> or <code class=\"nowrap\">--nsInclude<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ mongorestore --oplogReplay --db myapp --drop dump\/myapp\nThe --db and --collection flags are deprecated for this use-case; please use --nsInclude instead, i.e. with --nsInclude=${DATABASE}.${COLLECTION}\nFailed: cannot use --oplogReplay with includes specified\n0 document(s) restored successfully. 0 document(s) failed to restore.\n\n$ mongorestore --oplogReplay --nsInclude 'myapp.*' --drop dump\nFailed: cannot use --oplogReplay with includes specified\n0 document(s) restored successfully. 0 document(s) failed to restore.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I was facing these issues while adding point-in-time recovery to the MongoDB DMK. I ran into several things that were not obvious from the <code class=\"nowrap\">applyOps<\/code> reference page alone.<\/p>\n\n\n\n<h2 id=\"what-applyops-does\" class=\"wp-block-heading\">What <code class=\"nowrap\">applyOps<\/code> does<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code class=\"nowrap\">applyOps<\/code> is a database command that takes a list of raw operations, in the exact format oplog entries are stored in. It applies them directly, bypassing the normal insert\/update\/delete driver API:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>db.adminCommand({\n  applyOps: &#091;\n    { op: \"i\", ns: \"myapp.orders\", o: { _id: 1, status: \"shipped\" } }\n  ]\n})<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each entry uses the oplog field names:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code class=\"nowrap\">op<\/code> is <code class=\"nowrap\">i<\/code> for insert, <code class=\"nowrap\">u<\/code> for update, <code class=\"nowrap\">d<\/code> for delete, <code class=\"nowrap\">c<\/code> for a collection-level command.<\/li>\n\n\n\n<li><code class=\"nowrap\">ns<\/code> is the target namespace.<\/li>\n\n\n\n<li><code class=\"nowrap\">o<\/code> the operation data.\nThe MongoDB documentation describes it as primarily an internal command, mainly there to support sharding. <code class=\"nowrap\">mongorestore<\/code> uses it as its replay mechanism, by reading oplog entries out of a backup, and handing them to <code class=\"nowrap\">applyOps<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This is the part that makes namespace-scoped PITR possible. To achieve this in-place, we need to read the oplog, keep only the entries for the database or collection you care about, and replay just those.<\/p>\n\n\n\n<h2 id=\"batch-problem-not-every-write-is-a-separate-oplog-entry\" class=\"wp-block-heading\">Batch problem: not every write is a separate oplog entry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The first surprise comes from something as simple as <code class=\"nowrap\">insertMany()<\/code>. A batch of statements committed together doesn\u2019t produce one oplog entry per statement. It produces a single top-level entry with <code>op: \"c\"<\/code>, <code>ns: \"admin.$cmd\"<\/code>, and the real operations nested inside <code class=\"nowrap\">o.applyOps<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\"op\":\"c\",\"ns\":\"admin.$cmd\",\"o\":{\"applyOps\":&#091;\n  {\"op\":\"i\",\"ns\":\"myapp.orders\",\"o\":{\"_id\":1,\"n\":1}},\n  {\"op\":\"i\",\"ns\":\"myapp.orders\",\"o\":{\"_id\":2,\"n\":2}},\n  {\"op\":\"i\",\"ns\":\"myapp.orders\",\"o\":{\"_id\":3,\"n\":3}}\n]},\"ts\":{\"$timestamp\":{\"t\":1787326648,\"i\":2}}}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A namespace filter that only looks at the top-level <code class=\"nowrap\">ns<\/code> field never sees <code class=\"nowrap\">myapp.orders<\/code> here at all. It sees <code class=\"nowrap\">admin.$cmd<\/code> and moves on, silently skipping every write inside the batch. Any manual oplog replay has to <strong>recursively unpack nested <code class=\"nowrap\">applyOps<\/code> entries<\/strong> before filtering by namespace, or it will drop real writes without any error.<\/p>\n\n\n\n<h2 id=\"applyops-rejects-entries-from-a-freshly-restored-collection\" class=\"wp-block-heading\"><code class=\"nowrap\">applyOps<\/code> rejects entries from a freshly restored collection<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once the batch problem was fixed, replaying entries against a just-restored collection still failed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MongoServerError: applyOps had one or more errors applying ops<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The response contained no per-entry detail beyond <code>results: [false, false]<\/code>. The real cause is that every oplog entry also has a <code class=\"nowrap\">ui<\/code> field. It is the internal UUID of the collection at the moment the entry was written. <code class=\"nowrap\">applyOps<\/code> checks that UUID against the UUID of the live collection and rejects the operation on any mismatch.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code class=\"nowrap\">mongorestore<\/code> always assigns a <strong>new<\/strong> UUID to a collection it creates during a restore. The exception is when you explicitly pass both <code class=\"nowrap\">--drop<\/code> and <code class=\"nowrap\">--preserveUUID<\/code> together. The UUID of a restored collection never matches what the oplog entries reference, which is why every single op gets rejected, correctly but without saying why.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code class=\"nowrap\">mongorestore<\/code> oplog replay code strips the <code class=\"nowrap\">ui<\/code> field before calling <code class=\"nowrap\">applyOps<\/code>, unless <code class=\"nowrap\">--preserveUUID<\/code> was explicitly requested:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if !restore.OutputOptions.PreserveUUID {\n    op.UI = nil\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When building a custom oplog replay instead of using <code class=\"nowrap\">mongorestore<\/code>, you need the same fix. <strong>Drop <code class=\"nowrap\">ui<\/code> from every entry<\/strong> before handing it to <code class=\"nowrap\">applyOps<\/code>. The operation then resolves by namespace, instead of a UUID that no longer exists.<\/p>\n\n\n\n<h2 id=\"ejson-relaxed-mode-changes-your-data-types\" class=\"wp-block-heading\"><code class=\"nowrap\">EJSON<\/code> relaxed mode changes your data types<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reading oplog entries back out of an archive, to filter and replay them, means going through <code class=\"nowrap\">EJSON.stringify()<\/code> and <code class=\"nowrap\">EJSON.parse()<\/code> in <code class=\"nowrap\">mongosh<\/code>. Both default to <strong>relaxed mode<\/strong>, and relaxed mode collapses <code class=\"nowrap\">NumberLong<\/code> into a JavaScript number:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; EJSON.stringify({txnNumber: NumberLong(1)})\n'{\"txnNumber\":1}'\n\n&gt; EJSON.stringify({txnNumber: NumberLong(1)}, null, 0, {relaxed: false})\n'{\"txnNumber\":{\"$numberLong\":\"1\"}}'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If you feed the relaxed-mode output back into <code class=\"nowrap\">applyOps<\/code>, the server rejects it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BSON field 'OplogEntryBase.txnNumber' is the wrong type 'int', expected type 'long'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nothing about the data itself was wrong, the type was just downgraded through JSON. You must pass <code>{relaxed: false}<\/code> on both <code class=\"nowrap\">EJSON.stringify()<\/code> and <code class=\"nowrap\">EJSON.parse()<\/code>. Do it whenever oplog entries (or any BSON document with <code class=\"nowrap\">Long<\/code>, <code class=\"nowrap\">Timestamp<\/code>, or <code class=\"nowrap\">Decimal128<\/code> fields) need to persist through a JSON round trip intact.<\/p>\n\n\n\n<h2 id=\"dropdatabase-inside-a-replay-window\" class=\"wp-block-heading\"><code class=\"nowrap\">dropDatabase<\/code> inside a replay window<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The last one is less more a warning then a bug. A <code class=\"nowrap\">dropDatabase<\/code> in the oplog window is replayed like any other entry, and it deletes data. I tested it in three steps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, <code class=\"nowrap\">applyOps<\/code> refuses a batch that mixes <code class=\"nowrap\">dropDatabase<\/code> with other operations:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; print(EJSON.stringify(db.adminCommand({applyOps: &#091;\n...   {op: \"i\", ns: \"myapp.orders\", o: {_id: 10}},\n...   {op: \"c\", ns: \"myapp.$cmd\", o: {dropDatabase: 1}}\n... ]})))\ndropDatabase in an applyOps must be the only entry<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Second, the same <code class=\"nowrap\">dropDatabase<\/code> sent alone is accepted and applied:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; print(EJSON.stringify(db.adminCommand({applyOps: &#091;{op: \"c\", ns: \"myapp.$cmd\", o: {dropDatabase: 1}}]})))\n{\"applied\":1,\"results\":&#091;true],\"ok\":1,...}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Third, <code>mongorestore --oplogReplay<\/code> does not stop on a <code class=\"nowrap\">dropDatabase<\/code>. The base dump held the document <code>_id: 1<\/code>. The captured oplog then inserted <code>_id: 2<\/code>, dropped the database, re-created the collection and inserted <code>_id: 3<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ mongorestore --oplogReplay --drop dump\nreplaying oplog\napplied 6 oplog entries<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; db.getSiblingDB(\"myapp\").orders.find()\n&#091;\n  {\n    _id: 3\n  }\n]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Documents created before the drop are gone, because the restore followed the history up to the end of the window. Whether to stop at a <code class=\"nowrap\">dropDatabase<\/code> depends on what you restore:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A single collection restored in place: stop. Replaying the drop would also delete the other collections of the database, which are not part of the restore.<\/li>\n\n\n\n<li>A whole database or instance: replaying it is correct, since the drop is part of the history. The risk is a target time set after the drop, which restores an empty database.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">In both cases, I would recommend to detect the <code class=\"nowrap\">dropDatabase<\/code> in the replay window and report its timestamp. If your target moment is after a real <code class=\"nowrap\">dropDatabase<\/code>, replay deletes the data, because the drop is part of the history. Pick a target just before the drop\u2019s timestamp instead. Of course, this works only if your base backup is older than the drop and the oplog slices cover the gap, since a backup taken after the drop no longer contains the data.<\/p>\n\n\n\n<h2 id=\"summary\" class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Building point-in-time recovery (PITR) for a single database or collection on top of <code class=\"nowrap\">applyOps<\/code> means dealing with four things. <code class=\"nowrap\">mongorestore<\/code> already handles them for a full-instance restore, and you now have to handle them yourself:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Unpack nested <code class=\"nowrap\">applyOps<\/code> batches before filtering by namespace, or multi-document writes are silently ignored.<\/li>\n\n\n\n<li>Strip the <code class=\"nowrap\">ui<\/code> field from every entry before replay, matching what <code class=\"nowrap\">mongorestore<\/code> does by default.<\/li>\n\n\n\n<li>Use <code>{relaxed: false}<\/code> on every <code class=\"nowrap\">EJSON.stringify()<\/code>\/<code class=\"nowrap\">EJSON.parse()<\/code> round trip, or <code class=\"nowrap\">Long<\/code>\/<code class=\"nowrap\">Timestamp<\/code>\/<code class=\"nowrap\">Decimal128<\/code> and other fields can be changed.<\/li>\n\n\n\n<li>Stop at a <code class=\"nowrap\">dropDatabase<\/code> when restoring a single collection in place, and report its timestamp in every case.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>mongorestore &#8211;oplogReplay gives you point-in-time recovery for a whole instance, replayed from the captured oplog of a single backup. It has no equivalent for a single database or a single collection. Say you need \u201crestore just this one collection to 4:35pm\u201d instead of the whole instance. The supported tooling has nothing to offer for in-place [&hellip;]<\/p>\n","protected":false},"author":152,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[4293,202,3571,2961,4292,1702,854],"type_dbi":[4295,4294,3842,3407,4298,4297,4296],"class_list":["post-48120","post","type-post","status-publish","format-standard","hentry","category-non-classifiee","tag-applyops","tag-backup","tag-mongo","tag-mongodb","tag-oplog","tag-pitr","tag-restore","type-applyops","type-backup","type-mongo","type-mongodb","type-oplog","type-pitr","type-restore"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.8 (Yoast SEO v28.8) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>applyOps and manual oplog replay for MongoDB PITR - dbi Blog<\/title>\n<meta name=\"description\" content=\"applyOps manual oplog replay explained: why mongorestore strips the ui field from oplog entries, and what breaks in a hand-built PITR.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"applyOps and manual oplog replay for MongoDB PITR\" \/>\n<meta property=\"og:description\" content=\"applyOps manual oplog replay explained: why mongorestore strips the ui field from oplog entries, and what breaks in a hand-built PITR.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/\" \/>\n<meta property=\"og:site_name\" content=\"dbi Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-11-05T07:21:00+00:00\" \/>\n<meta name=\"author\" content=\"Julien Delattre\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Julien Delattre\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/\"},\"author\":{\"name\":\"Julien Delattre\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/812297b963933317cfcbbf006a273eec\"},\"headline\":\"applyOps and manual oplog replay for MongoDB PITR\",\"datePublished\":\"2026-11-05T07:21:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/\"},\"wordCount\":937,\"commentCount\":0,\"keywords\":[\"applyops\",\"Backup\",\"Mongo\",\"mongodb\",\"oplog\",\"PITR\",\"Restore\"],\"articleSection\":[\"Non classifi\u00e9(e)\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/\",\"name\":\"applyOps and manual oplog replay for MongoDB PITR - dbi Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-11-05T07:21:00+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/812297b963933317cfcbbf006a273eec\"},\"description\":\"applyOps manual oplog replay explained: why mongorestore strips the ui field from oplog entries, and what breaks in a hand-built PITR.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/applyops-and-manual-oplog-replay-for-mongodb-pitr\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"applyOps and manual oplog replay for MongoDB PITR\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/\",\"name\":\"dbi Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/#\\\/schema\\\/person\\\/812297b963933317cfcbbf006a273eec\",\"name\":\"Julien Delattre\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a97d00e680bbf237126e24b65281cbcb66cd20bd1ed2d14bf928991b2bf68eb5?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a97d00e680bbf237126e24b65281cbcb66cd20bd1ed2d14bf928991b2bf68eb5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a97d00e680bbf237126e24b65281cbcb66cd20bd1ed2d14bf928991b2bf68eb5?s=96&d=mm&r=g\",\"caption\":\"Julien Delattre\"},\"url\":\"https:\\\/\\\/www.dbi-services.com\\\/blog\\\/author\\\/juliendelattre\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"applyOps and manual oplog replay for MongoDB PITR - dbi Blog","description":"applyOps manual oplog replay explained: why mongorestore strips the ui field from oplog entries, and what breaks in a hand-built PITR.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/","og_locale":"en_US","og_type":"article","og_title":"applyOps and manual oplog replay for MongoDB PITR","og_description":"applyOps manual oplog replay explained: why mongorestore strips the ui field from oplog entries, and what breaks in a hand-built PITR.","og_url":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/","og_site_name":"dbi Blog","article_published_time":"2026-11-05T07:21:00+00:00","author":"Julien Delattre","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Julien Delattre","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/#article","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/"},"author":{"name":"Julien Delattre","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/812297b963933317cfcbbf006a273eec"},"headline":"applyOps and manual oplog replay for MongoDB PITR","datePublished":"2026-11-05T07:21:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/"},"wordCount":937,"commentCount":0,"keywords":["applyops","Backup","Mongo","mongodb","oplog","PITR","Restore"],"articleSection":["Non classifi\u00e9(e)"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/","url":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/","name":"applyOps and manual oplog replay for MongoDB PITR - dbi Blog","isPartOf":{"@id":"https:\/\/www.dbi-services.com\/blog\/#website"},"datePublished":"2026-11-05T07:21:00+00:00","author":{"@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/812297b963933317cfcbbf006a273eec"},"description":"applyOps manual oplog replay explained: why mongorestore strips the ui field from oplog entries, and what breaks in a hand-built PITR.","breadcrumb":{"@id":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.dbi-services.com\/blog\/applyops-and-manual-oplog-replay-for-mongodb-pitr\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/www.dbi-services.com\/blog\/"},{"@type":"ListItem","position":2,"name":"applyOps and manual oplog replay for MongoDB PITR"}]},{"@type":"WebSite","@id":"https:\/\/www.dbi-services.com\/blog\/#website","url":"https:\/\/www.dbi-services.com\/blog\/","name":"dbi Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.dbi-services.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.dbi-services.com\/blog\/#\/schema\/person\/812297b963933317cfcbbf006a273eec","name":"Julien Delattre","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/a97d00e680bbf237126e24b65281cbcb66cd20bd1ed2d14bf928991b2bf68eb5?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/a97d00e680bbf237126e24b65281cbcb66cd20bd1ed2d14bf928991b2bf68eb5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a97d00e680bbf237126e24b65281cbcb66cd20bd1ed2d14bf928991b2bf68eb5?s=96&d=mm&r=g","caption":"Julien Delattre"},"url":"https:\/\/www.dbi-services.com\/blog\/author\/juliendelattre\/"}]}},"_links":{"self":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/48120","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/users\/152"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/comments?post=48120"}],"version-history":[{"count":1,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/48120\/revisions"}],"predecessor-version":[{"id":48121,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/posts\/48120\/revisions\/48121"}],"wp:attachment":[{"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/media?parent=48120"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/categories?post=48120"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/tags?post=48120"},{"taxonomy":"type","embeddable":true,"href":"https:\/\/www.dbi-services.com\/blog\/wp-json\/wp\/v2\/type_dbi?post=48120"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}