Deployment and Migration

Deployment and migration are two related but distinct concerns when managing databases on AWS. Deployment is the process of setting up and configuring a database in AWS — creating an instance, choosing an instance type and storage option, and configuring security — using a service such as Amazon RDS, Amazon Aurora, or Amazon DocumentDB. Migration is the process of moving data from one database to another, whether from an on-premises database into AWS or between two AWS databases, using tools such as AWS Database Migration Service (DMS) and AWS Schema Conversion Tool (SCT). SCT and DMS also support conversion between different database types — for example, SCT can convert a MongoDB schema to a MySQL schema, and DMS can then migrate the data from MongoDB to MySQL.

Deployment Automation

Automating database deployments reduces the time and error risk associated with manual setup, and makes deployments consistent and repeatable across environments.

Infrastructure as Code

Infrastructure as Code (IaC) tools such as AWS CloudFormation and Terraform define the desired state of infrastructure in code, which can be version-controlled, tested, and deployed automatically. A CloudFormation template can declare a VPC, subnet, security group, and an RDS DB instance together, then be deployed with:

aws cloudformation create-stack --stack-name mydatabase --template-body file://database.yaml

Creates a CloudFormation stack from a local template file, provisioning every resource it declares.


The same infrastructure can be declared equivalently with Terraform and deployed with terraform init followed by terraform apply. Both approaches are shown in full in the code examples below.

CI/CD

Integrating database deployments into a Continuous Integration/Continuous Delivery (CI/CD) pipeline — using tools such as AWS CodePipeline and AWS CodeBuild — ensures database changes are tested and validated before reaching production.

Preconfigured Solutions and Monitoring

Amazon RDS offers preconfigured, scalable, secure, and highly available solutions for popular engines (MySQL, PostgreSQL, Oracle, and others) that can be deployed in a few clicks from the console or the AWS CLI. Once deployed, Amazon CloudWatch monitors key database metrics and can raise alerts on conditions such as high CPU usage or low disk space.

Evaluating Application Requirements

Before automating a deployment, evaluate the application's requirements to determine which components to deploy:


Choosing Deployment Tools

When selecting deployment tools and services, consider the team's existing familiarity with a tool; the complexity of the deployment (the console or CLI for simple deployments, CloudFormation or Terraform for deployments spanning multiple resources); how well the tool integrates with an existing CI/CD pipeline such as Jenkins or GitLab; the level of vendor support and documentation available; and any cost the tool adds beyond standard AWS fees.

Data Preparation and Migration

Preparing for a migration involves identifying every data source to migrate (on-premises databases, third-party applications, cloud services), determining the data's type and format (structured, unstructured, or semi-structured such as JSON/XML), assessing data quality (missing data, duplicates, inconsistencies), choosing a migration method appropriate to the data volume and downtime tolerance, planning the migration sequence and rollback procedure, and monitoring and tuning database performance during and after the migration.

Data Migration Methods

Snapshots, replication, and restore are the three broad migration methods; the right choice depends on data size, data complexity, tolerable downtime, and the desired level of control:


Migration Tools and Services

AWS Database Migration Service (DMS), AWS Schema Conversion Tool (SCT), and native database tools (MySQL Workbench's Migration Wizard, SQL Server Management Studio's Import and Export Wizard) are the principal migration tools on AWS. Choosing among them depends on the migration's complexity, the source and target database types, the data-consistency guarantees required, the tolerable downtime, cost, and ease of use.

AWS DMSA fully managed service supporting homogeneous migrations (e.g. Oracle to Oracle) and heterogeneous migrations (e.g. Oracle to PostgreSQL), with setup, configuration, and ongoing monitoring built in.
AWS SCTConverts the schema and code of a relational database — Oracle, Microsoft SQL Server, or MySQL — to run on Aurora, PostgreSQL, MySQL, or MariaDB. SCT can be used standalone (without DMS) to generate the SQL scripts that create the target schema, leaving data migration to other tools.
Native toolsTools bundled with the database software, such as MySQL Workbench or SQL Server Management Studio, suit smaller or simpler migrations.

