How to Migrate from AWS RDS to DanubeData (Step-by-Step Guide)

Rehearse an outbound AWS RDS PostgreSQL migration with extension, role, sequence and data checks, a write freeze, and an explicit rollback boundary.

Adrian Silaghi

10 min read

846 views

awsrdsmigrationpostgresqlmysqlcost-savings

Moving a database out of AWS RDS is a compatibility and recovery exercise before it is a cost exercise. This guide focuses on an outbound PostgreSQL migration to DanubeData Managed PostgreSQL, with a separate MySQL/MariaDB checklist. The default path uses a rehearsed dump and restore during a write freeze. It does not promise zero downtime.

Reviewed 2 November 2026. The downloadable lab below runs against two disposable local PostgreSQL containers. It checks a representative archive, roles, an extension, row security, data and identity sequences. It has not contacted a real RDS instance or a managed destination. Your source privileges, TLS, extensions, dataset size and cutover time need a separate rehearsal.

Why migrate, and when to keep RDS

A move can simplify a small database estate or change the operating cost and supplier arrangement. Compare the whole configuration: CPU, memory, storage, IOPS, replicas, recovery retention, support, outbound transfer, tax and currency. An entry price is not an equivalent replacement for an RDS Multi-AZ deployment. Use the current PostgreSQL plans and your AWS bill rather than the historical savings percentages previously shown here.

Keep RDS if the application depends on an AWS-only integration, its recovery model meets your requirements, or the engineering cost of a move outweighs a demonstrated benefit. EU infrastructure location is one input to supplier review; it does not by itself establish GDPR compliance.

Pre-migration inventory

Capture engine, capacity and application dependencies

aws rds describe-db-instances   --db-instance-identifier mydb   --query 'DBInstances[0].[DBInstanceClass,Engine,EngineVersion,AllocatedStorage,MultiAZ,BackupRetentionPeriod]'

Record the engine and major version, actual data and index size, growth rate, peak connections, slow queries, storage headroom, maintenance window and recovery objectives. Inventory every writer: web processes, workers, scheduled jobs, ETL, administration scripts and external integrations. Collect service owners and a way to stop and restart each writer. Changing a web application's connection string alone does not freeze the database.

Inspect PostgreSQL compatibility with a direct connection

Create protected libpq service definitions named rds_source and migration_target in your local service file. Set their host, database, user, port, sslmode=verify-full and the provider's trusted CA file. Put passwords in an owner-only .pgpass file or use your approved credential tooling. The commands below use service names so credentials do not appear in pasted URLs or shell history. Neither service should point at a transaction pooler.

psql 'service=rds_source' -X -v ON_ERROR_STOP=1 <<'SQL'
SHOW server_version;
SELECT pg_size_pretty(pg_database_size(current_database()));
SELECT extname, extversion FROM pg_extension ORDER BY extname;
SELECT rolname, rolsuper, rolreplication, rolbypassrls
FROM pg_roles WHERE rolname !~ '^pg_' ORDER BY rolname;
SELECT schemaname, tablename, policyname, roles, cmd, qual, with_check
FROM pg_policies ORDER BY schemaname, tablename, policyname;
SELECT schemaname, sequencename, last_value
FROM pg_sequences ORDER BY schemaname, sequencename;
SQL
pg_dump --version
pg_restore --version

Check destination support for each extension and its version, collations, encodings, procedural languages, foreign data wrappers, large objects and external endpoints. RDS-managed roles and privileges are not portable administrator roles. List application grants, default privileges, ownership, security-definer functions and scheduled extension jobs separately. A database archive does not create cluster-wide login roles or reproduce RDS IAM authentication.

Use a pg_dump client that understands the source major version. Restoring into an older PostgreSQL major version is not a safe default; rehearse the same or a newer supported destination with the appropriate client tools. Treat an engine upgrade and a provider move as two compatibility decisions. Review the PostgreSQL 18 pg_dump manual before selecting options.

Choose a migration method from measured constraints

PathSuitable whenWhat must be proved
Custom archive and restoreA maintenance window can cover the final export, transfer, restore and validationFull restore succeeds; all writers are frozen; application tests pass
Logical replication or a migration serviceThe measured dump window is too longSource permissions and settings, destination support, schema handling, replication coverage and a final consistent cutover
Physical replicationOnly with an explicitly supported provider-specific export or replication routeA documented supported RDS-to-target arrangement

