Comparisons

PostgreSQL vs MySQL: differences, performance and which to choose

PostgreSQL vs MySQL compared: licence, MVCC, JSON vs JSONB, extensions, replication, performance, MariaDB, which apps need which, and how to choose.

Published ~11 min read
On this page

Short answer: PostgreSQL and MySQL are both mature, free relational databases that run most web applications. PostgreSQL is the stricter and more feature-rich one: JSONB, extensions such as PostGIS, transactional DDL, a richer SQL dialect. MySQL, and its fork MariaDB, is simpler to run and is what WordPress and much of the PHP world expect. Let the application decide first.

The short version

  • The application names one: use that one. WordPress needs MySQL or MariaDB; Mastodon, GitLab and Discourse need PostgreSQL. No feature list outweighs that.
  • You are writing something new and are free to choose: PostgreSQL is the safer default for data that will grow in structure, because of its type system, JSONB, extensions and stricter behaviour.
  • A classic PHP site, a team that knows MySQL, phpMyAdmin habits: MySQL or MariaDB, with nothing to apologise for.
  • “Which is faster?” Neither, in general. It depends on the workload, the schema and the configuration, and a test with your own queries beats any published benchmark.

PostgreSQL vs MySQL at a glance

PostgreSQLMySQL
LicencePostgreSQL License, a permissive licence similar to BSD or MITCommunity Edition under GPLv2; Oracle also sells commercial licences and an Enterprise Edition
Who develops itThe PostgreSQL Global Development Group, a community projectOracle
Releases (as of October 2026)One major version a year, each supported for five years; 18 is current, 19 at release candidateLTS series (8.4, 9.7) plus short-lived Innovation releases; 8.0 left Oracle's extended support in April 2026
ConnectionsOne operating-system process per connectionOne thread per connection
Storage engineOne built-in storage format plus the write-ahead logPluggable; InnoDB is the default and the right choice for nearly every table
MVCCOld row versions stay in the table until VACUUM (autovacuum) clears themOld versions live in undo logs and are purged in the background
Default isolationRead CommittedRepeatable Read
DDL inside transactionsYes: most CREATE, ALTER and DROP can be rolled backNo: DDL commits implicitly (each statement is atomic since 8.0)
CTEs, window functionsSince 8.4 (2009)Since 8.0 (MariaDB since 10.2)
JSONjson (stored as text) and jsonb (binary, indexable with GIN)JSON type in a binary format; indexed via generated columns or multi-valued indexes
ExtensionsCREATE EXTENSION: PostGIS, pgvector, pg_trgm, pg_stat_statements and many morePlugins and components; spatial types built in
String comparisonCase-sensitive by defaultCase-insensitive with the default utf8mb4_0900_ai_ci collation
ReplicationStreaming (physical), logical publications and subscriptions, optional synchronousBinary-log replication (async or semi-sync), Group Replication
UpsertINSERT ... ON CONFLICT, and MERGE since 15INSERT ... ON DUPLICATE KEY UPDATE
Default port54323306
Everyday toolspsql, pg_dump and pg_restore, pgAdminmysql, mysqldump, MySQL Shell, MySQL Workbench, phpMyAdmin

Version and support facts are from postgresql.org’s versioning page and Oracle’s MySQL end-of-life notice, both read in October 2026. Check the version you actually run with SELECT version();, which works in both.

Licence and who is behind it

PostgreSQL is a community project with no single owner, under a short permissive licence: you can embed, modify and redistribute it, also in closed products, without publishing your changes. MySQL belongs to Oracle. The Community Edition is GPLv2, which costs nothing for running it behind your own application; the licence matters mainly if you distribute software that includes MySQL itself, and that is the case Oracle’s commercial licence exists for. For a website or an internal tool on your own server, neither licence changes anything in practice.

MVCC, locking and VACUUM

Both use multi-version concurrency control: readers do not block writers and writers do not block readers, and both lock at the row level. The difference is where the old row versions go. PostgreSQL writes a new version of the row into the table and leaves the old one behind until VACUUM reclaims it. Autovacuum does that by default, but on a busy table with long-running transactions it can fall behind, and the table bloats. InnoDB updates the row in place and keeps the old version in an undo log, which a purge thread trims. You rarely think about it, until a transaction left open for hours makes the undo history grow.

One behaviour difference trips people who port code: in PostgreSQL almost all DDL is transactional. A migration that fails halfway through inside BEGIN ... COMMIT rolls back completely, tables and all. In MySQL each DDL statement commits on its own, so a failed migration can leave the schema half changed.

SQL features and dialect

