Last updated
The same list template is copied across dozens of site collections, and there is no single place to report on all of them together without exporting each one by hand.
Forty project sites, all created from one template, means forty copies of the same Risk Register list. When the board wants the top risks across the portfolio, someone opens each site, exports the list and pastes it into a spreadsheet that is out of date before the meeting starts. SharePoint’s content rollups and search help little here: they were not built for joins across that many lists, and they return SharePoint’s item shape.
One database, forty prefixes
SPSync replicates the same list from many sites into one destination database. Each site gets its own table prefix, so a row always says which site it came from, and each site syncs on its own schedule. Whole-site selection brings a site’s related lists in together, so the register arrives with the lookup lists it points at.
- 1ConnectSign in with Microsoft, or grant per-site app permissions.
- 2DiscoverPick sites, lists and libraries, or a whole site.
- 3MapSPSync reads each list's columns and creates the tables and keys.
- 4Full loadEvery item, paged the way Microsoft recommends, into the destination.
- 5Delta syncEvery few minutes only what changed moves: inserts, updates, deletes.
- 6ReconcileWeekly by default, or on demand with the command-line tool: a full comparison to confirm SharePoint and your database match, or to catch back up after an outage.
Steps 5 and 6 repeat for as long as the replication runs. Nothing is ever written to SharePoint.
Inside each prefix the tables have the shape a single-site sync produces: lookups as key
columns, multi-value columns as child tables, and people resolved into the shared
sp_users table. The portfolio report is a UNION ALL across the per-site tables,
written once as a view so everyone downstream queries one name.
| Title | Client lookup | Tags multi-choice | Owner person | Due |
|---|---|---|---|---|
| Riverside | Acme Ltd | Design; Build | Ada Lovelace | 2026-11-30 |
| Harbour | Borealis plc | Build | Alan Turing | 2027-02-14 |
then sync
- id
- title
- client_id → clients.id
- owner_id → sp_users.id
- due
- is_deleted soft delete
- sp_modified
- id
- title
- …
- project_id
- tag
One row per value: Riverside has two.
- id
- display_name
One users table shared by every list.
Forty configs from one file
Setting up forty sites by hand is the chore this use case removes. Browse or search the
tenant for the sites that carry the list, then write a batch definition that names them,
or names the whole site collection. spsync batch create turns that definition into one
replication config per site, all pointing at the same database, each with its own prefix.
A new project site next quarter is one more line in the batch file.

Questions the board will ask
Historical totals add up. When a risk is deleted from a site’s register, its row stays in
that site’s table with sp_deleted_at set, so last quarter’s count still matches. And the
consolidated report needs one destination database however many sites feed it; the
prefixes keep the sources apart once everything sits in one place.
Desktop, Azure or command line
All three editions run the same engine, write the same tables and use the same licence key, so the choice comes down to who looks after the sync.
Desktop app
For macOS and Windows. Sign in with Microsoft, tick the lists, choose a database and sync from your own machine on the schedule you set. Delegated sign-in needs no admin unless your tenant requires admin consent for apps.
In your Azure
SPSync in your Azure, a managed application for your own subscription, is deployed with you on a call while the Marketplace listing is prepared. Once deployed it is always on, so the sync keeps running with every laptop closed.
Command line
Every replication is a YAML file you can review and commit. Run it from a server, a container or a CI pipeline, and re-run it safely any time.
Set it up
Ready to run this against your own site? The setup guide takes you from install to a first replication, and the SharePoint connection guide covers signing in.
Script it
Everything you can click, you can script. The CLI, YAML config and automation below reproduce this scenario exactly. See the CLI reference.
spsync auth login --client-id 00000000-0000-0000-0000-000000000000
spsync export-config --output replication.yaml --pipeline-name consolidate_multiple_sites --prefix consolidate_multiple_sites --destination-kind postgres --connection-string-env SPSYNC_DB_CONNECTION --dataset-name consolidate_multiple_sites --client-id 00000000-0000-0000-0000-000000000000 --site-url https://contoso.sharepoint.com/sites/example --all-lists
spsync doctor replication.yaml
spsync run replication.yaml pipeline_name: consolidate_multiple_sites
prefix: consolidate_multiple_sites
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: postgres
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: consolidate_multiple_sites
scopes:
- site_url: https://contoso.sharepoint.com/sites/example
mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false
Cron (PostgreSQL nightly):
# every night at 02:00
0 2 * * * cd /opt/spsync && SPSYNC_DB_CONNECTION="$(cat /etc/spsync/db.env)" uv run spsync run replication.yaml >> /var/log/spsync/run.log 2>&1 GitHub Actions:
# .github/workflows/spsync-run.yml
name: SPSync
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch: {}
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- run: uv sync
- run: uv run spsync run replication.yaml
env:
SPSYNC_DB_CONNECTION: ${{ secrets.SPSYNC_DB_CONNECTION }}
SPSYNC_LICENCE_KEY: ${{ secrets.SPSYNC_LICENCE_KEY }} Azure DevOps:
# azure-pipelines.yml
schedules:
- cron: "0 2 * * *"
branches: { include: [main] }
always: true
steps:
- script: uv sync
- script: uv run spsync run replication.yaml
env:
SPSYNC_DB_CONNECTION: $(SPSYNC_DB_CONNECTION)
SPSYNC_LICENCE_KEY: $(SPSYNC_LICENCE_KEY) Windows (PowerShell + Task Scheduler):
# Windows Task Scheduler action (see /docs/cli/automation/)
$env:SPSYNC_DB_CONNECTION = (Get-Content C:\spsync\db.env)
uv run spsync run C:\spsync\replication.yaml
schtasks /Create /SC DAILY /ST 02:00 /TN "SPSync" /TR "powershell -File C:\spsync\run.ps1" macOS (launchd):
# ~/Library/LaunchAgents/io.spsync.run.plist, then: launchctl load ~/Library/LaunchAgents/io.spsync.run.plist Secrets (licence key, connection strings) come from the environment, so never write them into the YAML. See scheduling and secrets.