The old version of this article showed a generic pg_basebackup from RDS followed by promotion of a managed destination. That is not a supported general recipe for leaving RDS. This article deliberately uses logical export. No downtime number is universal: rehearse with representative data and measure every stage.

Rehearse PostgreSQL dump and restore

Prepare an empty isolated target and explicit roles

Create a new database for the rehearsal, with enough disk for data, indexes, temporary work and growth. Never restore over the source or an existing production target. Provision the destination application roles with suitable new credentials before restoring. Decide whether to preserve application grants from the archive or deliberately recreate them. Do not import RDS service roles or grant an application superuser access to make a restore succeed.

For the commands below, the target login owns the restored objects and the archive retains application ACLs. This means named roles referenced by grants and policies must already exist. Using --no-acl would omit grants, not solve policy-role dependencies; it requires a reviewed permissions script afterward.

Export, inspect and restore

umask 077
mkdir -p migration-rehearsal
pg_dump --dbname='service=rds_source'   --format=custom --no-owner   --file=migration-rehearsal/application.dump
pg_restore --list migration-rehearsal/application.dump   > migration-rehearsal/archive-contents.txt
sha256sum migration-rehearsal/application.dump   > migration-rehearsal/application.dump.sha256
pg_restore --dbname='service=migration_target'   --no-owner --exit-on-error --verbose   migration-rehearsal/application.dump
psql 'service=migration_target' -X -v ON_ERROR_STOP=1 -c 'ANALYZE;'

Run these GNU/Linux commands from a controlled migration host. On macOS, shasum -a 256 can produce the digest instead. A custom-format archive can use pg_restore --jobs=4 when destination capacity permits; increasing parallelism is a measured tuning choice. A directory-format dump supports parallel export. Check tool output and exit status; a partially restored database is a failed rehearsal.

A dump reads a consistent database snapshot while normal source writes continue. That is useful for rehearsal, but it does not include writes committed after that snapshot. The final dump must happen after all application writes stop, unless an independently validated replication plan captures the changes. Inspect SQL before restoring data from a source you do not trust: database functions in an archive can execute code.

Validate more than row estimates

Do not use pg_stat_user_tables.n_live_tup as a reconciliation count: it is an estimate. Record exact counts and business totals on a frozen source and the restored target. Compare ordered digests for critical tables, verify indexes and constraints, list extensions and policies, and inspect sequences. Choose checks with your application's owner so an equal row count cannot conceal changed balances or missing attachments.

-- Run against source and target after the source write freeze.
SELECT count(*), min(id), max(id), sum(amount) FROM public.orders;
SELECT schemaname, tablename, indexname FROM pg_indexes
WHERE schemaname = 'public' ORDER BY tablename, indexname;
SELECT schemaname, sequencename, last_value FROM pg_sequences
ORDER BY schemaname, sequencename;

Replace orders and amount with your real tables and invariants. Test an insert that uses each important serial or identity sequence in the rehearsal database. Test as the application role, not just the owner. Verify SELECT, INSERT, UPDATE and DELETE authorization for two different tenants if the app uses row security, and ensure one cannot modify the other's data.

Download and run the local archive lab

Download the PostgreSQL migration rehearsal. With Docker Engine, Python 3 and the Docker CLI available, run python3 lab.py. It creates two uniquely named disposable PostgreSQL 18 containers without publishing ports and removes only those containers afterward. The image tag and resolved image ID appear in the result.

The fixture creates three rows, an hstore extension, an identity sequence and a tenant policy. It intentionally tries a restore before roles exist, checks that this fails, then provisions roles and restores a fresh schema. It compares an ordered data digest, verifies the extension and policy, checks Alice/Bob visibility, rejects a cross-tenant insert and checks the next generated ID. These are local toolchain checks, not proof of an RDS migration, a Supabase service migration or managed-platform compatibility. Read result.json in the download for the recorded run.

