SharePoint and PostgreSQL two-way sync, what works today

Most requests for SharePoint and Postgres two-way sync are a heavy read and a light write. SPSync handles the read today; write-back is designed.

Book a call

Book a call to talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.

Last updated

Someone has asked for two-way sync between a SharePoint list and Postgres. Underneath, they need a current copy to query all day and a couple of fields written back with someone's approval.

You have a SharePoint list that people edit every day. It might be a supplier register that purchasing keeps current, or a change request list that a Power Apps form writes to. Now somebody wants the same data in PostgreSQL, perhaps an analyst who needs to join it to the ERP export, or a developer building an internal app on Supabase. The request arrives as “can we have two-way sync between SharePoint and Postgres?” and a search for that phrase turns up vendors with very different answers.

Ask what the two-way part is for and the answer is usually lopsided. The report or the app has to read every row, every few minutes, with the lookups resolved. The writes are a status column and perhaps an approval date, and the people who own the list want a say before either one changes.

What two-way sync with SharePoint means in practice

It helps to split the job in two, because the reads and the writes have very different shapes. The reads are bulk and constant. A Power BI report wants the whole list joined to two lookup lists. An internal app wants to filter by a person column on a list that passed the 5,000-item threshold last year, and it wants the answer without paging through Microsoft Graph. More and more often there is an AI agent as well, answering questions with SQL over the same rows.

The writes are small and they need rules. Someone approves a request in the app and the Status column in SharePoint should say Approved. A scheduled job marks overdue items. Each write touches one item and one or two columns, and the list owner will ask who made it and when. A database that can rewrite every column of every item is rarely what anyone asked for. SPSync does the read half today. The write half is designed and has not shipped.

Reading SharePoint from PostgreSQL with SPSync

On the read side, SPSync signs in to your tenant with read-only Graph permissions and copies the lists you pick into PostgreSQL tables. Each list becomes a table keyed on the SharePoint item ID. A lookup column becomes a key column with its display value beside it, so you join on it the way you would join on any foreign key. Multi-choice and multi-person columns become child tables, and people resolve into a shared sp_users table. The SharePoint to PostgreSQL page has the full column mapping and the setup steps.

After the first load, SPSync reads the Graph delta feed on a schedule (five minutes by default) and applies only the items that changed. A deleted item keeps its row with sp_deleted_at set, so last month’s report still adds up. A full reconcile compares the copy with SharePoint, weekly by default and configurable, and it is how you check the copy for an audit or after a network outage.

Because the copy is plain Postgres, the usual Postgres tools work on it. You can index whichever columns your app filters on and add views for the reports, and none of it changes the list. A reporting role can get SELECT on just the two tables it needs. Before an AI agent queries the data, you can put row-level security on the tables and point any Postgres MCP server at the replica under a role that only sees what it should. None of those queries touch SharePoint, so they cost nothing against Graph throttling and the list view threshold never comes into it.

Writing changes back to SharePoint, as designed

As of today SPSync does not write to SharePoint. The product contains no Graph write call, which is why the permissions it asks for are read-only. Write-back is designed in detail and has no release date. The design follows the lopsided shape above, with small, approved writes on lists you choose.

It starts with a per-list opt-in. You mark one list as writable and every other table stays a read-only copy. On a writable table, database triggers set a dirty flag on any row you change and record a deleted row as a tombstone. Each interval, the writer collects the dirty rows and sends each item to Graph as a PATCH request with an If-Match header carrying the row’s sp_etag. That column is already on every replicated row today.

If someone changed the item in SharePoint since the last sync, the etag no longer matches and the design treats the write as a conflict. SharePoint wins by default, with a per-list option to let the database win instead. The losing values go into a conflicts table with both versions, their timestamps and the editor’s name, so a person decides what happens to them. A list can also run in dry-run mode, where the writer logs what it would send and sends nothing, and a cap limits how many items a single run may write. The design also logs every write with its before and after values.

The design covers text, number, currency, yes/no, date, choice, single lookup, person and hyperlink columns. Calculated and system columns stay read-only, and attachments and library files are outside it. Until write-back ships, an app that has to change SharePoint calls Graph itself or hands the change to a Power Automate flow, and SPSync picks up the new value on its next run. The two-way sync roadmap page carries the current state.

SharePoint and PostgreSQL sync tools compared

These are the routes people take for this pairing. The competitor columns describe what each vendor’s own page said when we checked on 20 October 2026.

SPSyncLayer2 Cloud ConnectorSkyviaStacksyncPower Automate flow
Direction todaySharePoint to PostgresBoth ways, per connectionBoth ways“Direction requires confirmation”, in both directionsWhichever way you build each flow
SharePoint connector documentedYesYes, with a PostgreSQL walkthroughYes, a SharePoint Lists to PostgreSQL pageNo, see the quote belowYes, Microsoft’s SharePoint connector
Write-back to SharePointDesigned, with no release dateYesYesTheir connector page says it is not establishedYes, one flow action per write
When both sides changeDoes not arise while read-only; SharePoint wins in the designA conflict mode you choose, such as PostgreSQL always winsNot described on the pageNot described on the pageWhatever logic the flow contains
Lookups and multi-value columnsKey columns and child tablesColumn mapping you configureMapping you set up in Skyvia’s designerNot described on the pageExpressions in each flow
Where it runsYour machine or your own Azure subscriptionA Windows service you hostSkyvia’s cloudStacksync’s cloudMicrosoft’s cloud

Stacksync’s PostgreSQL and SharePoint page (stacksync.com/integrations/postgresql-and-sharepoint, checked 20 October 2026) states its own position in one line. It reads “Stacksync support for SharePoint is not established by the connector documentation reviewed for this page.” The page suggests starting with one record and a conversation with their engineers.

Layer2’s guide says a connection can be scheduled every 10 minutes or more often, and Skyvia’s page says its integrations can run as often as once per minute. Both of them write to SharePoint today. The Layer2 comparison, the Skyvia comparison and the Power Automate comparison go into each one in more depth, and the Stacksync comparison covers Stacksync.

When another tool is the better pick

If people edit the same columns on both sides all day, you need two-way sync with conflict rules now, and Layer2 Cloud Connector is built for that. It also covers SharePoint on-premises. Skyvia is a reasonable choice when you want a hosted tool and your record volumes fit its plans. For one list and a few writes a day, such as setting a status when a form is approved, a Power Automate flow is often enough.

SPSync is the better fit when most of the load is reading. That usually means a report or an app on a large list with lookups, where you want tables a developer would have designed by hand. Writes then go through a flow or the app’s own Graph call, and SPSync brings the result back into Postgres within a few minutes.

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 sharepoint_postgresql_two_way_sync --prefix sharepoint_postgresql_two_way_sync --destination-kind postgres --connection-string-env SPSYNC_DB_CONNECTION --dataset-name sharepoint_postgresql_two_way_sync --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: sharepoint_postgresql_two_way_sync
prefix: sharepoint_postgresql_two_way_sync
auth:
  mode: delegated
  client_id: 00000000-0000-0000-0000-000000000000
  tenant_id: common
destination:
  kind: postgres
  connection_string_env: SPSYNC_DB_CONNECTION
  dataset_name: sharepoint_postgresql_two_way_sync
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.

Book a call

Book a call to talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.