MariaDB vs PostgreSQL, Two Open-Source Patterns of Freedom

When people compare MariaDB and PostgreSQL, most focus on performance, indexing, or JSON support. But beneath those metrics lies a deeper question, one about freedom patterns and how open-source software evolves when power, profit, and community intersect.

This is not just a database comparison, it’s a story about two philosophies that shaped how open source survives in the age of cloud giants.

The Fork that Saved Openness

MariaDB was born out of rebellion. When Oracle bought Sun Microsystems, and with it, MySQL, the open-source world panicked.

What if Oracle slowly closed off MySQL, turning it into a corporate product?

Michael “Monty” Widenius, MySQL’s original creator, didn’t wait to find out. He forked the code and launched MariaDB, named after his daughter.

But this wasn’t just a technical fork, it was an ideological one.

MariaDB Foundation was created with a single purpose, to keep the database free forever, every commit, every new feature, had to align with that principle.

That philosophy speaks to me deeply. I choose MariaDB for most of my project is not because it’s faster or lighter, but because it embodies the idea that open source must protect itself.

It’s the same instinct that keeps communities free from corporate capture, a belief that openness isn’t just about code, it’s about governance and continuity.

The Quiet Strength of Continuity

PostgreSQL took an unusual path. It wasn’t born from a corporation but from the POSTGRES research project at UC Berkeley, and it matured under the PostgreSQL License, permissive in the same family spirit as BSD/MIT, built to invite adoption, forks, experiments, and strange new ideas without asking permission first.

Its governance is still profoundly community-shaped, a small core team, a long tail of contributors, and decisions hammered out through consensus rather than executive decree.

That gives PostgreSQL a quiet kind of legitimacy, earned, not marketed. But let’s not romanticize the consequence, permissive licensing also means anyone can take the core, wrap it in a proprietary control plane, and sell you “PostgreSQL” as a product while the real value, monitoring, automation, scaling knobs, guardrails, lives behind a curtain.

You see it everywhere, AWS RDS for PostgreSQL, Azure Database for PostgreSQL, Google Cloud SQL for PostgreSQL, the same familiar engine, but the steering wheel is locked in a vendor’s glovebox.

And you see a more explicit version of the same gravity in projects like TimescaleDB, where part of the stack stays permissively open, and another part shifts into a more restrictive, source-available reality depending on the feature set.

Perfectly legal, perfectly rational and exactly the kind of “open on the brochure, closed in the cockpit” pattern that makes my skin itch.

Licenses as Philosophy

Here’s where the difference stops being academic and becomes personal. MariaDB lives under GNU GPL v2, strong copyleft, not as a vibe, but as a weaponized boundary.

It doesn’t mean you must publish your code just because you run MariaDB. It means this, if you ship a modified MariaDB as a product, if you distribute the derivative, you don’t get to hoard the source like a dragon.

You either give back, or you don’t play.

PostgreSQL, on the other hand, is permissive by design. It lets you modify, embed, repackage, and yes, close your version if that’s what your business model demands.

That freedom is powerful, sometimes beautiful, but it also leaves a wide-open door for enclosure, rebranding, and “innovation” that never comes home.

So when people say, “It’s just a license,” I laugh.

MariaDB enforces openness through law, PostgreSQL relies on culture.

One is a contract you can take to court. The other is hope, and I’ve been in this industry long enough to know hope is not a strategy, especially when corporate gravity starts pulling.

Copyleft isn’t a limitation. It’s a seatbelt, You don’t wear it because you plan to crash, You wear it because eventually someone will.

Ecosystem and Commercial Gravity

MariaDB’s ecosystem stays closer to its roots, Galera Cluster, Aria, ColumnStore, there’s an insistence that the heart remains shareable, inspectable, forkable.

While there’s a commercial company in the picture, the MariaDB Foundation exists as a custodian of the server project, an upstream anchor that’s meant to keep “open” from becoming a marketing costume the moment a quarterly report gets nervous.