Cut over with a single write authority

  1. Approve a maintenance window using the measured rehearsal duration plus validation and rollback time. Take the source recovery point your organization requires.
  2. Stop all application and integration writers; drain or pause queues and scheduled jobs. Confirm that no unaccounted session is changing business data.
  3. Take the final archive, verify its transfer digest, restore to the prepared destination and rerun exact data and authorization checks.
  4. Update connection configuration and restart every process that caches database connections. Check direct and pooled endpoints separately, with TLS hostname verification.
  5. Run a read-only smoke test first. Then explicitly release the write freeze and perform a controlled application write/read test. Monitor errors, connection pressure, locks, queue processing and the actual user journey.

Record the time writes stop, the snapshot/export point, restore completion, validation completion and the moment target writes begin. DNS time-to-live is only part of the window; open connection pools may retain the old endpoint.

Rollback before and after target writes

Before the target accepts writes: if validation fails, keep it isolated, point all clients back to the unchanged source, reopen source writes and record the failed cutover. Confirm every writer moved back.

After the target accepts writes: the source is stale. Simply switching back can lose every new order, account change and job result. Freeze writes again, preserve both databases, reconcile or reverse-migrate target changes under a tested plan, and only then choose a new authority. One-way source-to-target replication is not reverse synchronization. Keeping RDS for seven days is useful for investigation but does not make an immediate rollback safe.

If you need logical replication

AWS documents RDS logical replication requirements, including the static rds.logical_replication setting, reboot and appropriate RDS roles. Confirm source storage can retain WAL while a consumer is unavailable; abandoned slots can fill the source disk.

Establish source and destination compatibility, replica identity for updated/deleted tables, a schema-change freeze, extension coverage and the exact set of tables. Monitor table synchronization and apply errors rather than relying only on a lag display. Before target writes, account for sequences, DDL and large objects: PostgreSQL logical replication does not replicate all of them. Stop source writes, prove the final change has arrived and set sequence state before switching writers. Do not copy a publication/subscription snippet into production without a provider-approved design.

MySQL and MariaDB need a separate rehearsal

PostgreSQL instructions do not migrate MySQL. For an RDS MySQL or MariaDB source, inventory engine and major version, character sets, collations, SQL modes, storage engines, routines, triggers, events, users, grants and DEFINER clauses. Check destination support and choose the matching vendor's client. A --single-transaction dump gives a consistent snapshot for transactional tables; concurrent DDL and nontransactional tables need additional handling. Do not import AWS system databases into a different managed service.

# Example for an application database; rehearse against matching versions.
# Credentials belong in owner-only client option files.
set -euo pipefail
umask 077
mysqldump --defaults-extra-file=/secure/rds-client.cnf   --single-transaction --routines --triggers --events   --databases application > application.sql
mysql --defaults-extra-file=/secure/target-client.cnf < application.sql

Review GTID, tablespace and privilege options against the exact MySQL/MariaDB versions; this example is not a replication setup. Test login, exact business totals, routines and generated keys. For lower downtime, use a supported binlog/DMS migration design and test the same single-writer and rollback boundaries. No live MySQL or MariaDB migration was run for this article.

Common failures and safe responses

  • Missing extension: stop and confirm a supported target version. Removing its objects may change application behavior.
  • Missing role or permission: restore the reviewed role/grant plan, then repeat on an empty target. A broad superuser grant is not a fix.
  • Sequence conflict: compare sequence state and table IDs in rehearsal, then repair with an application-aware plan while writes are frozen.
  • Slow restore: measure index build, network and disk pressure before adjusting parallelism.
  • TLS error: fix the CA/hostname/service configuration. Do not disable verification for a cutover.
  • Application passes reads but fails writes: test grants, defaults, triggers and background workers before releasing maintenance.

Plan your destination and recovery

Use the PostgreSQL product page to select the current version, capacity and recovery options. For independent encrypted archives, see the Linux Restic backup and restore guide and Object Storage. For Neon or Supabase, additional source-specific dependencies apply.

Ask for a migration review with the source version, extension inventory, measured rehearsal result, required recovery window and planned write-freeze duration. The evidence that matters is a restored application and a reversible cutover plan, not an unverified savings or downtime claim.

Browse all articles
Tutorials7 min read

Raport zilnic de stoc WooCommerce cu n8n

Importă un workflow n8n pentru raportul de stoc WooCommerce: paginare, variații, praguri și controlul retrimiterilor, cu acces doar de citire în magazin.

Adrian Silaghi59 views

Ready to get started?

Deploy your infrastructure in minutes with DanubeData's managed services.