[{"data":1,"prerenderedAt":163},["ShallowReactive",2],{"seo-verification":3,"blog-migrating-sql-server-2016-to-postgresql-on-a-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-migrating-sql-server-2016-to-postgresql-on-a-vps-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":30,"intro":33,"sections":34,"ctaTitle":128,"ctaBody":129,"ctaButton":130,"ctaUrl":131,"relatedPosts":132},467,"migrating-sql-server-2016-to-postgresql-on-a-vps",{"fr":12,"en":10,"ar":13,"es":14},"migrer-sql-server-vers-postgresql-vps","هجرة-sql-server-2016-إلى-postgresql-على-vps","migrar-sql-server-2016-a-postgresql-en-un-vps","Migrating SQL Server 2016 to PostgreSQL on a VPS","SQL Server 2016 reached end of extended support in July 2026. Technical guide to migrate to PostgreSQL on a VPS: data types, T-SQL gaps, pgloader, cutover plan.",12,0,false,"2026-10-18T00:00:00+00:00","2026-10-18T21:57:56+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},6,"Databases","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fmigrer-sql-server-vers-postgresql-vps-poster.svg","On July 14, 2026, Microsoft ended extended support for SQL Server 2016. No further security patches will be released for this version. Teams still running this database have two realistic options: pay for an upgrade to SQL Server 2022 or migrate to an open-source alternative. PostgreSQL 16 is a mature, high-performance relational database with no licensing cost. The migration is not trivial — Microsoft's T-SQL dialect is not standard SQL, and data types differ in several important ways. This guide details the incompatibilities to know, the type mapping, and a structured cutover plan to avoid data loss and application regressions.",[35,39,50,53,89,92,112,115,118,122,125],{"type":36,"title":37,"body":38},"h2","SQL Server 2016: end of extended support on July 14, 2026","Microsoft's lifecycle policy provides ten years of support for each SQL Server version: five years of mainstream support, followed by five years of extended support. For SQL Server 2016, the end of extended support date is July 14, 2026. After this date, Microsoft no longer publishes security updates, critical fixes, or technical updates for this version. A SQL Server 2016 instance exposed on a network after this date accumulates unpatched vulnerabilities. Regulatory requirements — PCI-DSS, GDPR, ISO 27001 — generally require that infrastructure components receive available security patches. Running SQL Server 2016 after July 2026 therefore constitutes a documented deviation in an audit.",{"type":40,"title":41,"items":42},"ul","Why choose PostgreSQL over a SQL Server upgrade or Azure SQL",[43,44,45,46,47,48,49],"Zero licensing cost: PostgreSQL is under the PostgreSQL license (permissive, BSD-like), with no CALs and no separate Enterprise edition to purchase.","Full infrastructure control: on a dedicated VPS, you manage versions, extensions, memory parameters, and backups without depending on a cloud vendor.","SQL Server 2022 fixes the version debt but keeps licensing costs and ties the organization to the Microsoft ecosystem.","Azure SQL Managed Instance simplifies migration but introduces recurring monthly costs, Azure dependency, and additional network constraints.","PostgreSQL supports the same advanced features: analytical window functions, recursive CTEs, native JSON, table partitioning, logical replication.","The tooling ecosystem is broad: pgAdmin, DBeaver, Metabase, PostgREST, and most ORMs (Entity Framework, Hibernate, SQLAlchemy, ActiveRecord) support PostgreSQL.","Major Linux distributions package PostgreSQL with regular security updates included in the distribution maintenance contract.",{"type":36,"title":51,"body":52},"Preliminary audit: what migrates cleanly, what requires work","Before writing a single migration command, a schema audit is essential. Elements that migrate without friction include simple tables with standard types, `PRIMARY KEY`, `FOREIGN KEY`, `UNIQUE` and `NOT NULL` constraints, classic B-tree indexes, views without proprietary T-SQL syntax, and sequences. Elements that require explicit work include stored procedures and T-SQL functions (incompatible syntax, must be rewritten in PL\u002FpgSQL or moved to the application layer), triggers that use the virtual tables `INSERTED` and `DELETED` (PostgreSQL equivalent: `NEW` and `OLD`), `ROWVERSION` \u002F `TIMESTAMP` columns used for optimistic concurrency detection, computed columns, locking hints (`WITH (NOLOCK)`, `WITH (UPDLOCK)`), and global temporary tables (`##temp`). A SQL Server inventory can be obtained by querying `INFORMATION_SCHEMA` and system views `sys.objects`, `sys.procedures`, `sys.triggers`.",{"type":54,"title":55,"rows":56,"headers":85},"comparison","SQL Server → PostgreSQL data type mapping",[57,61,65,69,73,77,81],[58,59,60],"DATETIME2","TIMESTAMPTZ","Store timestamps with time zone; DATETIME2 has no timezone, define UTC convention explicitly",[62,63,64],"UNIQUEIDENTIFIER","UUID","PostgreSQL natively supports UUID type; use `uuid-ossp` extension or `gen_random_uuid()` for generation",[66,67,68],"NVARCHAR(MAX) \u002F NTEXT","TEXT","PostgreSQL stores all strings as UTF-8; no NVARCHAR\u002FVARCHAR distinction and no separate MAX limit",[70,71,72],"BIT","BOOLEAN","Values `0`\u002F`1` become `false`\u002F`true`; watch for BIT columns used as integers in calculations",[74,75,76],"MONEY \u002F SMALLMONEY","NUMERIC(19,4)","SQL Server MONEY is a scaled integer, NUMERIC is exact; identical behavior for financial calculations",[78,79,80],"INT IDENTITY(1,1)","SERIAL or GENERATED ALWAYS AS IDENTITY","SERIAL is shorthand for a sequence; GENERATED ALWAYS AS IDENTITY is SQL:2003 standard, preferred for new tables",[82,83,84],"ROWVERSION \u002F TIMESTAMP","bytea + trigger or xmin","No direct equivalent; the system column `xmin` handles optimistic concurrency detection in most cases",[86,87,88],"SQL Server type","PostgreSQL type","Note",{"type":36,"title":90,"body":91},"Common T-SQL incompatibilities and their PostgreSQL equivalents","Several common T-SQL constructs have no direct equivalent in standard SQL and require rewriting.\n\n`TOP N` without `ORDER BY`: SQL Server accepts `SELECT TOP 10 * FROM t` without an order clause. PostgreSQL requires `LIMIT 10`; if a defined order matters, add an explicit `ORDER BY`.\n\n`ISNULL(expr, value)`: the T-SQL `ISNULL` function maps to the standard `COALESCE(expr, value)`. Behavior is identical for common cases.\n\n`GETDATE()` and `GETUTCDATE()`: these functions become `NOW()` (timestamp with session timezone) and `NOW() AT TIME ZONE 'UTC'` or `CURRENT_TIMESTAMP` respectively.\n\n`DATEDIFF` and `DATEADD`: PostgreSQL uses interval arithmetic: `date + INTERVAL '7 days'`, `date2 - date1` returns an `INTERVAL`.\n\n`WITH (NOLOCK)` hint: this hint requests a read without a shared lock (non-repeatable reads). PostgreSQL does not support this hint; the `READ COMMITTED` isolation level (default) covers most use cases. For analytical reports that tolerate slightly stale data, a read replica is the structural solution.\n\n`PRINT` in procedures: use `RAISE NOTICE 'message'` in PL\u002FpgSQL.\n\n`GO` as a batch separator: this separator does not exist in PostgreSQL. SQL scripts must be restructured into transactions or separate `DO $$...$$` blocks.",{"type":93,"title":94,"steps":95},"steps","8-step migration plan",[96,98,100,102,104,106,108,110],{"body":97},"Schema inventory and audit: export the list of all tables, columns with types, indexes, constraints, stored procedures, functions, triggers, and views. Identify and prioritize objects that require manual rewriting.",{"body":99},"Set up the target PostgreSQL VPS: install PostgreSQL 16, configure `postgresql.conf` (shared memory, connections, WAL), create the application user with minimum required privileges, enable TLS for remote connections.",{"body":101},"Schema and data conversion with pgloader or manually: `pgloader mssql:\u002F\u002Fuser:pass@host\u002Fdb postgresql:\u002F\u002Fuser:pass@vps\u002Fdb` migrates the schema and data in one command. For complex schemas, export the SQL Server DDL and adapt it manually, applying the type mapping table.",{"body":103},"Validate the migrated schema: verify table count, row count per table, referential integrity (`FOREIGN KEY`), and indexes. Compare actual types with expected types via `information_schema.columns`.",{"body":105},"Rewrite stored procedures and triggers: each T-SQL object must be rewritten in PL\u002FpgSQL or moved to the application layer. Test each procedure independently with representative data sets.",{"body":107},"Adapt the application code: search the source code for all literal SQL strings and parameterized queries that use proprietary T-SQL constructs. Update the connection driver (switch from `mssql` to `pg`, `psycopg2`, `npgsql`, or the appropriate driver for your stack).",{"body":109},"Load and regression testing: run the complete regression test suite against the staging PostgreSQL database. Use `pgbench` to validate performance under load. Compare execution plans for critical queries (`EXPLAIN ANALYZE`) between SQL Server and PostgreSQL.",{"body":111},"Cutover during a maintenance window: schedule a window (night or weekend), put the application in maintenance mode, run a final differential migration (data inserted since the initial migration), point the application DSN to PostgreSQL, validate critical functions, and lift maintenance. Keep SQL Server in read-only mode for 48 hours to allow a fast rollback if needed.",{"type":36,"title":113,"body":114},"Configuring PostgreSQL 16 on a VPS for production","A default PostgreSQL installation is not tuned for production. The following parameters in `postgresql.conf` are a starting point for a VPS with 4 GB RAM.\n\n`shared_buffers`: set to 25% of available RAM, `1GB` for 4 GB. This parameter determines the memory allocated to PostgreSQL's internal cache.\n\n`effective_cache_size`: set to 75% of total RAM, `3GB`. This parameter informs the query planner about available memory for the operating system cache.\n\n`work_mem`: `16MB` to `64MB` depending on the complexity of sort queries. This parameter applies per sort operation and per connection; too high a value on an instance with many concurrent connections exhausts memory.\n\n`maintenance_work_mem`: `256MB` for maintenance operations (`VACUUM`, `CREATE INDEX`).\n\n`max_connections`: `100` is sufficient for most applications using a connection pool. If the application opens one connection per request without a pool, use PgBouncer upstream.\n\nFor `pg_hba.conf`, restrict remote connections to the application server's IP address and require TLS: `hostssl all appuser 10.0.0.5\u002F32 scram-sha-256`.\n\nEnable TLS by generating a certificate (Let's Encrypt or internal certificate) and configuring `ssl = on`, `ssl_cert_file`, and `ssl_key_file` in `postgresql.conf`.",{"type":36,"title":116,"body":117},"Stored procedures and T-SQL triggers: the main pitfall","T-SQL stored procedures are the main friction point in a SQL Server to PostgreSQL migration. T-SQL is a proprietary dialect with control flow structures, cursor handling, and error handling (`TRY...CATCH` blocks) that have no direct equivalent in PL\u002FpgSQL.\n\nSQL Server `FAST_FORWARD` cursors become `FOR` loops in PL\u002FpgSQL, but performance is similar and cursor usage should in any case be questioned: a set-based query almost always replaces a cursor more efficiently.\n\nThe `TRY...CATCH` block becomes `BEGIN...EXCEPTION WHEN OTHERS THEN` in PL\u002FpgSQL.\n\nLocal temporary tables (`#temp`) correspond to session-level temporary tables in PostgreSQL created with `CREATE TEMPORARY TABLE`. Global temporary tables (`##temp`) have no direct equivalent — they must be replaced by permanent tables with explicit lifecycle management, or by tables in a dedicated schema with cleanup policies.\n\nTriggers that reference `INSERTED` and `DELETED` (SQL Server virtual tables) must be rewritten to use `NEW` and `OLD` in PostgreSQL. The trigger structure also differs: PostgreSQL distinguishes `BEFORE` and `AFTER` triggers, and `FOR EACH ROW` from `FOR EACH STATEMENT` triggers.\n\nA pragmatic approach is to move stored procedure logic into the application layer during migration, which simplifies testing and reduces coupling between the application and the database.",{"type":119,"title":120,"body":121},"tip","Load testing before cutover: pgbench and query plan comparison","Before cutting over to production, validate performance with `pgbench`. Initialize a representative test database: `pgbench -i -s 50 db_name` creates a benchmark schema with 5 million rows. Run a load simulation: `pgbench -c 20 -j 4 -T 60 db_name` simulates 20 parallel connections for 60 seconds and reports transactions per second. For critical application queries, compare execution plans between SQL Server (`SET STATISTICS IO ON; SELECT ...`) and PostgreSQL (`EXPLAIN (ANALYZE, BUFFERS) SELECT ...`). Watch for unexpected full table scans (missing index or uncalculated statistics — run `ANALYZE` after migration) and `NESTED LOOP` joins on large volumes that would benefit from a `HASH JOIN`. Update statistics after the initial migration with `ANALYZE table_name` or `VACUUM ANALYZE` for the entire database.",{"type":36,"title":123,"body":124},"Troubleshooting — common errors in a SQL Server to PostgreSQL migration","Several error categories come up systematically in early migrations.\n\n`ERROR: column is of type uuid but expression is of type character varying`: a `UNIQUEIDENTIFIER` column migrated to `UUID` receives a UUID-formatted string without an explicit cast. Either adapt the application query to send a proper UUID type, add an implicit cast in PostgreSQL (`CREATE CAST`), or migrate the column to `TEXT` if UUID semantics are not exploited.\n\n`ERROR: operator does not exist: boolean = integer`: a `BIT` column migrated to `BOOLEAN` is compared to `0` or `1`. Replace `col = 1` with `col = true` or simply `col` in `WHERE` clauses.\n\n`ERROR: function isnull(character varying, text) does not exist`: replace all occurrences of `ISNULL` with `COALESCE`.\n\n`ERROR: syntax error at or near \"TOP\"`: the `SELECT TOP N` syntax does not exist in PostgreSQL. Replace with `SELECT ... LIMIT N`.\n\nSilent overflows on `MONEY` → `NUMERIC` columns: verify that the extreme values in the source table fit within the target precision `NUMERIC(19,4)`. SQL Server `MONEY` has a range of ±922,337,203,685,477.5807; `NUMERIC(19,4)` covers ±999,999,999,999,999.9999, sufficient in almost all cases.\n\nData loss on `DATETIME` columns without timezone: SQL Server `DATETIME` and `DATETIME2` do not store timezone. If the database was fed by applications in multiple timezones, timestamps migrated to `TIMESTAMPTZ` without prior conversion will be interpreted as UTC. Define a convention (UTC or local time) and apply an `AT TIME ZONE` conversion during migration if necessary.",{"type":36,"title":126,"body":127},"Verify the migration and restore user access","After cutover, a structured verification checklist reduces the risk of restoring users to an incomplete database.\n\nVerify row counts for all critical tables: query `information_schema.tables` with a `COUNT(*)` per table, compared against the figures recorded from SQL Server before migration.\n\nVerify referential integrity: `SELECT * FROM pg_constraint WHERE contype = 'f'` lists all foreign key constraints; attempt an insert that would violate a constraint to confirm it is active.\n\nVerify indexes: `SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public'` lists all created indexes. Compare with the SQL Server index list.\n\nTest critical application functions: purchase flow, authentication, reports, exports — at minimum the paths touching the most voluminous tables and most complex queries.\n\nMonitor PostgreSQL logs for the first 24 hours: `tail -f \u002Fvar\u002Flog\u002Fpostgresql\u002Fpostgresql-*.log`. Errors like `character varying` vs `uuid` type mismatches, connection timeouts, or suboptimal query plans typically appear in the first hours of real traffic.\n\nKeep SQL Server in read-only mode for 48 hours with restricted access limited to the technical team. Rollback means pointing the application DSN back to SQL Server and replaying data written to PostgreSQL during the cutover window.","VPS ready for PostgreSQL 16","ServOrbit VPS configured for PostgreSQL 16: NVMe SSD, daily snapshot, full root access. Deploy your migrated database without licensing constraints.","View ServOrbit VPS","\u002Fsolutions\u002Fprofessionnels",[133,148],{"id":134,"slug":135,"slugs":136,"title":140,"excerpt":141,"readTime":142,"views":18,"isPinned":19,"publishedAt":143,"updatedAt":21,"category":144,"categories":145,"featuredImage":30,"bgImage":31,"posterImage":147,"relatedSolution":30},464,"mysql-8-0-end-of-life-migration-vps",{"fr":137,"en":135,"ar":138,"es":139},"mysql-8-0-eol-migration-vps","mysql-8-0-انتهاء-الدعم-ترقية-vps","mysql-8-0-fin-de-soporte-migracion-vps","MySQL 8.0 to 8.4 LTS migration on a VPS: step-by-step guide","MySQL 8.0 EOL April 30, 2026: no more security patches. Migrate to MySQL 8.4 LTS — breaking changes, pre-checks, in-place upgrade and when to pick MariaDB 11.x.",10,"2026-10-17T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[146],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fmysql-8-0-eol-migration-vps-poster.svg",{"id":149,"slug":150,"slugs":151,"title":155,"excerpt":156,"readTime":157,"views":18,"isPinned":19,"publishedAt":158,"updatedAt":21,"category":159,"categories":160,"featuredImage":30,"bgImage":31,"posterImage":162,"relatedSolution":30},461,"migrate-postgresql-from-rds-cloud-sql-supabase-to-vps",{"fr":152,"en":150,"ar":153,"es":154},"postgresql-migrer-cloud-vers-self-hosted-vps","نقل-postgresql-من-rds-و-cloud-sql-و-supabase-إلى-vps","migrar-postgresql-de-rds-cloud-sql-o-supabase-a-un-vps","Migrate PostgreSQL from RDS, Cloud SQL or a managed DB to a VPS","Step-by-step PostgreSQL migration plan from RDS, Cloud SQL and Supabase to a self-hosted VPS: exact commands, source-specific pitfalls and zero-downtime cutover.",13,"2026-10-16T00:00:00+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[161],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fpostgresql-migrer-cloud-vers-self-hosted-vps-poster.svg",1792361091896]