Last updated
One way: SharePoint stays the system of record, and SPSync keeps a read-only copy of your lists current in Amazon Aurora.
Status: Shipped · product: SPSync for Relational Databases
Point SPSync at the cluster’s writer endpoint and it behaves as it would against any PostgreSQL or MySQL server. Aurora’s contribution comes after the write: storage that grows on its own and reader instances that spread query load, which matters once a Power BI model or a busy internal app reads the replicated tables all day. This page covers Aurora PostgreSQL first; where Aurora MySQL differs, it says so.
How do I connect SPSync to Aurora?
Both Aurora engines take a connection string held in an environment variable, with a database user and password. IAM database authentication tokens are not supported.
- Aurora PostgreSQL. Add
sslmode=requireto the connection string. Whether the cluster insists on TLS depends on its version:rds.force_ssldefaults to on for Aurora PostgreSQL 17 and later and to off for 16 and older. Settingsslmodeyourself keeps the sync encrypted on both, and keeps it working through a major upgrade that flips the default. - Aurora MySQL. Download the regional RDS certificate bundle and pass its path as
ssl_cain the connection string. That makes TLS required and verifies the server.
Most clusters sit in private subnets. Run the sync from a machine inside the VPC, or one with a route into it, and allow it through the cluster’s security group on 5432 or 3306.
SPSync uses its standard PostgreSQL and MySQL destinations for Aurora. Those are tested
against stock PostgreSQL and MySQL; the Aurora behaviour on this page comes from the AWS
documentation, so connect once from the sync host with psql or mysql before the first
run.
A database user for the sync
The cluster’s admin user works, but a user of its own makes the sync easy to audit and
easy to revoke. On Aurora PostgreSQL, the sync writes into the schema named by the
replication’s dataset_name and stages each run’s changed rows in a second schema with
_staging on the end. Create both and hand them to the sync’s user:
create role field_sync login password 'kept-in-secrets-manager';create schema field_ops authorization field_sync;create schema field_ops_staging authorization field_sync;On Aurora MySQL a schema is a database, so the same pair is two databases, field_ops
and field_ops_staging, with CREATE, ALTER, DROP, INDEX, SELECT, INSERT,
UPDATE and DELETE granted on each to a user created for the sync. Either way the
password lives in the environment variable with the rest of the connection string.
Which endpoint does what
An Aurora cluster hands out several DNS names, and each has a job in this setup:
| Endpoint | Use it for |
|---|---|
| Cluster (writer) endpoint | The sync. It always resolves to the current writer, including after a failover |
| Reader endpoint | Dashboards and apps that only read; it spreads connections across the replicas |
| Custom endpoint | A named group of instances, such as one large replica kept for Power BI refreshes and agents |
| Instance endpoint | Troubleshooting one instance; avoid it in the sync’s string, since it does not follow a failover |
With Aurora Global Database, the clusters in secondary regions are read-only. The sync writes in the primary region, and readers in other regions receive the rows through Aurora’s own storage replication. Point a reporting tool in another region at its local reader endpoint.
A failover moves the writer role to a replica. A run in progress at that moment fails, and the next scheduled run reconnects through the same cluster endpoint and carries on from the last completed run.
Upgrades with a blue/green deployment
A major version upgrade is where an Aurora cluster most often changes under a sync. With
an Amazon RDS blue/green deployment, AWS builds a copy of the cluster (green) on the new
version and keeps it in step with the current one (blue) by replication. The sync goes
on writing to blue the whole time, since green accepts no writes from it. At switchover,
AWS renames the endpoints so the cluster endpoint in the sync’s connection string now
reaches the upgraded cluster. The run that straddles the switchover may fail; the next
one carries on. If you move from Aurora PostgreSQL 16 to 17 this way, confirm the
string has sslmode set first, because the TLS default changes with the version.
Aurora Serverless and auto-pause
On Aurora Serverless with a minimum capacity of zero, an instance with no user connections for the period you set pauses and stops billing for compute. The sync’s connection resumes it, as any client would, so expect the first statement of a run to wait. A schedule of every few minutes means the instance rarely pauses during the day; an hourly schedule lets it pause between runs. The reconcile, weekly by default and configurable per replication, reads every item, so its run keeps the instance busy for longer than an ordinary delta.
How lists become tables
Take a work-order list on a site replicated with the prefix field, with a Customer
lookup, a Priority choice, an Engineer person column, a Due date and a
multi-choice Skills column. On Aurora PostgreSQL the first run creates:
| Table | Columns |
|---|---|
field_work_orders | sp_item_id primary key, title, priority, due as timestamptz, customer_id, engineer_user_id, sp_modified, sp_deleted_at |
field_customers | The Customers list the lookup points at |
field_work_orders__skills | sp_item_id, value, ordinal, one row per order and skill |
sp_users | id, display_name, email, login, shared by every list |
Aurora MySQL gets the same tables. Its IDs and choice values are bounded VARCHAR(n)
columns that you can index directly; free text stays TEXT, which MySQL does not index
without help. A new SharePoint column appears on the next run. Removed ones stay put. A
column whose type changes gets a _v2 column beside it, so existing queries keep working.
Worked example: an engineer’s open orders, read from a replica
Run this through the reader endpoint, so a dispatcher’s screen never competes with the sync’s writes:
select u.display_name as engineer, w.priority, count(*) as open_orders, min(w.due) as next_due, string_agg(distinct s.value, ', ') as skills_neededfrom field_work_orders wleft join sp_users u on u.id = w.engineer_user_idleft join field_work_orders__skills s on s.sp_item_id = w.sp_item_idwhere w.sp_deleted_at is nullgroup by u.display_name, w.priorityorder by next_due;On Aurora MySQL, swap string_agg(distinct s.value, ', ') for
group_concat(distinct s.value separator ', '); the joins and keys are identical. A
reader can trail the writer by a short interval under heavy load, so a row the sync
wrote a moment ago may appear on the reader a moment later.
When the first run fails
cannot execute CREATE TABLE in a read-only transactionon Aurora PostgreSQL, orThe MySQL server is running with the --read-only optionon Aurora MySQL, means the string points at the reader endpoint or an instance endpoint of a replica. Use the cluster endpoint.- A connection timeout means no route or no security group rule from the sync host. Aurora clusters in private subnets are not reachable from a laptop outside the VPC.
no pg_hba.conf entry ... SSL offmeans the cluster requires TLS and the string has nosslmode. This appears after an upgrade to Aurora PostgreSQL 17 on a sync that never set it.
Once runs succeed, the run history in SPSync records each one. In the database,
sp_synced_at shows when each row was last written, which helps when a dashboard on a
reader looks behind: compare max(sp_synced_at) on the writer and on the reader to see
whether the gap is replica lag or a list with no recent changes.
Read scaling in practice
A Power BI report or a set of KPI dashboards can read from a reader endpoint while the sync writes to the cluster, so a heavy refresh never competes with a write. An application read replica serving many users can do the same. When several sites share one list template, each site gets its own table prefix in one cluster.
An agent on the reader endpoint
The same split works for AI. Run a Postgres MCP server, or a MySQL one for Aurora MySQL,
against a custom endpoint that groups the replicas you set aside for it, and give it a
database user whose only grant is SELECT on the replicated schema. spsync mcp serve,
SPSync’s own read-only MCP server, is an alternative to either. The agent answers dispatch questions from the copy, holds no Graph permission,
and costs SharePoint nothing however often it asks. SharePoint item permissions are not
copied into Aurora, so the database grants, and row-level security on Aurora PostgreSQL,
decide what it can read.
Vectors on Aurora PostgreSQL
Aurora PostgreSQL supports the pgvector extension, so the cluster can also carry an
embedding for each work order. Enable it with create extension vector, then pick
Postgres with pgvector as the destination kind in place of plain
PostgreSQL. The sync then writes the same tables and adds one embedding per row, built
from the text columns you choose by OpenAI, Azure OpenAI or a local model, and it
re-embeds a row only when that text changes. Aurora MySQL has no equivalent in SPSync.
Choosing an Aurora storage configuration
Aurora Standard bills I/O per request, and Aurora I/O-Optimized folds I/O into a higher instance and storage price. A sync is light on I/O between first loads: each run writes only the items that changed. The heavy moments are the first load and the reconcile, weekly by default. If the cluster’s own app traffic is I/O heavy, I/O-Optimized may already be the better choice; the sync rarely tips the balance on its own.
Watching the sync in Database Insights
Performance metrics for an Aurora cluster can be sliced by database user, so a role created for the sync shows its own load: short bursts of inserts and updates on the schedule you set, and a longer stretch during a first load or a reconcile. If a dashboard’s queries slow at the same moments, they belong on a reader endpoint. The sync holds a connection only during a run, so a steady connection count from its user between runs points at something else using the same login.
First run
- Sign in with delegated access to look around, then ask an admin for
Sites.Selectedon the sites the sync needs so it can run unattended. - Store the writer endpoint connection string in an environment variable and name it in the destination settings.
- Pick the lists, libraries or whole site.
- The first run creates the schema and loads everything; after that each run reads only changes, with a reconcile, weekly by default.
- Give your reporting tools the reader endpoint.
Identifier limits come from the engine: 63 bytes on PostgreSQL, 64 characters on MySQL. Aurora bills instances, storage and, on Aurora Standard, I/O to your AWS account, separately from SPSync.
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.
Frequently asked questions
Which Aurora engines does SPSync support?
Aurora PostgreSQL and Aurora MySQL. SPSync treats them as PostgreSQL and MySQL servers, so delta sync, soft deletes and schema changes behave as they do on those engines anywhere else.
Should SPSync use the writer endpoint or a reader endpoint?
The cluster's writer endpoint. Replication is a single-writer workload, and a reader endpoint cannot accept the writes. Once the tables exist, Power BI and other read-heavy tools can use a reader endpoint to keep their load off the writer.
Does Aurora Serverless auto-pause stop the sync?
No. A paused instance resumes when a connection arrives, and the sync's connection counts. The first statement of that run waits while the instance resumes, and each scheduled run keeps it awake for a while afterwards.
Is Aurora worth it over plain RDS for this?
It depends on the read traffic. Aurora's shared storage and fast replicas help when many users or a large Power BI model read the tables. For a handful of lists read by one report, RDS on the same engine costs less and is simpler.
Talk to us about Amazon Aurora (Postgres, MySQL)
SPSync is licensed per installation and per target family, with Standard Support included. Tell us which targets you need and how many machines, and we will size it with you. See how licensing works.
Set it up
Sign in to SharePoint with the SharePoint connection guide, then follow the PostgreSQL setup guide or MySQL and MariaDB setup guide to create the database user and open the connection for Amazon Aurora (Postgres, MySQL). If the connection test fails, the troubleshooting guide lists the errors people hit and what fixes them.
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 amazon_aurora --prefix amazon_aurora --destination-kind postgres --connection-string-env SPSYNC_DB_CONNECTION --dataset-name amazon_aurora --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: amazon_aurora
prefix: amazon_aurora
auth:
mode: delegated
client_id: 00000000-0000-0000-0000-000000000000
tenant_id: common
destination:
kind: postgres
connection_string_env: SPSYNC_DB_CONNECTION
dataset_name: amazon_aurora
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.