SShareShiftby Cameron Shields
Troubleshooting

How to pause and resume a SharePoint migration safely

Checkpoints, stop after the current package, schedule windows and throughput caps, and what each kind of stop keeps and loses.

By Cameron ShieldsPublished 8 September 20268 minute read

The most common complaint about SharePoint migration tools is not speed; it is that a migration which crashed, was throttled or was cancelled on Saturday night has to start again from the beginning. ShareGate's own FAQ states that its migrations cannot be paused, resumed or reverted, only cancelled, and G2 reviewers ask specifically to be able to kick off from where it stopped. To resume a SharePoint migration safely, a tool has to understand what Microsoft's Migration API is doing while it is paused, because Microsoft does not stop just because you did.

This article explains the unit of control, what each kind of stop keeps and loses, how checkpoints work, and how schedule windows and throughput caps let a migration coexist with a busy tenant. It describes how ShareShift does it, validated against SharePoint Online, and the principles apply to any tool built on the Migration API.

The package is the unit of control

A Migration API migration is a sequence of packages. Each package is provisioned a container, encrypted, uploaded, submitted as one job with CreateMigrationJobEncrypted, and then polled with GetMigrationJobProgress until Microsoft reports it finished. Once submitted, Microsoft processes it regardless of what your worker does next, and the job's progress log, which holds the per-item errors, is only retrievable for a limited time.

That shapes everything about pausing. A well-behaved tool only ever acts between packages: nothing is ever half-sent, and every package that Microsoft has accepted is polled to the end so its result is recorded rather than lost and re-imported later. Package boundaries are also the checkpoint boundaries, so the worst case for any interruption is one package of work.

ShareShift packages to Microsoft's guidance of 100 to 250 MB or about 250 items, with single large files in their own packages, and persists every step in a SQLite state file. The state machine is small: Running to Pausing to Paused and back to Running; Running to Stopping to Stopped; Running to and from WaitingForWindow; ending in Completed or Failed.

Three kinds of stop, and what each keeps

ControlWhat happensWhat it keepsWhat it loses
PausePackages already uploading finish and are submitted; no new package starts. Jobs Microsoft has accepted are still polled while paused. Resume continues from the next packageEverythingNothing. Pausing can take as long as the in-flight uploads need
Stop after current packageIn-flight packages finish upload, submit and are polled to completion; nothing new starts; the run returns as Stopped. Remaining packages stay Planned; the next run re-plans exactly those, skipping unchanged itemsEverything completed and everything submittedNothing. Can take as long as Microsoft needs to process the in-flight package
Stop nowCancels in-flight uploads immediately and returns a Stopped summaryEverything persisted up to that point; submitted jobs are reconciled first on the next run without re-uploadingThe bytes of the package being uploaded, which is re-sent next run; and the polling of already-submitted jobs, whose items are re-imported only if Microsoft's job log has expired

The distinction between the second and third rows is the one that matters at 2am. Stop after current package is the safe default: it costs waiting time and loses nothing. Stop now is for when the worker has to go down immediately, and its only real cost is one package of upload.

Killing the worker outright, or losing power, is harder than any of these, because nothing runs on the way out: no tidy-up, no closing checkpoint, no clean database shutdown. The hazard is the moment between Microsoft accepting a job and the tool recording that it did. A tool that writes the job identifier only after the call returns can be killed in that gap, and the resumed run will re-upload and re-import content Microsoft already has, quietly duplicating items and their version history. ShareShift commits the intent to submit, with both container addresses and the encryption key, before the call is made, and commits the job identifier from inside the call the instant it arrives. If it dies anywhere in between, the next run knows the package is in doubt.

It then does the one safe thing. There is no Migration API call that lists a site's jobs, so the tool cannot ask Microsoft what it submitted; it reads the destination instead, marks every item that is already there as done, and re-plans only the ones that are not. It waits for the destination to stop changing first, so a job Microsoft is still importing is never half-counted and half-sent again. A package in doubt is never re-sent on a guess.

Two more things follow from taking this seriously. The state database is set to flush every commit to disk, not the faster default that can lose the last few writes in a power cut, because the write it would lose is the one that says a package was submitted. And a run takes an exclusive lock on its state file for its lifetime, so a second run, or the app and the command line at once, is refused with a message naming the machine, user and process holding it rather than quietly interleaving with it. The lock is the open file handle, so a killed worker releases it immediately and leaves nothing to clear by hand.

Checkpoints and resuming

After every package, and on every pause, stop or window transition, ShareShift writes a small checkpoint file next to the state database: run id, status, packages done and total, items done, last package ordinal, timestamps and the next window start. It is for people and for the app; the SQLite store remains the source of truth. Before resuming, the app inspects the state without opening a connection to Microsoft and reports whether a previous run exists, how it ended (Completed, Stopped, Failed or Interrupted), the package and item counts, the package to resume from, and whether it can resume.

  1. Open the migration and read the resume summary: for example, stopped after package 6 of 10, 1,500 of 2,500 items done.
  2. Resume. Submitted jobs are reconciled first, without re-uploading. Planned packages are re-planned against the source; unchanged items are skipped by the incremental diff.
  3. Items Microsoft reported as failed in earlier packages are retried automatically in a second pass, separated by an exponential back-off that starts at 15 seconds and doubles. With transient-only retry, only items whose error text matches throttling, 429 or 503, timeouts, try again or server busy are resubmitted; the rest stay failed with Microsoft's message and are counted as permanent.
  4. The run ends Completed or Failed, and the report reflects every package from every run against the state file.