AWS Database Migration Service (DMS)

AWS DMS is a managed migration and replication service that moves databases and analytics workloads into AWS while the source database remains fully operational, minimizing application downtime. It supports homogeneous migrations (Oracle to Oracle) as well as heterogeneous migrations between engines (Oracle or SQL Server to Aurora), and can continuously replicate data with low latency from a supported source to a supported target — for example, streaming multiple sources into Amazon S3 to build a data lake, or into Amazon Redshift to build a data warehouse.

Most replication tasks can be set up in under 10 minutes from the DMS section of the console: specify source and target endpoints, choose or create a replication instance, and accept or customize the schema mapping rules. Billing covers only the replication instances used and any additional log storage; data transfer itself is free, and DMS Schema Conversion is free to use as part of DMS (only storage is billed).

A typical migration creates the target database, migrates the schema, sets up data replication, performs a full load followed by ongoing change data capture and apply, and finishes with a switchover of the production environment once the target has caught up with the source. For continuous data replication rather than a one-time cutover, the same process applies but omits the final switchover step — the task simply runs until stopped or changed. Compare this asynchronous, continuous replication model with MySQL's own Replication mechanisms.

DMS can read from and write to encrypted databases: it connects at the SQL interface layer, so it can extract decrypted data from sources using Transparent Data Encryption (Oracle, SQL Server) or storage-level encryption, and propagate it to the target. Data encrypted at the application level passes through DMS unchanged, still encrypted, and is inserted into the target as-is; enabling encryption-at-rest on the target is recommended to preserve confidentiality.

Migration progress can be monitored through DMS metrics in the console, which give an end-to-end view of the replication pipeline including diagnostic and performance data at each stage. DMS also integrates with AWS CloudTrail and CloudWatch Logs, and exposes a provisioning API and CLI so that replication tasks can be created, restarted, managed, and terminated programmatically or on a schedule from outside the console.

Supported DMS Sources and Targets

Either the source or the target (or both) must reside in RDS or on EC2; replication directly between two on-premises databases is not supported.


AWS Schema Conversion Tool (SCT)

AWS SCT converts database schema and code so an existing application can run on a different engine, and can generate the SQL used to build the converted schema on the target. SCT and DMS work together: SCT copies schemas for homogeneous migrations and converts them for heterogeneous migrations, then either DMS or SCT moves the data itself — DMS traditionally handles smaller relational workloads (under 10 TB) with ongoing replication support, while SCT is primarily used for large data warehouse workloads and does not support ongoing replication.

SCT's OLTP schema conversions:

IBM Db2 for z/OSAurora MySQL, Aurora PostgreSQL, MySQL, PostgreSQL.
IBM Db2 LUWAurora MySQL, Aurora PostgreSQL, MariaDB, MySQL, PostgreSQL.
Microsoft Azure SQL DatabaseAurora MySQL, Aurora PostgreSQL, MySQL, PostgreSQL.
Microsoft SQL ServerAurora MySQL, Aurora PostgreSQL, Babelfish for Aurora PostgreSQL (assessment reports only), MariaDB, SQL Server, MySQL, PostgreSQL.
MySQLAurora PostgreSQL, MySQL, PostgreSQL. (MySQL schema and data can migrate to an Aurora MySQL cluster without AWS SCT/DMS at all.)
OracleAurora MySQL, Aurora PostgreSQL, MariaDB, MySQL, Oracle, PostgreSQL.
PostgreSQLAurora MySQL, Aurora PostgreSQL, MySQL, PostgreSQL.
SAP ASEAurora MySQL, Aurora PostgreSQL, MariaDB, MySQL, PostgreSQL.

