MSSQL to Neon
This is an operator’s playbook for an MSSQL to Neon migration. It covers the Neon-specific endpoint, scale-to-zero, and TLS details plus the SQL Server type caveats you need to move Microsoft SQL Server into Neon’s serverless PostgreSQL.
If you searched for how to migrate SQL Server to Neon or move MSSQL to Neon Postgres, the short version is: point pgferry at Neon’s unpooled (direct) endpoint, disable scale-to-zero for the load, and let pgferry’s sys.* catalog introspection handle the SQL Server types that generic tools botch.
What this guide is for
Section titled “What this guide is for”Use this guide when you have a Microsoft SQL Server database (on-prem, Azure SQL, AWS RDS for SQL Server, etc.) and want it on Neon Postgres. It assumes you have a Neon project and branch. For source-side behavior that is not Neon-specific, read the generic MSSQL to PostgreSQL guide alongside this page.
Why use pgferry instead of generic pgloader advice
Section titled “Why use pgferry instead of generic pgloader advice”For SQL Server, generic advice is thin: pgloader’s MSSQL path is largely unmaintained and most tutorials assume MySQL. pgferry is built for this pair:
- It introspects SQL Server through
sys.*catalog views and applies SQL Server-specific conversions (UUID byte reordering,datetime2/timescale clamping,money→numeric). - It streams with chunked, parallel
COPYand resumes from a checkpoint — important over a Neon connection that can auto-suspend. pgferry planreports computed columns, skipped non-B-tree/filtered indexes, temporal tables, andNEXT VALUE FORdefaults before PostgreSQL is touched.- It creates objects as the connecting role, avoiding the ownership/
SET ROLEerrors apg_dump-style restore hits against Neon’s non-superuser role.
Destination prerequisites
Section titled “Destination prerequisites”- A Neon project and branch. Note the default database (
neondb) and owner role (neondb_owner), or create your own. - The connection string from the Neon console (Connect), which gives both pooled and direct host forms.
- Neon’s owner role is a member of
neon_superuser— not a true superuser, but it can create schemas, tables, indexes, FKs, sequences, and allow-listed extensions. That covers pgferry’s needs.
Recommended pgferry config
Section titled “Recommended pgferry config”schema = "app"on_schema_exists = "error"unlogged_tables = falseresume = truevalidation = "row_count"chunk_size = 100000source_snapshot_mode = "single_tx"
[source]type = "mssql"source_schema = "dbo"# dsn supplied via PGFERRY_SOURCE_DSN
[target]# dsn supplied via PGFERRY_TARGET_DSN
[type_mapping]datetime_as_timestamptz = falsemoney_as_numeric = trueresume = true requires unlogged_tables = false. On MSSQL, source_snapshot_mode = "single_tx" uses SNAPSHOT isolation — see the MSSQL guide for the ALLOW_SNAPSHOT_ISOLATION details.
Neon DSN, TLS, pooling, and firewall notes
Section titled “Neon DSN, TLS, pooling, and firewall notes”Neon endpoints differ only by a -pooler suffix:
| Endpoint | Host shape | Use for |
|---|---|---|
| Direct (unpooled) | ep-<id>.<region>.aws.neon.tech | Migrations, DDL, bulk load |
| Pooled | ep-<id>-pooler.<region>.aws.neon.tech | App runtime |
- Use the direct (unpooled) endpoint. The pooled endpoint is PgBouncer in transaction mode and breaks session-scoped DDL and the session features pgferry relies on.
- TLS is mandatory. Neon rejects non-TLS connections. Use
?sslmode=requireat minimum;verify-fullworks against the system trust store. Neon’s console strings also includechannel_binding=require, supported by thepgxdriver pgferry uses. - IP Allow is a paid-plan feature, default open. If enabled, add your migration host’s egress IP/CIDR first.
Example direct-endpoint target DSN:
export PGFERRY_TARGET_DSN='postgresql://neondb_owner:<password>@ep-<id>.<region>.aws.neon.tech/neondb?sslmode=require'export PGFERRY_SOURCE_DSN='sqlserver://user:pass@mssql-host:1433?database=source_db'Scale-to-zero — the Neon-specific gotcha
Section titled “Scale-to-zero — the Neon-specific gotcha”Neon computes auto-suspend after inactivity (5 minutes by default; fixed on Free). Disable scale-to-zero (or raise the timeout) for the migration window in Branches → compute → Edit, then re-enable it after. For large datasets, raise the compute size for more max_connections and index-build headroom. Keep transactions moving so the 5-minute idle_in_transaction_session_timeout does not terminate one mid-load.
Source-specific caveats (SQL Server)
Section titled “Source-specific caveats (SQL Server)”These come from the MSSQL side (full detail in the MSSQL guide):
- Choose the right
source_schemainstead of defaulting todboblindly. - Decide
datetime_as_timestamptz; keepmoney_as_numeric = trueunless you want text. datetime2/timefractional precision is clamped to PostgreSQL’s max scale of 6 (SQL Server allows 7).uniqueidentifiervalues are byte-reordered into standard UUID order.- Computed columns are materialized as values and reported for manual recreation.
- Non-B-tree indexes (columnstore, hash, XML, spatial) and filtered indexes are skipped with warnings.
NEXT VALUE FORdefaults, system-versioned temporal tables, andsql_variantcolumns produce semantic warnings — handle via hooks or manual DDL.- External tables are excluded by default.
Step-by-step MSSQL to Neon migration flow
Section titled “Step-by-step MSSQL to Neon migration flow”- Create the Neon project/branch and copy the direct connection string (no
-pooler). - Disable scale-to-zero and, for large data, raise the compute size.
- Generate a config with
pgferry wizardor start from the snippet above. - Export
PGFERRY_SOURCE_DSNandPGFERRY_TARGET_DSN. - Run
pgferry plan migration.tomland resolve every warning. - Run
pgferry migrate migration.toml; rerun on interruption (resume = true). - Recreate views, routines, triggers, and
NEXT VALUE FORdefaults via hooks.
Validation and cutover checklist
Section titled “Validation and cutover checklist”pgferry validate migration.tomlre-runs validation without redoing DDL orCOPY.- Verify computed columns and sequence-backed columns on the target.
- Confirm required extensions exist.
- Re-enable scale-to-zero and restore the compute size if you changed it.
- Walk the cutover checklist and first production migration checklist.
Common failures for this provider pair
Section titled “Common failures for this provider pair”| Symptom | Cause | Fix |
|---|---|---|
| Session/DDL errors, temp-table failures | Connected via the -pooler endpoint | Use the direct (unpooled) endpoint |
| Connection refused / SSL required | Missing TLS | Append ?sslmode=require |
| Compute suspended mid-load | Scale-to-zero fired during a quiet gap | Disable scale-to-zero for the load |
single_tx fails on a read-only login | Snapshot isolation not enabled | Enable ALLOW_SNAPSHOT_ISOLATION or grant ALTER (see MSSQL guide) |
| Missing column defaults | NEXT VALUE FOR not translated | Recreate sequences/defaults via hooks |
See common failures and recovery.
Related
Section titled “Related”- MSSQL to PostgreSQL — generic source guide
- Configuration reference
- Type mapping
- MSSQL minimal-safe example
- Cutover checklist · First production migration checklist
- Other destinations: MSSQL to Supabase · MSSQL to PlanetScale Postgres · MySQL to Neon