Since MySQL 8.0 the gap in everyday SQL is small: both have common table expressions, recursive queries, window functions, CHECK constraints (enforced by MySQL since 8.0.16) and generated columns. PostgreSQL still goes further with arrays, range types, DISTINCT ON, partial and expression indexes, more index types (GIN, GiST, BRIN), custom types and domains, and functions in several languages. The differences you meet first when moving code between them are smaller and more annoying:

PostgreSQLMySQL
Quoting identifiers"order"`order`
String concatenation'a' || 'b'CONCAT('a', 'b')
|| operatorConcatenatesLogical OR, unless sql_mode has PIPES_AS_CONCAT
Auto-increment idid bigint GENERATED ALWAYS AS IDENTITYid BIGINT AUTO_INCREMENT
BooleanA real boolean typeBOOLEAN is an alias for TINYINT(1)
Unquoted namesFolded to lower caseKept; table-name case sensitivity follows lower_case_table_names
Case-insensitive matchILIKE, or the citext extensionLIKE already ignores case with the default collation

JSON vs JSONB

PostgreSQL has two JSON types. json stores the text as given; jsonb stores a parsed binary form that drops whitespace and duplicate keys and can be indexed as a whole with a GIN index. That index answers containment queries such as WHERE data @> '{"status": "paid"}' across every key without planning the index per field. That is why teams keep semi-structured data in PostgreSQL instead of adding a document database.

MySQL’s JSON type is also stored in a binary format and validated on insert. To index it you pick the fields: a generated column with an ordinary index, or a multi-valued index for JSON arrays (since 8.0.17). It works well when you know which keys you will query. MariaDB’s JSON is different again: an alias for LONGTEXT with a validity check, with JSON functions working on the text.

Extensions: PostGIS, pgvector and friends

CREATE EXTENSION loads new data types, index methods and functions into one database. The ones you meet most: PostGIS for geographic data and spatial queries, pgvector for embeddings and similarity search, pg_trgm for fuzzy text matching, and pg_stat_statements for finding slow queries. Debian and Ubuntu package the common ones (for example postgresql-16-postgis-3; the number is your server version), and the PostgreSQL project’s APT repository has them for every supported version. MySQL has spatial types and functions built in, and recent MySQL and MariaDB releases have added vector types of their own, but nothing matches the breadth of PostgreSQL’s extension ecosystem.

Replication and high availability

Both replicate well. PostgreSQL ships the write-ahead log to standbys (streaming replication, optionally synchronous) and has logical replication of chosen tables between servers, also across major versions, which makes upgrades with little downtime possible. Automatic failover comes from outside tools such as Patroni. MySQL replicates through its binary log, asynchronously or semi-synchronously, and Group Replication (packaged with MySQL Shell and MySQL Router as InnoDB Cluster) adds automatic failover inside the product. MariaDB brings Galera Cluster for synchronous multi-primary setups.

Performance: workload first, benchmarks second

Every published “PostgreSQL is faster” or “MySQL is faster” result measures one workload, one schema and one configuration. Some tendencies are real but narrow:

  • InnoDB stores each table in primary-key order, so lookups and range scans by primary key are very cheap, which suits typical web reads.
  • PostgreSQL’s planner, parallel query and wider choice of index types tend to help complex joins, analytics and reporting.
  • PostgreSQL starts a process per connection, so thousands of short connections hurt it more than MySQL’s threads. A pooler such as PgBouncer removes that difference.
  • Untuned defaults decide more benchmarks than the engines do. Both ship with small memory settings meant to start on any machine.

If performance is the deciding factor, load a copy of your data into both and replay your own slow queries, using EXPLAIN ANALYZE (MySQL since 8.0.18; MariaDB writes ANALYZE SELECT ...) to see what each one does. pgbench and sysbench are fine for testing a server, not for choosing between the two.

Where MariaDB fits

MariaDB started in 2009 as a fork of MySQL by MySQL’s original developers and stays under GPLv2. It speaks the same protocol, so the mysql client, PHP’s drivers, WordPress and phpMyAdmin work unchanged. Debian ships MariaDB instead of Oracle MySQL, so on a Debian server “MySQL” usually means MariaDB.

The two have drifted apart since MySQL 5.7: different JSON handling, different GTID formats for replication, different default password plugins (caching_sha2_password in MySQL 8, mysql_native_password or ed25519 in MariaDB), and features of its own on the MariaDB side such as sequences, system-versioned tables and Galera. A dump usually moves between them with small edits at most; their data directories do not. Per mariadb.org (August 2026), the maintained long-term series are 10.11, 11.4, 11.8 and 12.3, and 10.6 reached end of life in July 2026. For the PostgreSQL vs MySQL question, treat MariaDB as the MySQL side: everything above about InnoDB, the dialect and the tooling applies.

Which applications expect which database