SCT also converts data warehouses (Amazon Redshift, Azure Synapse Analytics, BigQuery, Greenplum, SQL Server, Netezza, Oracle, Teradata, Vertica, and Snowflake all convert to Amazon Redshift), one NoSQL conversion (Apache Cassandra to Amazon DynamoDB), big data frameworks (Apache Hive to Hive on Amazon EMR; Apache HDFS to S3 or HDFS on EMR), and ETL pipelines (Informatica scripts stay on Informatica; SQL Server Integration Services (SSIS) packages convert to AWS Glue or AWS Glue Studio; Teradata BTEQ scripts convert to AWS Glue or Amazon Redshift RSQL; Teradata FastExport/FastLoad/MultiLoad job scripts convert to Amazon Redshift RSQL).

As part of DMS, DMS Schema Conversion automates converting Oracle PL/SQL and SQL Server T-SQL code into the equivalent RDS for MySQL SQL dialect or PL/pgSQL for PostgreSQL; any fragment that cannot be converted automatically is clearly flagged for manual review. The downloadable, standalone AWS SCT client offers the same conversion capability outside of a DMS task.

Data Sources and Targets Preparation


Schema Conversion Methods

Using AWS SCT to determine and apply a schema conversion typically follows this sequence: install SCT (available for Windows, macOS, and Linux); configure it with AWS access keys and source/target connection details; create a project and select the source and target database types (MySQL, Oracle, SQL Server, PostgreSQL, and others are supported); analyze the source schema to surface differences in data types, constraints, indexes, and other objects versus the target; apply the conversion (SCT offers data type mappings, constraint conversions, and index conversions); test the conversion against a small data subset; then migrate the full schema using DMS, a native tool, or another third-party tool.

Heterogeneous vs. Homogeneous Migration

A homogeneous migration moves a database between platforms running the same engine — for example, an on-premises MySQL database to an Amazon RDS MySQL instance — and is simpler and lower-risk because no object or data-type conversion is needed. A heterogeneous migration moves a database to a different engine — for example, Oracle to Aurora PostgreSQL — and is more complex, since objects, data types, and other components must be converted for compatibility with the target engine.

Choosing between the two depends on engine compatibility (whether the source and target support the same data types and features), the overall complexity of the migration (database size, number of objects, degree of customization), the timeframe (heterogeneous migrations generally take longer due to schema conversion work), and the resources available (heterogeneous migrations often need additional database expertise or tooling).

Migration Execution and Validation

Executing a migration typically proceeds through these steps: configure AWS DMS (replication instance, source and target endpoints, and migration tasks); start the migration so DMS copies data from source to target through the replication instance; monitor the replication instance via the DMS console or CLI while it runs; validate the copied data once the migration completes, comparing source and target for consistency; test the target database with queries, integrity checks, and application-level testing; switch the application over to the target's endpoint once satisfied; and finally decommission the source database once the target is confirmed fully functional.

Design and Script

Designing a migration means deciding on the source and target databases, the migration method (full or incremental), and any required schema conversion or data transformation, then choosing the appropriate tool — DMS, SCT, the AWS CLI, or a hand-written script — extracting and transforming the data as needed, writing and thoroughly testing the migration scripts, running them while monitoring progress, validating the migrated data, and finally switching the application over to the new endpoint. Four common scripting approaches, each shown as a worked example below:


Extraction and Migration Scripts

Running migration scripts generally involves obtaining or writing the scripts for the chosen method; configuring the AWS environment ( security groups, IAM roles, network settings) so the scripts can reach the source and target databases; deploying the scripts to an EC2 instance or container, or attaching them to a DMS/SCT task; executing them while monitoring progress and troubleshooting any issues; and validating the target data and updating application configuration once the migration is complete.

Verification

