Skip to main content

PostgreSQL vs MySQL vs MariaDB

·OSSAlt Team
postgresqlmysqlmariadbdatabaseself-hostingdocker2026

TL;DR

For new self-hosted projects in 2026: use PostgreSQL. It has the most advanced features (JSONB, arrays, extensions, full-text search), excellent performance, true open source governance, and is the default for most modern frameworks. MySQL is a solid choice for legacy PHP applications that explicitly require it. MariaDB is the best MySQL alternative if you need MySQL compatibility without Oracle dependency — better governance, Galera replication, and drop-in compatibility.

Key Takeaways

  • PostgreSQL: Most advanced SQL features, JSONB, PostGIS, true open source, best for modern apps
  • MySQL 8.x: Oracle-owned (GPL), massive ecosystem, best for WordPress/legacy PHP, 8.4 LTS
  • MariaDB: MySQL fork by original MySQL creator, GPL, drop-in compatible, better governance, Galera clustering
  • Performance: All three handle typical self-hosted workloads comfortably; differences matter at high scale
  • Docker: All have official Alpine-based images — PostgreSQL is ~78MB, MySQL ~565MB, MariaDB ~403MB
  • Governance: PostgreSQL (community) > MariaDB (foundation) > MySQL (Oracle)

Quick Comparison

FeaturePostgreSQL 16MySQL 8.4MariaDB 11.4
LicensePostgreSQL LicenseGPL 2.0 (dual)GPL 2.0
GovernanceCommunityOracleMariaDB Foundation
JSONB / JSON✅ JSONB (binary, indexed)JSON (text)JSON (text)
Arrays✅ Native
Window functions✅ Full
CTEs✅ (recursive)
Full-text search✅ Native + tsvector
Extensions✅ PostGIS, pgvector, etc.
ACID compliance✅ Full✅ InnoDB✅ InnoDB/Aria
ReplicationStreaming, logicalBinary, GTIDBinary + Galera
Multi-masterVia Citus extensionInnoDB Cluster✅ Galera native
Default port543233063306
Docker image size~78MB (alpine)~565MB~403MB
Best forModern apps, complex queriesWordPress, legacy PHPMySQL replacement

PostgreSQL: The Default Choice for Modern Apps

PostgreSQL is the most feature-complete open source relational database. Governed by the PostgreSQL Global Development Group (community-owned, no corporate control), it implements more of the SQL standard than any other open source database.

Why PostgreSQL for New Projects

JSONB support. Postgres stores JSON as binary (JSONB), enabling full indexing and efficient querying:

-- Store JSON documents efficiently:
CREATE TABLE events (
  id BIGSERIAL PRIMARY KEY,
  data JSONB NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);

-- Index specific JSON fields:
CREATE INDEX idx_events_user ON events((data->>'user_id'));

-- Query JSON:
SELECT data->>'user_id', COUNT(*)
FROM events
WHERE data->>'event_type' = 'purchase'
  AND (data->>'amount')::numeric > 100
GROUP BY 1;

Extensions. PostgreSQL has a rich extension ecosystem:

  • pgvector: Vector similarity search for AI/ML applications
  • PostGIS: Geospatial queries (closest locations, within radius, etc.)
  • pg_trgm: Fuzzy text search (autocomplete, typo tolerance)
  • TimescaleDB: Time-series data optimization
  • Citus: Horizontal scaling / sharding

Better standards compliance. PostgreSQL implements more of the SQL standard, resulting in more portable queries.

Docker Setup

services:
  postgres:
    image: postgres:16-alpine    # 78MB vs 565MB for full MySQL image
    restart: unless-stopped
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myapp"]
      interval: 10s
      timeout: 5s
      retries: 5

Common Operations

# Connect:
docker exec -it postgres psql -U myapp

# Backup:
docker exec postgres pg_dump -U myapp myapp | gzip > backup.sql.gz

# Restore:
gunzip -c backup.sql.gz | docker exec -i postgres psql -U myapp -d myapp

MySQL 8.x: The PHP Ecosystem Standard

MySQL remains the world's most deployed relational database. Its ubiquity in the PHP ecosystem (WordPress, Drupal, Joomla, Laravel) means you'll encounter it constantly.

When to Choose MySQL

  • WordPress / Drupal / Joomla: These CMS platforms are optimized for MySQL and officially recommend it
  • Legacy PHP applications: Codebases written against MySQL-specific behavior
  • Existing MySQL infrastructure: No reason to switch if it works
  • Managed MySQL services: RDS, PlanetScale, Railway all offer managed MySQL

License Warning

MySQL's GPL 2.0 is a dual license — the Community Edition is free, but Oracle can (and does) restrict commercial use in certain ways. If you're building a commercial product and want simpler licensing: use PostgreSQL (permissive) or MariaDB (GPL without the Oracle dependency).

Docker Setup

services:
  mysql:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: myapp
      MYSQL_USER: myapp
      MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"
    command: --default-authentication-plugin=mysql_native_password
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