ApplicationDatabase
WordPress, WooCommerceMySQL or MariaDB only (wordpress.org recommends MySQL 8.0+ or MariaDB 10.11+)
Matomo, Magento Open SourceMySQL or MariaDB
Mastodon, GitLab, DiscoursePostgreSQL only
n8nSQLite or PostgreSQL; MySQL and MariaDB dropped in n8n 2.0
Nextcloud, DrupalEither (and SQLite for small installs)
Django, Rails, Laravel appsEither; the app's own docs decide

Requirements change between releases, so read the installation page of the version you deploy. Running a PostgreSQL-only application against MySQL through a compatibility layer is possible in theory and a support burden in practice. For n8n specifically, see self-hosting n8n.

Running each on a VPS

Memory

PostgreSQL keeps its own cache (shared_buffers, commonly about a quarter of RAM on a database server) and leans on the operating system’s page cache for the rest. InnoDB’s innodb_buffer_pool_size is usually set much larger, half or more of RAM on a dedicated database server and less when a web server and PHP share the machine. Both default to 128 MB. Per-connection memory matters more on PostgreSQL: work_mem applies per sort or hash step, per connection.

Maintenance and upgrades

PostgreSQL needs autovacuum to keep up; watch table bloat on heavily updated tables. A major version upgrade changes the data-directory format, so it goes through pg_upgrade or a dump and restore. MySQL needs little routine maintenance and upgrades in place from one LTS series to the next (8.0 to 8.4, for example); going back to an older series is not supported, so take a backup first.

Backups

logical backups, side by side (as root)
# PostgreSQL: one database, custom format, plus roles (they live outside databases)
install -d -o postgres -g postgres -m 700 /var/backups/postgresql
sudo -u postgres pg_dump -Fc -f /var/backups/postgresql/app.dump app
sudo -u postgres pg_dumpall --globals-only -f /var/backups/postgresql/globals.sql

# MySQL / MariaDB: one database, consistent for InnoDB without locking
mysqldump --single-transaction --routines --events app | gzip > /var/backups/app.sql.gz

For point-in-time recovery, PostgreSQL combines a base backup (pg_basebackup) with archived WAL; MySQL combines a full backup with its binary logs. The detailed commands for each are in the backup sections of installing PostgreSQL and installing MariaDB or MySQL.

How to choose, in order

  1. 1Read the application's requirements

    If it supports only one, the decision is made. If it supports both, check which one its developers test against and document first; that is where bugs get fixed fastest.

  2. 2Count what you already run

    Existing servers, backups, monitoring and the knowledge in your head are worth more than a feature you might use. Two database engines double the operational work.

  3. 3Check for features only one side has

    Spatial queries, indexed JSON documents, vector search inside the database, complex reporting, transactional migrations: PostgreSQL. Galera multi-primary clustering: MariaDB. Tight integration with phpMyAdmin and shared-hosting panels: MySQL or MariaDB.

  4. 4Benchmark only if it is still a tie

    With your data and your queries, on the server size you plan to rent, after basic memory tuning on both sides.

Switching later is possible but real work: a schema conversion, a data migration with a tool such as pgloader, and testing every query that relied on one dialect. Choosing deliberately at the start is cheaper.

Getting either running

Both install from the standard Ubuntu and Debian archives in a minute: see installing PostgreSQL and installing MariaDB or MySQL. Once the server runs, the everyday commands for accounts and databases are in listing users and databases in MySQL. A small Linux VPS runs either one comfortably for a typical application, and both can live on the same server while you try them side by side. For a WordPress site that should simply work, our Plesk hosting comes with MySQL databases ready to use.

Frequently asked

Is PostgreSQL better than MySQL?

Neither is better in general. PostgreSQL offers more SQL features, JSONB, extensions such as PostGIS and transactional DDL; MySQL and MariaDB are simpler to run and are what WordPress and many PHP applications require. Use what your application supports first, then what your team already operates.

Is PostgreSQL faster than MySQL?

It depends on the workload. InnoDB's clustered primary key makes simple key lookups cheap, while PostgreSQL's planner and wider choice of index types tend to help complex queries and reporting. Default settings and schema design usually matter more than the engine, so test your own queries on both before deciding on speed.

Can WordPress use PostgreSQL?

Not as supported software. As of October 2026, WordPress's requirements page names only MySQL (8.0 or newer recommended) and MariaDB (10.11 or newer recommended). Running it on PostgreSQL needs third-party compatibility layers that WordPress core and most plugins are not tested against.

Is MariaDB the same as MySQL?

MariaDB is a 2009 fork of MySQL that speaks the same protocol, so the mysql client, PHP drivers and WordPress work with both. They have diverged since then: JSON handling, replication GTIDs and some SQL features differ, and their data directories are not interchangeable. Debian ships MariaDB instead of Oracle MySQL.

Related articles

All comparisons