skip to content

Designing a NETCONF change pipeline, when should the source of truth push complete configurations by replace rather than send incremental merge edits?

level: principalimportance: nice to knowfreq 4%

answer

  1. who owns every node in scope
  2. merge never deletes
  3. blast radius of a partial source
  4. replace at the subtree you own

basics

~20 s

Push complete configurations by replace only where the pipeline owns everything in scope, since replace deletes whatever its source omits. Elsewhere, send merge edits plus explicit delete or remove; scoped replace on owned subtrees is the usual middle ground.

solid answer

~40 s

Full replace, either `<copy-config>` of a complete configuration or `<edit-config>` with `<default-operation>replace</default-operation>`, makes the device converge on the source of truth: stale entries and hand-made changes disappear without anyone computing a diff. Its price is ownership: anything in scope that the source does not model, such as a hand-set description or another system's settings, is deleted, and a source built from an incomplete read wipes real configuration. Incremental merge has a small blast radius and coexists with other writers, but merge never deletes, so removals must be sent explicitly as `delete` or `remove`, and drift accumulates unless something reconciles. Most teams settle on scoped replace: `operation="replace"` on the subtrees the pipeline owns, merge elsewhere, staged and checked before it reaches running.

go deeper

for a junior

Recall that replace deletes what it is not given and merge only adds or changes, so the two suit different callers.

for a middle

Explain the scope of each write: copy-config and default-operation replace cover the whole datastore, a replace attribute covers one subtree, merge covers sent nodes only.

for a senior

Show the failure modes: incomplete reads feeding replaces, merge-only drift, devices refusing running as a copy target, and running that delete-config cannot touch.

for a principal

Frame it as ownership: map which systems and people own which subtrees, size the replace scope to that map, and decide how the pipeline treats changes it does not own.

## The decision A configuration pipeline holds an intended state in a source of truth and must make NETCONF devices match it. RFC 6241 offers three ways to express a write, and they encode different claims about **ownership**: - **Whole-datastore replace.** `<copy-config>` creates or replaces an entire datastore with a complete configuration, from another datastore or an inline `<config>`. `<edit-config>` with `<default-operation>replace</default-operation>` has the same scope: its `<config>` "completely replaces the configuration in the target datastore". - **Scoped replace.** An `operation="replace"` attribute on one element replaces only that element's subtree; RFC 6241 contrasts it with `<copy-config>`, since only the configuration present in `<config>` is affected. - **Incremental merge.** The default operation. Only sent nodes change; nothing is deleted unless a `delete` or `remove` says so. There is no universally right answer; the right scope is the largest one the pipeline can honestly claim to own. ## What full replace buys and costs | | Full replace | Incremental merge | |---|---|---| | Stale configuration | Removed automatically | Persists until explicitly deleted | | Out-of-band changes | Overwritten on the next push | Survive unless they collide | | Other writers on the same device | Their nodes are deleted | Coexist | | Blast radius of a bad source | Entire datastore | Only the nodes sent | | Work in the pipeline | Render a complete, correct config | Compute adds, changes and removals | Full replace fits devices or datastores where the source of truth really is complete: a fleet of identical access switches built only from templates, or a lab. It fails where the device carries configuration the pipeline does not model: credentials provisioned by another process, descriptions set by operations staff, settings another automation system owns. ## Traps specific to the RPCs 1. **Incomplete reads feed destructive writes.** A pipeline that seeds its source by reading devices with `<get-config>` under an account restricted by the access-control model (RFC 8341) receives a reply with unreadable nodes silently omitted. A later full replace built from that source deletes them. 2. **A device may refuse running as the copy target.** RFC 6241 lets a device decline `<running/>` as the target of `<copy-config>` even when it advertises `:writable-running`, so the design needs a path that does not depend on it. 3. **Same source and target is an error.** `<copy-config>` naming the same datastore or URL on both sides returns `invalid-value`. 4. **delete-config is not a reset of running.** RFC 6241 states that the running datastore cannot be deleted; `<delete-config>` targets `<startup/>` (with `:startup`, which RFC 6241 describes as resetting the device to its factory defaults) or a `<url>` (with `:url`). 5. **Merge-only pipelines leak.** When a VLAN or peer is removed from the source, a merge of the remaining config does not remove it from the device; the pipeline must compute and send the removal. ## The usual middle ground - **Declare ownership per subtree.** The pipeline owns, say, the interface list or a routing instance; it sends `operation="replace"` on exactly those elements and merges or leaves alone everything else. - **Leave hand-owned leaves out of owned subtrees, or model them.** If operations staff set descriptions, either the source carries descriptions or the pipeline does not replace the entries that hold them. - **Use tolerant deletes for convergence.** `remove` keeps retries quiet; `delete` where absence signals drift. - **Check before landing.** `<test-option>test-only</test-option>` on servers with `:validate:1.1` validates without applying; staging in a candidate datastore and committing, with confirmation where available, gives an all-or-nothing landing and an undo path. - **Prefer atomic error handling.** `rollback-on-error` where advertised, so a failed push does not leave the device half-converged. ## How to argue it in an interview A strong answer names the axis, which is who owns which nodes, rather than a favourite RPC. It accepts that full replace is the cleanest model for convergence and the most dangerous when ownership is incomplete, and that merge is safe per push but drifts. It then proposes measurable controls: an inventory of owned subtrees, a pre-push diff against a fresh `<get-config>` read with full access, and a policy for what happens to unowned changes the pipeline finds. Whatever mode it uses, the pipeline should log it per device and per push, so an incident review can tell a replace from a merge without reconstructing the request.

  • How would you stop a full-replace pipeline from deleting configuration that operations staff set by hand?
    Either bring that configuration into the source of truth so the replace carries it, or narrow the replace scope so it never covers the nodes staff own. A pre-push diff against a fresh `<get-config>` read, flagging deletions of nodes the pipeline did not create, catches the cases neither rule anticipated before they reach running.
  • What does delete-config offer a pipeline that wants a clean slate?
    Less than it seems. RFC 6241 forbids deleting the running datastore, so `<delete-config>` cannot reset what is in effect. With `:startup` it deletes the startup datastore, which RFC 6241 describes as resetting the device to its factory defaults; with `:url` it deletes a stored configuration file. A clean slate on running is a full replace, with the ownership risks that carries.

saying these in an interview costs you the question

  • Full replace is always safer, because the device then matches the source exactly.
  • A merge-only pipeline removes config that was deleted from the source.
  • <copy-config> with a partial config updates only the parts it contains.
  • A pipeline can wipe a device by sending <delete-config> to running.
  • A replace attribute has the same scope wherever it is placed.