Moving your PostgreSQL database off AWS? Whether you're cutting costs, simplifying your stack, or escaping vendor lock-in, Miget makes it straightforward to migrate from AWS RDS using AWS's own Database Migration Service (DMS).
This guide walks through a full migration: creating a DMS replication instance, pointing it at your RDS source and Miget target, and running a full-load migration. The same approach works for ongoing replication if you need a zero-downtime cutover.
What You'll Need
- An AWS account with permissions to create RDS, DMS, and IAM resources
- A Miget PostgreSQL database with public access enabled
- Your Miget database connection details (host, port, database, username, password)
- AWS CLI installed locally (or use the AWS Console)
Architecture
┌──────────────┐ ┌──────────────────┐
│ AWS RDS │ AWS DMS │ Miget │
│ PostgreSQL │ ──────────────────────>│ PostgreSQL │
│ (source) │ full-load or CDC │ (target) │
└──────────────┘ └──────────────────┘
AWS DMS reads from your RDS instance and writes directly to your Miget database over the public internet. Miget's PostgreSQL endpoint accepts standard PostgreSQL connections - no special configuration needed on the Miget side.
Step 1: Enable Public Access on Miget
In your Miget dashboard, navigate to your PostgreSQL service and enable Public Access. This gives your database a public endpoint like:
postgres-abc123.db.eu-east-1.onmiget.com:5432
Note down the connection details:
- Host -
postgres-abc123.db.eu-east-1.onmiget.com - Port -
5432 - Database - your database name
- Username - your database user
- Password - your database password
You can verify connectivity from your local machine:
PGPASSWORD=yourpassword psql -h postgres-abc123.db.eu-east-1.onmiget.com \
-U youruser yourdatabase -c "SELECT version();"
Step 2: Create a DMS Replication Instance
The replication instance is the DMS worker that reads from the source and writes to the target. A dms.t3.small is sufficient for most migrations.
Using AWS CLI
# Create a subnet group (uses default VPC subnets)
aws dms create-replication-subnet-group \
--replication-subnet-group-identifier my-dms-subnet \
--replication-subnet-group-description "DMS migration subnet" \
--subnet-ids subnet-aaaa subnet-bbbb subnet-cccc
# Create the replication instance
aws dms create-replication-instance \
--replication-instance-identifier rds-to-miget \
--replication-instance-class dms.t3.small \
--allocated-storage 20 \
--replication-subnet-group-identifier my-dms-subnet \
--publicly-accessible \
--no-multi-az
If you get an error about dms-vpc-role, create the IAM service role first - see the AWS DMS prerequisites docs.
Wait for the instance to become available:
aws dms wait replication-instance-available \
--filters Name=replication-instance-id,Values=rds-to-miget
Using AWS Console
- Go to AWS DMS > Replication instances > Create replication instance
- Set the name, instance class (
dms.t3.small), and storage - Choose your VPC and enable Publicly accessible
- Click Create
Step 3: Create Source Endpoint (RDS)
Point DMS at your existing RDS database:
aws dms create-endpoint \
--endpoint-identifier rds-source \
--endpoint-type source \
--engine-name postgres \
--server-name mydb.xxxxxxxxxxxx.eu-west-1.rds.amazonaws.com \
--port 5432 \
--database-name mydb \
--username postgres \
--password 'your-rds-password' \
--ssl-mode require
Use --ssl-mode require for the RDS source - RDS enforces SSL by default.
Step 4: Create Target Endpoint (Miget)
Point DMS at your Miget database:
aws dms create-endpoint \
--endpoint-identifier miget-target \
--endpoint-type target \
--engine-name postgres \
--server-name postgres-abc123.db.eu-east-1.onmiget.com \
--port 5432 \
--database-name yourdatabase \
--username youruser \
--password 'your-miget-password' \
--ssl-mode none
Use --ssl-mode none for the Miget target. The connection goes through Miget's PostgreSQL router which handles routing at the wire protocol level.
Step 5: Test Connectivity
Before creating the migration task, verify DMS can reach both endpoints:
# Get the replication instance ARN
REPL_ARN=$(aws dms describe-replication-instances \
--filters Name=replication-instance-id,Values=rds-to-miget \
--query 'ReplicationInstances[0].ReplicationInstanceArn' \
--output text)
# Get endpoint ARNs
SRC_ARN=$(aws dms describe-endpoints \
--filters Name=endpoint-id,Values=rds-source \
--query 'Endpoints[0].EndpointArn' --output text)
TGT_ARN=$(aws dms describe-endpoints \
--filters Name=endpoint-id,Values=miget-target \
--query 'Endpoints[0].EndpointArn' --output text)
# Test both connections
aws dms test-connection \
--replication-instance-arn $REPL_ARN \
--endpoint-arn $SRC_ARN
aws dms test-connection \
--replication-instance-arn $REPL_ARN \
--endpoint-arn $TGT_ARN
Wait for both to show successful:
aws dms describe-connections \
--filters Name=endpoint-arn,Values=$SRC_ARN,$TGT_ARN \
--query 'Connections[*].[EndpointIdentifier,Status]' \
--output table
Step 6: Create and Start the Migration Task
Create a full-load migration task that copies all tables from the public schema:
aws dms create-replication-task \
--replication-task-identifier rds-to-miget-fullload \
--source-endpoint-arn $SRC_ARN \
--target-endpoint-arn $TGT_ARN \
--replication-instance-arn $REPL_ARN \
--migration-type full-load \
--table-mappings '{
"rules": [{
"rule-type": "selection",
"rule-id": "1",
"rule-name": "select-all-public",
"object-locator": {
"schema-name": "public",
"table-name": "%"
},
"rule-action": "include"
}]
}'
Wait for the task to be ready, then start it:
# Wait for task creation
aws dms wait replication-task-ready \
--filters Name=replication-task-id,Values=rds-to-miget-fullload
# Get task ARN and start
TASK_ARN=$(aws dms describe-replication-tasks \
--filters Name=replication-task-id,Values=rds-to-miget-fullload \
--query 'ReplicationTasks[0].ReplicationTaskArn' --output text)
aws dms start-replication-task \
--replication-task-arn $TASK_ARN \
--start-replication-task-type start-replication
Step 7: Monitor Progress
Track the migration in real-time:
aws dms describe-replication-tasks \
--filters Name=replication-task-id,Values=rds-to-miget-fullload \
--query 'ReplicationTasks[0].ReplicationTaskStats' \
--output json
Example output during a migration:
{
"FullLoadProgressPercent": 100,
"ElapsedTimeMillis": 7055,
"TablesLoaded": 3,
"TablesLoading": 0,
"TablesQueued": 0,
"TablesErrored": 0
}
When FullLoadProgressPercent reaches 100 and TablesErrored is 0, your migration is complete.
Step 8: Verify the Data
Connect to your Miget database and confirm the data arrived:
PGPASSWORD=yourpassword psql -h postgres-abc123.db.eu-east-1.onmiget.com \
-U youruser yourdatabase -c "
SELECT table_name, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
"
Compare row counts against the source to verify completeness.
Zero-Downtime Migration with CDC
For production databases that can't afford downtime, use full-load + CDC (Change Data Capture) instead:
aws dms create-replication-task \
--replication-task-identifier rds-to-miget-cdc \
--source-endpoint-arn $SRC_ARN \
--target-endpoint-arn $TGT_ARN \
--replication-instance-arn $REPL_ARN \
--migration-type full-load-and-cdc \
--table-mappings '...'
This performs a full load first, then continuously replicates changes from RDS to Miget. When you're ready to cut over:
- Stop writes to your application
- Wait for DMS replication lag to reach zero
- Update your application's
DATABASE_URLto point to Miget - Resume your application
CDC requires logical replication to be enabled on your RDS instance. Set rds.logical_replication to 1 in your RDS parameter group and reboot the instance.
Cleanup
After a successful migration, delete the AWS resources to stop incurring charges:
# Stop and delete the task
aws dms stop-replication-task --replication-task-arn $TASK_ARN
aws dms delete-replication-task --replication-task-arn $TASK_ARN
# Delete endpoints
aws dms delete-endpoint --endpoint-arn $SRC_ARN
aws dms delete-endpoint --endpoint-arn $TGT_ARN
# Delete replication instance
aws dms delete-replication-instance --replication-instance-arn $REPL_ARN
# Delete RDS if no longer needed
aws rds delete-db-instance \
--db-instance-identifier mydb \
--skip-final-snapshot