PostgreSQL thrives in a much wider commercial universe, and that’s both its triumph and its vulnerability. The ecosystem is full of excellent work, some fully open source, some open-core, some unapologetically proprietary, especially at the hosted layer where the money really lives.

You get compatible engines, managed platforms, proprietary acceleration, closed operational tooling, and branded “Postgres” experiences that are mostly about everything around Postgres.

The database becomes the seed crystal, the vendor layer becomes the cathedral.

MariaDB feels more deliberate. It keeps the core under GPL even when that choice makes monetization harder, slower, less seductive.

That philosophy resonates with how I build systems, I’d rather pay the price of discipline now than pay the ransom of lock-in later.

Openness first, business second, and if business can’t survive that order, maybe the business model is the problem.

Freedom vs Flexibility

MariaDB’s copyleft model offers a legal shield against enclosure. PostgreSQL’s permissive model offers freedom to innovate but leaves the door open for fragmentation.

To me, it comes down to what kind of freedom you value. PostgreSQL gives you freedom to close. MariaDB gives you freedom to stay open.

I personally prefer the second, because once your foundation is closed, rebuilding openness becomes almost impossible.

Choosing the Path

When I build systems that I want to last, that others can audit, fork, and improve, I want that permanence to be enforced, not assumed. GPL gives that.

PostgreSQL trusts the world to stay kind, MariaDB assumes the world will change and protects itself accordingly.

That’s why, when I architect systems using Node.js, MongoDB, and Debian, MariaDB feels like a natural fit, philosophically consistent with how I see open source, not as a license choice, but as a commitment to the next generation of developers.

Two Patterns, One Goal

Both MariaDB and PostgreSQL are pillars of open-source freedom. One preserves it through law, the other through trust, both work, both matter.

But in my journey, I choose MariaDB, not because it’s easier or faster, but because its very existence is a reminder, freedom in software doesn’t just survive by trust, it survives by design.

Safely Migrating a PostgreSQL 15 Database to Another Server

There are many ways to move a PostgreSQL database between servers, but for a production-grade setup,where roles already exist and you simply want to migrate one database cleanly, the safest path is to create the target database first and restore into it.

This approach keeps ownerships and permissions consistent, avoids overwriting roles, and works perfectly between PostgreSQL servers of the same major version (in this case, version 15).

Preparing the Destination

Start by making sure the role (user) already exists on the destination. In this example, we’ll migrate a database named myapp that belongs to the role myapp_user.

Check if the role exists.

sudo -u postgres psql -c "SELECT 1 FROM pg_roles WHERE rolname='myapp_user';"
Code language: JavaScript (javascript)

If not found, create it.

sudo -u postgres psql -c "CREATE ROLE myapp_user WITH LOGIN PASSWORD 'strongpass';"
Code language: JavaScript (javascript)

We won’t import global roles from the source, because I already manage users manually.

Step 1, Create a Backup on the Source Server

On the source host, run:

pg_dump -U postgres -h localhost -Fc -f /tmp/myapp.dump myapp

This command creates a custom-format dump file that can be restored flexibly and in parallel.

Check the file.

ls -lh /tmp/myapp.dump

Step 2, Copy the Dump to the Destination

Send the dump file securely to the new server.

scp /tmp/myapp.dump user@pg-dst:/tmp/
Code language: JavaScript (javascript)

Or, if you prefer to pull it.

scp user@pg-src:/tmp/myapp.dump /tmp/
Code language: JavaScript (javascript)

Step 3, Create the Target Database

Now on the destination server:

sudo -u postgres createdb -O myapp_user myapp

This prepares an empty database owned by the correct role.
Confirm.

sudo -u postgres psql -c "\l myapp"
Code language: JavaScript (javascript)

Step 4, Restore the Dump

Restore the data into the new database.

sudo -u postgres pg_restore -d myapp /tmp/myapp.dump

