DEV Community

Cover image for From Aiven to Railway: Our Database Migration
Ajisafe Victor Oluwapelumi
Ajisafe Victor Oluwapelumi

Posted on

From Aiven to Railway: Our Database Migration

I didn't plan a migration. I just noticed we were paying for two databases.

This whole thing started with a very unglamorous realization:

We were paying for Aiven Postgres…
and also paying for Railway.

At some point I opened the Railway dashboard properly and thought:

"Wait — why aren't we using this for the database too?"

No incident. No outage. No scaling crisis.

Just mild financial confusion.


The decision wasn't really "should we migrate?"

It was more like:

"We're already using Railway for everything else. Why is the database emotionally living somewhere else?"

So I did what any developer does when something feels inconsistent: I turned it into a migration project.


The migration itself was weirdly easy

I expected:

  • scripts
  • downtime planning
  • CSV exports
  • at least one regretful late-night rollback

Instead, Railway just pointed me to their Postgres migrator:

Railway Postgres Migrator

The setup was:

  • Source: Aiven Postgres URL
  • Destination: Railway Postgres
  • Action: Deploy

That's it. I didn't trust it at first.


The one thing that actually tripped me up

Before the migrator worked, I hit a version mismatch. Railway's Postgres was running v18 — and the migrator requires source and destination to be on the same major version. Aiven was v17, so I had to upgrade it first before anything else would proceed.

A screenshot of a PostgreSQL migration log showing timestamped entries from April 27, 2026. It records validation steps for source and target databases, indicating the source is PostgreSQL 17.9 and the target is PostgreSQL 18.3. The final line displays an error message stating “connection validation failed: major version mismatch,” highlighting that both databases must use the same major version.

Once versions matched, there was another quiet gotcha: two optional environment variables — NO_ACL and NO_OWNER — had to be set to true for the migration to complete cleanly.

Variable What it does
NO_ACL Skips restoring access privileges (GRANT/REVOKE commands)
NO_OWNER Skips restoring object ownership (ALTER OWNER commands)

The Railway docs show their default as true, but in practice they were false — meaning the migrator was trying to restore ACLs and ownership from Aiven, hitting permission errors, and failing silently enough that it wasn't immediately obvious why.

Setting both to true explicitly is what actually made it work.


I kept waiting for something to break. Nothing did.

  • No data loss.
  • No partial sync weirdness.
  • No "why is production dead" moment.

Just a working database in a new place, which is almost more stressful than failure — because you keep checking if you missed something.


Railway vs Aiven (honest experience, not a benchmark)

Aiven Postgres:

  • Solid and reliable
  • More manual setup
  • External to your deployment ecosystem
  • SSL and config overhead you have to manage

Railway Postgres:

  • Lives inside your deployment environment
  • Internal networking by default
  • Less cognitive overhead on config
  • Everything in one dashboard

The biggest difference wasn't performance. It was mental overhead. Fewer places to check. Fewer credentials to rotate. One less tab permanently open.


Would I recommend this?

If you're already on Railway and running an external Postgres for no strong reason — probably yes. The migrator genuinely works, the internal networking is a real quality-of-life upgrade, and the consolidation is worth it just to stop context-switching between dashboards.

If you have a strong reason for keeping the database external (compliance, specific Aiven features, multi-cloud setups) — that calculus changes. But "we started there and never questioned it" isn't a strong reason.

Sometimes the migration is just noticing you forgot to consolidate.

Top comments (0)