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
| PostgreSQL | MySQL | |
|---|---|---|
| Licence | PostgreSQL License, a permissive licence similar to BSD or MIT | Community Edition under GPLv2; Oracle also sells commercial licences and an Enterprise Edition |
| Who develops it | The PostgreSQL Global Development Group, a community project | Oracle |
| Releases (as of October 2026) | One major version a year, each supported for five years; 18 is current, 19 at release candidate | LTS series (8.4, 9.7) plus short-lived Innovation releases; 8.0 left Oracle's extended support in April 2026 |
| Connections | One operating-system process per connection | One thread per connection |
| Storage engine | One built-in storage format plus the write-ahead log | Pluggable; InnoDB is the default and the right choice for nearly every table |
| MVCC | Old row versions stay in the table until VACUUM (autovacuum) clears them | Old versions live in undo logs and are purged in the background |
| Default isolation | Read Committed | Repeatable Read |
| DDL inside transactions | Yes: most CREATE, ALTER and DROP can be rolled back | No: DDL commits implicitly (each statement is atomic since 8.0) |
| CTEs, window functions | Since 8.4 (2009) | Since 8.0 (MariaDB since 10.2) |
| JSON | json (stored as text) and jsonb (binary, indexable with GIN) | JSON type in a binary format; indexed via generated columns or multi-valued indexes |
| Extensions | CREATE EXTENSION: PostGIS, pgvector, pg_trgm, pg_stat_statements and many more | Plugins and components; spatial types built in |
| String comparison | Case-sensitive by default | Case-insensitive with the default utf8mb4_0900_ai_ci collation |
| Replication | Streaming (physical), logical publications and subscriptions, optional synchronous | Binary-log replication (async or semi-sync), Group Replication |
| Upsert | INSERT ... ON CONFLICT, and MERGE since 15 | INSERT ... ON DUPLICATE KEY UPDATE |
| Default port | 5432 | 3306 |
| Everyday tools | psql, pg_dump and pg_restore, pgAdmin | mysql, 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:
| PostgreSQL | MySQL | |
|---|---|---|
| Quoting identifiers | "order" | `order` |
| String concatenation | 'a' || 'b' | CONCAT('a', 'b') |
| || operator | Concatenates | Logical OR, unless sql_mode has PIPES_AS_CONCAT |
| Auto-increment id | id bigint GENERATED ALWAYS AS IDENTITY | id BIGINT AUTO_INCREMENT |
| Boolean | A real boolean type | BOOLEAN is an alias for TINYINT(1) |
| Unquoted names | Folded to lower case | Kept; table-name case sensitivity follows lower_case_table_names |
| Case-insensitive match | ILIKE, or the citext extension | LIKE 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
| Application | Database |
|---|---|
| WordPress, WooCommerce | MySQL or MariaDB only (wordpress.org recommends MySQL 8.0+ or MariaDB 10.11+) |
| Matomo, Magento Open Source | MySQL or MariaDB |
| Mastodon, GitLab, Discourse | PostgreSQL only |
| n8n | SQLite or PostgreSQL; MySQL and MariaDB dropped in n8n 2.0 |
| Nextcloud, Drupal | Either (and SQLite for small installs) |
| Django, Rails, Laravel apps | Either; 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
# 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.gzFor 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
- 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.
- 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.
- 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.
- 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.