If you only want to import schema and data (ignoring ownership or ACL info from the dump), use.

sudo -u postgres pg_restore --no-owner --no-acl -d myapp /tmp/myapp.dump

Large databases can be restored in parallel.

sudo -u postgres pg_restore -j 4 --no-owner --no-acl -d myapp /tmp/myapp.dump

Step 5, Verify the Result

List tables.

sudo -u postgres psql -d myapp -c "\dt"
Code language: JavaScript (javascript)

Log in as the application user.

psql -h localhost -U myapp_user -d myapp

Inside psql.

SELECT current_user;
SELECT count(*) FROM information_schema.tables WHERE table_schema='public';
\q
Code language: JavaScript (javascript)

If that works, the migration is complete.

Optional, Direct Pipe Transfer (No Dump File)

If the servers are connected by SSH and you prefer streaming,

sudo -u postgres createdb -O myapp_user myapp
ssh postgres@pg-src "pg_dump -U postgres -Fc myapp" | sudo -u postgres pg_restore --no-owner --no-acl -d myapp
Code language: CSS (css)

This performs the same transfer in one step without creating a dump file on disk.

Summary

StepActionCommand
1Dump database on sourcepg_dump -Fc -f /tmp/myapp.dump myapp
2Copy to destinationscp /tmp/myapp.dump user@pg-dst:/tmp/
3Create DB on destinationcreatedb -O myapp_user myapp
4Restorepg_restore -d myapp /tmp/myapp.dump
5Verifypsql -d myapp -c "\dt"

Closing Notes

When both PostgreSQL instances are on version 15, this method is safe, idempotent, and easy to automate.

Creating the database first and assigning it to the same role keeps your access structure intact, ideal for teams managing multiple environments with predefined roles.

It’s the same principle you’d apply in any infrastructure move, build the container first, then pour the data in.

Master-Slave PostgreSQL Replication Setup on Debian 12

PostgreSQL replication is a robust solution for ensuring database availability, load balancing, and failover readiness. This guide provides a step-by-step approach to setting up Master-Slave replication on Debian 12, using PostgreSQL 15, along with hostname-based configurations.

1. System Prerequisites

1.1 Server Environment

We will use four Debian 12 servers with PostgreSQL 15 installed:

  • Master: postgre.master.x1.demo.internal (10.11.33.53)
  • Slave 1: postgre.slave.x1.demo.internal (10.11.33.66)
  • Slave 2: postgre.slave.x2.demo.internal (10.11.33.67)
  • Slave 3: postgre.slave.x3.demo.internal (10.11.33.68)

1.2 Update Hostname Configuration

Ensure that your /etc/hosts file includes the following lines:

127.0.0.1   localhost
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.11.33.53 postgre.master.x1.demo.internal
10.11.33.66 postgre.slave.x1.demo.internal
10.11.33.67 postgre.slave.x2.demo.internal
10.11.33.68 postgre.slave.x3.demo.internal
Code language: CSS (css)

2. Install PostgreSQL on All Servers

sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo apt install -y postgresql-15-postgis-3 postgresql-15-postgis-3-scripts


3. Configure the Master Server

3.1 Enable Replication Settings

Modify the PostgreSQL configuration file on the master:

sudo vi /etc/postgresql/15/main/postgresql.conf

Ensure these settings are applied:

listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
wal_keep_size = 512MB
max_wal_size = 4GB
min_wal_size = 1GB
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/15/archive/%f'
Code language: JavaScript (javascript)

Create the archive directory:

mkdir -p /var/lib/postgresql/15/archive
chown -R postgres:postgres /var/lib/postgresql/15/archive
chmod 700 /var/lib/postgresql/15/archive
Code language: JavaScript (javascript)

3.2 Configure Authentication

Modify the pg_hba.conf file to allow replication:

nano /etc/postgresql/15/main/pg_hba.conf

Add these lines:

# --- keep your existing local/loopback lines above ---

# 1) Replication: allow only the replicator from your subnet (MD5 as required)
host    replication     replicator      10.11.0.0/16           md5

# 2) App/client access: open to all (temporarily) but prefer SCRAM
host    all             all             0.0.0.0/0              scram-sha-256
host    all             all             ::/0                   scram-sha-256

# 3) Optional hardening even while “open”:
#    prevent remote superuser logins
host    all             postgres        0.0.0.0/0              reject
host    all             postgres        ::/0                   reject

Code language: PHP (php)

3.3 Restart the PostgreSQL Service

systemctl restart postgresql

3.4 Create Replication User

Switch to the PostgreSQL user and execute:

su - postgres -c "psql -c \"CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'yourpassword';\""
Code language: JavaScript (javascript)


4. Configure the Slave Servers

4.1 Stop PostgreSQL on the Slaves

systemctl stop postgresql

4.2 Remove Existing Data

rm -rf /var/lib/postgresql/15/main/*
Code language: JavaScript (javascript)

4.3 Synchronize Data from Master

su - postgres -c "pg_basebackup -h postgre.master.x1.demo.internal -D /var/lib/postgresql/15/main -U replicator -Fp -Xs -P -R"
Code language: JavaScript (javascript)

4.4 Create Standby Signal (If Needed)

touch /var/lib/postgresql/15/main/standby.signal
Code language: JavaScript (javascript)

4.5 Configure Connection to Master

Edit postgresql.conf on each slave:

nano /etc/postgresql/15/main/postgresql.conf
primary_conninfo = 'host=postgre.master.x1.demo.internal port=5432 user=replicator password=yourpassword'
Code language: JavaScript (javascript)

4.6 Restart PostgreSQL on the Slaves

systemctl start postgresql


5. Verify Replication

5.1 Check Master Replication Status

On the master, run:

su - postgres -c "psql -c \"SELECT * FROM pg_stat_replication;\""
Code language: JavaScript (javascript)

5.2 Check Slave Replication Status

On each slave, run:

su - postgres -c "psql -c \"SELECT pg_is_in_recovery();\""
Code language: JavaScript (javascript)

If replication is active, the output should be true.

5.3 Test Replication

On the master, create a test database and table:

su - postgres -c "psql -c \"CREATE DATABASE replication_test;\""
su - postgres -c "psql -d replication_test -c \"CREATE TABLE sample_table (id SERIAL PRIMARY KEY, value TEXT);\""
su - postgres -c "psql -d replication_test -c \"INSERT INTO sample_table (value) VALUES ('Replication Works!');\""
Code language: JavaScript (javascript)

On each slave, verify the data exists:

su - postgres -c "psql -d replication_test -c \"SELECT * FROM sample_table;\""
Code language: JavaScript (javascript)

6. Monitoring & Maintenance

6.1 Monitor Logs

journalctl -u postgresql

6.2 Set Up Automated Failover (Optional)

Consider using pg_auto_failover or Patroni for automated failover.

6.3 Schedule Regular Backups

Even with replication, backups are necessary. Use:

su - postgres -c "pg_dump -h postgre.master.x1.demo.internal -F c -b -v -f /backup/pg_backup.dump mydatabase"
Code language: JavaScript (javascript)

6.4 Automate WAL Cleanup

To prevent excessive storage usage, set up a cron job to delete old WAL files:

crontab -e

Add the following line to remove WAL files older than 7 days:

0 3 * * * sudo find /var/lib/postgresql/15/archive -type f -mtime +7 -delete
Code language: JavaScript (javascript)

This will run daily at 3 AM to free up space while ensuring replication stability.

7. Summary

This guide provides a host-based PostgreSQL Master-Slave replication setup on Debian 12. With real-time data replication, it ensures high availability and disaster recovery. By following these steps, your database cluster is resilient, scalable, and ready for production workloads. 🚀