Resuming from a different machine works the same way, because the state file is a file: move it, or point the second worker at the shared location, and it inspects and continues. The migration report article explains how the report is built from the same state.

Incremental re-runs and conflict rules

Resume and incremental are the same mechanism seen from different ends. A re-run against the same state file compares the source with what was written last time: unchanged items are skipped, new items are packaged, and changed items go through a REST upload that adds a new version on top of the destination's history and restores Created, Modified, Author and Editor. Resubmitting a changed file through the Migration API would replace it and all its versions, which is why the incremental path is different from the bulk path.

A conflict rule decides what happens to an item that already exists at the destination, using the Modified stamp recorded when it was last written there:

  • Source wins (default): unchanged skipped, changed overwritten as a new version.
  • Skip if destination newer: changed items are left alone when the destination stamp is newer than the source, which protects edits made in the new location during a long project.
  • Destination wins: never overwrite anything migrated successfully; earlier failures are still retried.
  • Always copy: ignore change detection and copy everything again.

Items held back by the rule are counted separately from unchanged items in the summary, so the report shows the difference between nothing changed and something changed but was deliberately not overwritten. For comparison, ShareGate's incremental copies new or modified items and never deletes at the destination; ShareShift also never deletes destination content unless the project owner enables it explicitly.

Schedule windows and throughput caps

Microsoft applies tighter limits to background applications during weekday daytime, so the best way to avoid needing to pause is to run when the tenant is quiet. A schedule is a list of allowed windows by day of week and wall-clock time in a chosen time zone. An end at or before the start crosses midnight, so Friday 18:00 to 06:00 runs to Saturday morning; an end of 00:00 means until the end of that day. The maths is daylight-saving safe: a start inside a spring-forward gap moves to the first valid instant after it, and an ambiguous autumn start takes its first occurrence.

Outside every window the run enters WaitingForWindow, reports the next start, and sleeps in short slices so pause and stop stay responsive. The check happens at run start and at every package boundary: a package that is uploading when a window closes finishes and is submitted, jobs already submitted are polled to the end, and nothing is sent before the first window opens.

Three caps apply per tenant for runs that must share a busy tenant with its users:

  • A maximum number of packages in flight, overriding the run's default concurrency.
  • A maximum upload rate in megabits per second, implemented as a token bucket shared by every parallel blob upload, with one second of burst after idle and an exact sustained rate applied to bytes on the wire.
  • A maximum number of requests per minute, applied client-side before every attempt, ahead of Microsoft's own 429 and 503 handling, which is always honoured through Retry-After.
# Friday evening to Monday morning, and weeknights, Europe/London
Windows: Fri 18:00-06:00, Sat 00:00-00:00, Sun 00:00-06:00, Mon-Thu 20:00-06:00
Caps:    4 packages in flight, 200 Mbps upload, 600 requests per minute

Windows and caps cost nothing in fidelity; they only change when bytes move. Combined with the throttling behaviours a well-mannered tool already has, they turn the weekend from a deadline into a schedule. This is standard on every ShareShift plan; the ShareGate comparison sets out the difference against a cancel-only model, and the pricing page has the 30-day trial if you want to test a stop and resume on your own content.

Frequently asked questions

Can a SharePoint migration be paused and resumed?

With a tool built for it, yes. The safe approach acts only between Migration API packages, keeps polling jobs Microsoft has already accepted, and persists every step so a resume continues from the next package without re-sending anything completed. ShareGate's FAQ states its migrations cannot be paused, resumed or reverted, only cancelled.

What happens to a Migration API job if the worker stops?

Microsoft keeps processing it. The job's progress log, which holds the per-item errors, is only retrievable for a limited time, so the next run reconciles submitted jobs first without re-uploading. If the log has expired, that package's items are re-imported.

Does resuming create duplicates?

No. Item identifiers are preserved across packages and scoped per destination list, and unchanged items are skipped by the incremental diff. Changed items are added as a new version rather than a duplicate. A worker killed between Microsoft accepting a job and ShareShift recording it leaves that package marked as in doubt, and the next run settles it by reading the destination rather than sending it again.

What happens if a migration is paused for days and the Azure containers expire?

Microsoft's migration containers carry a shared access signature valid for three days and are used for exactly one job. If a package's containers are expired or rejected when the run gets back to them, ShareShift provisions a fresh pair and uploads that package again, which is safe because no job was ever created against the old ones. It is counted and reported rather than failing the run.

Can two migrations run against the same state file?

No. A run takes an exclusive lock on its state file and a second one is refused with a message naming the process, machine and user holding it. Without that, two runs would each plan and submit the same packages.

How do schedule windows handle daylight saving?

A start that falls inside a spring-forward gap moves to the first valid instant after it, and an ambiguous autumn start takes its first occurrence. Windows are defined in wall-clock time of a chosen time zone and may cross midnight.

Related reading
Try ShareShift

Run your first migration on a 30-day free trial

Every workload, unlimited data, three workers, pause and resume built in. Worker-only, so your content never passes through our servers. The trial key is issued from the portal and works with the same app you would buy.

Resume a SharePoint migration: pause, stop, checkpoints · ShareShift