After a migration, review the DMS/SCT logs and console output for errors or warnings and resolve them before proceeding; validate that the target schema matches the source schema (using SCT's automatic conversion or manual review); compare the data itself between source and target, using DMS or a third-party comparison tool; test the migrated data's functionality with representative queries and application traffic; monitor the target database's performance and tune settings or configuration as needed; and document the migration process and its verification results for future reference and auditing. See also Backup and Recovery for MySQL's own backup and restore mechanisms, which remain relevant for pre-migration backups of a source database.

# CloudFormation template that deploys a VPC, subnet, subnet group, security
# group, and an RDS MySQL instance
Resources:
  # Create a VPC for the database
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: MyDatabaseVpc

  # Create a subnet for the database
  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: "10.0.0.0/24"
      AvailabilityZone: "us-west-2a"
      Tags:
        - Key: Name
          Value: MyDatabaseSubnet

  # Create a subnet group for the database
  DatabaseSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Subnet group for MyDatabase
      SubnetIds:
        - !Ref Subnet

  # Create a security group for the database
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow traffic to database
      VpcId: !Ref Vpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: MyDatabaseSecurityGroup

  # Create an RDS instance for the database
  Database:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: mysql
      EngineVersion: "8.0"
      DBInstanceClass: db.t2.micro
      AllocatedStorage: "20"
      DBInstanceIdentifier: MyDatabase
      MasterUsername: admin
      MasterUserPassword: mypassword
      DBSubnetGroupName: !Ref DatabaseSubnetGroup
      VPCSecurityGroups:
        - !Ref SecurityGroup
      Tags:
        - Key: Name
          Value: MyDatabaseInstance

# Deploy the stack with:
#   aws cloudformation create-stack --stack-name mydatabase --template-body file://database.yaml

# Terraform configuration that deploys a VPC, subnet, security group, and an
# RDS MySQL instance

# Create a VPC for the database
resource "aws_vpc" "database_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "MyDatabaseVpc"
  }
}

# Create a subnet for the database
resource "aws_subnet" "database_subnet" {
  vpc_id            = aws_vpc.database_vpc.id
  cidr_block        = "10.0.0.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "MyDatabaseSubnet"
  }
}

# Create a subnet group for the database
resource "aws_db_subnet_group" "database_subnet_group" {
  name       = "MyDatabaseSubnetGroup"
  subnet_ids = [aws_subnet.database_subnet.id]
}

# Create a security group for the database
resource "aws_security_group" "database_security_group" {
  name   = "MyDatabaseSecurityGroup"
  vpc_id = aws_vpc.database_vpc.id

  ingress {
    from_port   = 3306
    to_port     = 3306
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "MyDatabaseSecurityGroup"
  }
}

# Create an RDS instance for the database
resource "aws_db_instance" "database_instance" {
  engine                  = "mysql"
  engine_version          = "8.0"
  instance_class          = "db.t2.micro"
  allocated_storage       = 20
  identifier              = "MyDatabase"
  username                = "admin"
  password                = "mypassword"
  db_subnet_group_name    = aws_db_subnet_group.database_subnet_group.name
  vpc_security_group_ids  = [aws_security_group.database_security_group.id]

  tags = {
    Name = "MyDatabaseInstance"
  }
}

# Deploy with:
#   terraform init
#   terraform apply

# File: dms_migration.yaml
# Describes an AWS DMS full-load replication task from a MySQL source to a
# PostgreSQL target
---
Task:
  Name: MySQL-to-PostgreSQL-Migration
  SourceEndpoint:
    EndpointIdentifier: mysql-source-endpoint
    EngineName: mysql
    ServerName: mysql-server.example.com
    Port: 3306
    DatabaseName: mydb
    ExtraConnectionAttributes: "ssl_mode=required"
    UserName: user
    Password: password
  TargetEndpoint:
    EndpointIdentifier: postgresql-target-endpoint
    EngineName: postgresql
    ServerName: postgresql-server.example.com
    Port: 5432
    DatabaseName: mydb
    ExtraConnectionAttributes: "sslmode=require"
    UserName: user
    Password: password
  ReplicationInstanceArn: arn:aws:dms:us-west-2:123456789012:rep:my-replication-instance
  MigrationType: full-load
  TableMappings:
    - TableName: mytable
      TargetTablePrepMode: doNothing
  MigrationSettings:
    TargetMetadata:
      TargetSchema: public
      SupportLobs: true