Common Operations

# Connect:
docker exec -it mysql mysql -u myapp -p myapp

# Backup (mysqldump):
docker exec mysql mysqldump -u myapp -pmypassword myapp | gzip > backup.sql.gz

# Restore:
gunzip -c backup.sql.gz | docker exec -i mysql mysql -u myapp -pmypassword myapp

MariaDB: MySQL Without Oracle

MariaDB was created in 2009 by Michael "Monty" Widenius — the original creator of MySQL — after Oracle acquired Sun Microsystems (and MySQL with it). MariaDB is a drop-in MySQL replacement with cleaner governance (MariaDB Foundation, non-profit) and some technical improvements.

Why MariaDB Instead of MySQL

No Oracle dependency. MariaDB Foundation governs the project with community input — similar to PostgreSQL's governance model.

Galera Cluster. MariaDB includes native Galera synchronous multi-master replication — all nodes accept writes, writes replicate synchronously to all nodes. MySQL's InnoDB Cluster achieves similar results but with more complexity.

# MariaDB Galera cluster (3-node):
services:
  mariadb-1:
    image: mariadb:11
    environment:
      MARIADB_ROOT_PASSWORD: secret
      MARIADB_GALERA_CLUSTER_NAME: my-cluster
      MARIADB_GALERA_NODE_ADDRESS: mariadb-1
      MARIADB_GALERA_CLUSTER_ADDRESS: gcomm://mariadb-1,mariadb-2,mariadb-3

Drop-in MySQL replacement. The vast majority of MySQL applications work unmodified with MariaDB — same port (3306), same client protocol, same SQL dialect.

Better JSON in newer versions. MariaDB 11.2+ improved JSON handling significantly.

Docker Setup

services:
  mariadb:
    image: mariadb:11
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: myapp
      MARIADB_USER: myapp
      MARIADB_PASSWORD: "${MARIADB_PASSWORD}"
      MARIADB_ROOT_PASSWORD: "${MARIADB_ROOT_PASSWORD}"
    volumes:
      - mariadb_data:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5

Common Operations

# Connect (uses mysql client — identical to MySQL):
docker exec -it mariadb mariadb -u myapp -p myapp

# Backup:
docker exec mariadb mysqldump -u myapp -pmypassword myapp | gzip > backup.sql.gz

Performance Comparison

All three databases handle typical self-hosted workloads comfortably. Performance differences matter at scale (100K+ queries/second, billions of rows).

For typical self-hosted apps (< 10K req/s):

ScenarioFastest
Simple reads (SELECT by primary key)MySQL ≈ MariaDB ≈ PostgreSQL
Complex joins and aggregationsPostgreSQL
JSON/document storage and queriesPostgreSQL (JSONB)
Full-text searchPostgreSQL (tsvector) ≈ MySQL
Write-heavy workloadsMySQL ≈ MariaDB ≈ PostgreSQL
Geospatial queriesPostgreSQL + PostGIS

For an app with <1M rows and <1K concurrent connections, any of the three performs excellently.


Migration Between Databases

MySQL/MariaDB → PostgreSQL

# Use pgloader for MySQL→PostgreSQL migration:
pgloader mysql://user:pass@localhost/source_db \
  postgresql://user:pass@localhost/target_db

pgloader handles type conversions, constraints, and indexes automatically.

PostgreSQL → MySQL

Harder — PostgreSQL's advanced types (JSONB, arrays, custom types) don't map directly to MySQL. Requires manual schema adaptation.


Framework Defaults

FrameworkDefault Database
DjangoPostgreSQL (recommended)
Ruby on RailsPostgreSQL (recommended)
LaravelMySQL (but PostgreSQL supported)
Next.js / PrismaPostgreSQL (recommended)
WordPressMySQL / MariaDB
DrupalMySQL / MariaDB / PostgreSQL
SupabasePostgreSQL
PocketBaseSQLite → PostgreSQL

The trend in modern full-stack frameworks (Next.js, Remix, SvelteKit) is strongly toward PostgreSQL.


Decision Guide

Choose PostgreSQL if:
  → New project with no legacy constraints
  → You use Prisma, Drizzle, SQLAlchemy, or Active Record (all support it)
  → You need JSONB, arrays, or advanced SQL features
  → You want PostGIS for geospatial data
  → You want pgvector for AI/vector similarity search
  → True open source governance matters

Choose MySQL 8.x if:
  → Running WordPress, Drupal, or Joomla
  → Legacy PHP codebase with MySQL-specific queries
  → Existing MySQL infrastructure with no reason to migrate
  → Using managed services (PlanetScale, AWS RDS MySQL)

Choose MariaDB if:
  → You want MySQL compatibility without Oracle dependency
  → You need Galera synchronous multi-master replication
  → Your Linux distro ships MariaDB by default
  → Commercial product where MySQL's GPL dual-license is a concern

See all open source database tools at OSSAlt.com/categories/databases.

Comments