# Create the replication task from this file:
#   aws dms create-replication-task --replication-task-settings file://dms_migration.yaml

-- Sample AWS Schema Conversion Tool (SCT) script, run in the SCT SQL editor,
-- that stages a table and converts its data types and functions from an
-- Oracle source schema to a MySQL target schema

-- Work against the source database
USE aws_oracle_source;

-- Stage the conversion in a temporary table
CREATE TABLE temp_table (
    id   INT,
    name VARCHAR(50)
);

INSERT INTO temp_table
SELECT id, name FROM source_table;

DROP TABLE source_table;
ALTER TABLE temp_table RENAME TO source_table;

-- Switch to the target database and adjust data types/functions for MySQL
USE aws_mysql_target;

ALTER TABLE source_table
    MODIFY COLUMN id BIGINT,
    MODIFY COLUMN name VARCHAR(255);

UPDATE source_table
    SET name = CONCAT('Hello, ', name);

import boto3
import mysql.connector
import psycopg2

# Connect to the source MySQL database
mysql_conn = mysql.connector.connect(
    host="localhost",
    user="root",
    password="password",
    database="mydb"
)

# Select the data to migrate
mysql_cursor = mysql_conn.cursor()
mysql_cursor.execute(
    "SELECT column1, column2, column3 FROM mytable WHERE condition"
)
mysql_data = mysql_cursor.fetchall()

# Connect to the target PostgreSQL database
pg_conn = psycopg2.connect(
    host="localhost",
    user="postgres",
    password="password",
    database="mydb"
)

# Create the target table
pg_cursor = pg_conn.cursor()
pg_cursor.execute(
    "CREATE TABLE mytable (column1 INT, column2 VARCHAR(50), column3 TEXT)"
)

# Insert the extracted rows into PostgreSQL
for data in mysql_data:
    pg_cursor.execute(
        "INSERT INTO mytable (column1, column2, column3) VALUES (%s, %s, %s)",
        data
    )

pg_conn.commit()
mysql_cursor.close()
mysql_conn.close()
pg_cursor.close()
pg_conn.close()

# Export the migrated PostgreSQL table to S3 as CSV
s3 = boto3.client('s3')
bucket_name = 'mybucket'
key = 'mytable.csv'

pg_conn = psycopg2.connect(
    host="localhost",
    user="postgres",
    password="password",
    database="mydb"
)
pg_cursor = pg_conn.cursor()
with open(key, "w") as f:
    pg_cursor.copy_expert(
        "COPY mytable TO STDOUT WITH DELIMITER ',' CSV HEADER", f
    )
s3.upload_file(key, bucket_name, key)

# Load the S3 object into Amazon Redshift via the Redshift Data API
redshift = boto3.client('redshift-data')
redshift_copy_command = (
    f"COPY mytable FROM 's3://{bucket_name}/{key}' "
    "CREDENTIALS 'aws_iam_role=arn:aws:iam::123456789012:role/MyRedshiftRole' "
    "CSV DELIMITER ','"
)
redshift.execute_statement(Sql=redshift_copy_command)

-- Direct SQL migration script that transfers data from a MySQL source
-- database to a PostgreSQL target database

-- Select the data to migrate from the MySQL source
USE mydb;

SELECT column1, column2, column3
FROM mytable
WHERE condition;

-- Connect to the PostgreSQL target (psql meta-command)
\connect postgresql://user:password@localhost/mydb

-- Create the target table
CREATE TABLE mytable (
    column1 INT,
    column2 VARCHAR(50),
    column3 TEXT
);

-- Insert the migrated rows
INSERT INTO mytable (column1, column2, column3)
VALUES
    (value1, 'value2', 'value3'),
    (value4, 'value5', 'value6');