How to Set Up Amazon RDS: 12 Steps, 90 Min [2026]

Amazon RDS runs the database engine, applies patches, takes backups, and fails over to a standby when hardware misbehaves, so a team spends its time on schema design and query tuning instead of babysitting a database server. That pitch has aged well: “amazon rds” pulls roughly 8,100 searches a month in the US as of June 2026, according to DataForSEO tracking, with low keyword competition compared with most other AWS service terms. Demand for a real walkthrough, not just a marketing page, is the reason this tutorial exists.

By the end of this guide you’ll have a production-shaped setup, not a toy demo. That means a PostgreSQL instance running inside a private VPC subnet, a security group that only allows traffic from an application tier, credentials stored in AWS Secrets Manager instead of a hardcoded config file, Multi-AZ failover switched on, a read replica for reporting queries, automated backups with a 14-day retention window, IAM database authentication as an alternative to long-lived passwords, and a Terraform file that reproduces the entire stack from code. Every step includes the console path and, where it matters, the equivalent AWS CLI or Terraform command.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

What Is Amazon RDS and What You’ll Build in This Tutorial

Amazon RDS supports seven database engines: Amazon Aurora (AWS’s own MySQL- and PostgreSQL-compatible engine), PostgreSQL, MySQL, MariaDB, Oracle Database, Microsoft SQL Server, and Db2, according to AWS’s official RDS documentation. The SQL Server engine got a notable upgrade in July 2026, when AWS added support for SQL Server 2025 on RDS, including Standard Edition configurations with up to 32 cores, while the SQL Server 2025 Developer Edition now carries a $0 license cost as of July 2026. Each engine runs on a managed DB instance that AWS provisions, patches, and monitors. You choose the instance class (how much CPU and memory it gets), the storage type and size, and whether it should run in one Availability Zone or two.

The gap between RDS and “install PostgreSQL on an EC2 box yourself” comes down to what AWS takes off your plate: automated backups, point-in-time recovery, minor-version patching, Multi-AZ failover, read replicas provisioned in a few clicks, and IAM-integrated authentication. You give up root access to the underlying OS in exchange. For most application teams that trade is worth it, which is a large part of why RDS remains one of the most searched AWS services on the market.

This tutorial uses PostgreSQL for the hands-on steps because it’s the most-searched RDS engine after the platform itself (roughly 1,000 monthly searches for “amazon rds postgres” per DataForSEO). The same workflow applies to MySQL and MariaDB with minor syntax differences noted inline, and both engines keep shipping new preview versions worth tracking: AWS rolled out an RDS PostgreSQL 19 Beta 2 preview on July 16, 2026, with preview instances set to auto-delete after 60 days, and followed it with a preview of RDS MySQL 9.7 on July 23, 2026, currently limited to the US East (Ohio) region. If you’re deciding between RDS and Aurora, or between Postgres and MySQL specifically, see our separate breakdowns on Amazon Aurora vs RDS and PostgreSQL vs MySQL. This guide focuses on getting a real instance running correctly the first time.

Prerequisites and Amazon RDS Pricing You Need to Know

You’ll need an AWS account with billing enabled, an IAM user or role with permissions for RDS, EC2 (for VPC and security groups), and Secrets Manager, plus the AWS CLI (version 2.x) installed and configured with aws configure. A local PostgreSQL client (psql) or a GUI tool like DBeaver or pgAdmin makes Step 4 easier but isn’t required. If you plan to follow the infrastructure-as-code path in Step 11, install Terraform 1.x as well.

Cost is the first thing most teams get wrong, so look at the real numbers before you provision anything. AWS still runs an RDS Free Tier: 750 instance-hours a month on db.t2.micro, db.t3.micro, or db.t4g.micro for MySQL, PostgreSQL, or MariaDB in a Single-AZ configuration, for 12 months from account creation. Past the free tier, on-demand pricing for the smallest burstable instance class looks like this in US East (N. Virginia):

Instance ClassvCPU / RAMSingle-AZ (hourly / monthly)Multi-AZ Instance (hourly / monthly)Typical Use Case
db.t4g.micro2 vCPU / 1 GiB$0.016 / ~$11.68$0.032 / ~$23.36Dev, test, free tier
db.t4g.small2 vCPU / 2 GiB$0.032 / ~$23.36$0.064 / ~$46.72Small production apps
db.m6g.large2 vCPU / 8 GiB~$0.17 / ~$124~$0.34 / ~$248Mid-size production
db.r6g.xlarge4 vCPU / 32 GiB~$0.68 / ~$496~$1.36 / ~$992Memory-heavy workloads

Storage and I/O are billed separately from compute, and Extended Support is a line item worth flagging early: once a PostgreSQL or MySQL major version ages past AWS’s standard support window, RDS automatically shifts the instance into Extended Support, billed per vCPU-hour on top of the normal instance cost, per AWS’s extended support documentation. Staying current on major versions is the cheapest way to avoid that surprise. Full, current pricing always lives on the official Amazon RDS pricing page, since rates vary by region and change over time.

Step 1: Choose Your Database Engine and Instance Class

Pick the engine before you touch the console. If your team already writes SQL against PostgreSQL or MySQL and doesn’t need Oracle- or SQL Server-specific features, stick with the open-source engine, it’s cheaper and has no per-core licensing to track. PostgreSQL tends to win for teams that want strict data typing, JSONB columns, and window functions. MySQL wins when a framework or CMS (WordPress, Magento, older Rails apps) expects it by default.

For instance class, start smaller than instinct suggests. A db.t4g.micro or db.t4g.small handles most early-stage applications, and RDS lets you change instance class later with a few minutes of downtime (or none, if Multi-AZ is already on, since AWS fails over to the resized standby first). The “g” in t4g and m6g means the instance runs on AWS Graviton, AWS’s own Arm-based processor. Graviton instance classes are consistently priced lower than their Intel/AMD equivalents for the same vCPU and memory, a pattern we cover in more depth in our AWS Graviton vs Intel/AMD comparison. Unless a driver or extension you depend on explicitly requires x86, default to Graviton. When you do need x86, AWS added CPU-optimized M7i and R7i instance classes for RDS for SQL Server in December 2025, priced lower than the equivalent prior-generation instances, making them the newer x86 option worth checking before defaulting to an older M6i or R6i class.

Storage type matters just as much as instance class. General Purpose SSD (gp3) is the default and the right choice for the vast majority of workloads, since it decouples IOPS and throughput from volume size, unlike the older gp2 tier. Only reach for Provisioned IOPS (io2) storage once you’ve measured a real I/O bottleneck with Performance Insights, which you’ll set up in Step 10. Ceilings have moved too: AWS quadrupled the maximum storage for RDS for SQL Server to 256 TiB per instance in December 2025, and as of July 2026 RDS allows up to four storage modifications per 24-hour period per instance, so you can resize or change storage type more than once a day if a workload’s needs shift quickly.

Step 2: Set Up a VPC, Subnet Group, and Security Groups

RDS instances live inside a VPC, and AWS strongly recommends placing production databases in private subnets with no direct route to the internet, per the RDS VPC documentation. You need at least two subnets in two different Availability Zones inside the same VPC, even for a Single-AZ instance, because RDS requires a DB subnet group that spans multiple AZs to support future Multi-AZ upgrades and maintenance operations.

Create the subnet group first. In the RDS console, go to Subnet Groups, choose your VPC, and select two or more private subnets across different AZs. From the CLI:

aws rds create-db-subnet-group \
  --db-subnet-group-name app-db-subnet-group \
  --db-subnet-group-description "Private subnets for RDS" \
  --subnet-ids subnet-0a1b2c3d4e5f60789 subnet-0f9e8d7c6b5a43210

Next, create a security group that only accepts traffic on the database port (5432 for PostgreSQL, 3306 for MySQL/MariaDB) from your application’s security group, never from 0.0.0.0/0:

aws ec2 create-security-group \
  --group-name rds-postgres-sg \
  --description "Allows Postgres traffic from app tier only" \
  --vpc-id vpc-0123456789abcdef0

aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp --port 5432 \
  --source-group sg-0fedcba9876543210

That second command is the one people skip under deadline pressure, then open the database to the entire internet “just for now.” Don’t. A publicly exposed database with a weak password is still one of the most common ways production data ends up for sale.

Step 3: Launch Your Amazon RDS Instance

In the RDS console, choose Create database, then Standard create (Easy create hides options you want visible for a production build). Select PostgreSQL and the latest supported major version, for production builds that means PostgreSQL 17.4 as used throughout this guide, though AWS has already previewed PostgreSQL 18.0 on RDS (released September 25, 2025, with native UUIDv7 support) for teams that want to test the next major version ahead of general availability, choose the instance class and storage settings from Step 1, attach the subnet group and security group from Step 2, and set Public access to No. Leave Multi-AZ off for now, you’ll enable it deliberately in Step 6 so you can see the difference it makes to the instance’s behavior and cost.

Using the AWS CLI Instead of the Console

The console is fine for a one-off instance, but the CLI is what you’ll actually use once this becomes routine. Here’s the equivalent create command:

aws rds create-db-instance \
  --db-instance-identifier app-postgres-prod \
  --db-instance-class db.t4g.small \
  --engine postgres \
  --engine-version 17.4 \
  --master-username appadmin \
  --manage-master-user-password \
  --allocated-storage 20 \
  --storage-type gp3 \
  --db-subnet-group-name app-db-subnet-group \
  --vpc-security-group-ids sg-0123456789abcdef0 \
  --backup-retention-period 14 \
  --no-publicly-accessible \
  --storage-encrypted

Two flags there are doing real work. --manage-master-user-password tells RDS to generate the master password and store it in Secrets Manager automatically instead of asking you to type one in, which is the fast path to the setup you’re building toward in Step 5 anyway. --storage-encrypted turns on encryption at rest using AWS KMS, which costs nothing extra and should be the default on every database you create from this point forward. Provisioning typically takes several minutes. Check status with aws rds describe-db-instances --db-instance-identifier app-postgres-prod --query 'DBInstances[0].DBInstanceStatus' until it returns available.

Step 4: Connect to Your Database From a Client or App

Once the instance status shows available, grab its endpoint from the console or with aws rds describe-db-instances --db-instance-identifier app-postgres-prod --query 'DBInstances[0].Endpoint.Address'. Because the instance is private, you’ll need to connect from inside the VPC, through a bastion host, an SSM-enabled EC2 instance, or a VPN, not directly from your laptop.

psql -h app-postgres-prod.abc123xyz.us-east-1.rds.amazonaws.com \
     -U appadmin -d postgres -p 5432

A successful connection drops you into the psql prompt against the default postgres database. Create the application database and a scoped user before you point any app at it:

CREATE DATABASE appdb;
CREATE USER app_user WITH PASSWORD 'use-a-generated-password-here';
GRANT ALL PRIVILEGES ON DATABASE appdb TO app_user;

Never connect application code with the master user. Create a scoped role per application, and rotate its password on a schedule, which is exactly what the next step automates.

Step 5: Secure Credentials With AWS Secrets Manager

If you used --manage-master-user-password in Step 3, the master credential is already sitting in Secrets Manager. For the application-level user created in Step 4, store it the same way rather than dropping it into an environment variable or a .env file that inevitably gets committed to git at some point.

aws secretsmanager create-secret \
  --name prod/appdb/app_user \
  --description "App-tier Postgres credentials for appdb" \
  --secret-string '{"username":"app_user","password":"use-a-generated-password-here","host":"app-postgres-prod.abc123xyz.us-east-1.rds.amazonaws.com","port":5432,"dbname":"appdb"}'

Your application then fetches the secret at startup instead of reading a static config value:

import boto3, json

client = boto3.client("secretsmanager", region_name="us-east-1")
response = client.get_secret_value(SecretId="prod/appdb/app_user")
creds = json.loads(response["SecretString"])

conn_string = f"postgresql://{creds['username']}:{creds['password']}@{creds['host']}:{creds['port']}/{creds['dbname']}"

This buys you two things: nobody needs to grep through source code to find a password, and you can rotate credentials on a schedule without redeploying application code. We cover the full setup, including automatic rotation with a Lambda function, in our dedicated AWS Secrets Manager tutorial.

Step 6: Enable Multi-AZ for High Availability

A Single-AZ instance has exactly one copy of your data running in one physical data center. If that AZ has a bad day, or AWS needs to apply a patch during your maintenance window, your database goes down until it recovers. Multi-AZ fixes that by keeping a synchronously replicated standby in a second AZ and failing over to it automatically, and AWS backs the Multi-AZ configuration with a 99.95% monthly uptime SLA.

aws rds modify-db-instance \
  --db-instance-identifier app-postgres-prod \
  --multi-az \
  --apply-immediately

--apply-immediately triggers the change right away instead of waiting for the next maintenance window, and it comes with a brief interruption while the standby is provisioned and initial sync completes, so schedule it outside peak traffic the first time.

Multi-AZ DB Instance vs Multi-AZ DB Cluster

RDS actually offers two different Multi-AZ topologies, and the naming is confusing enough that it trips up experienced engineers. A Multi-AZ DB instance is the classic model: one primary, one standby, and the standby exists purely for failover, you can’t read from it. A Multi-AZ DB cluster runs one writer and two readable standbys across three AZs, using a faster, quorum-based replication protocol that typically fails over faster than the two-node model, and the two standbys can serve read traffic instead of sitting idle. The cluster option costs more (three instances instead of two) and isn’t available for every engine version, so check current availability for your engine before committing to it, but for write-heavy, latency-sensitive workloads it’s worth the extra spend.

Step 7: Add Read Replicas to Scale Reads

Multi-AZ solves availability. Read replicas solve throughput. If your application runs expensive reporting queries or you’re seeing read-heavy load that’s saturating the primary, a read replica gives you a separate, asynchronously replicated copy you can point read-only traffic at, per AWS’s read replica documentation.

aws rds create-db-instance-read-replica \
  --db-instance-identifier app-postgres-replica-1 \
  --source-db-instance-identifier app-postgres-prod \
  --db-instance-class db.t4g.small

Because replication is asynchronous, a replica can lag behind the primary by anywhere from milliseconds to several seconds under heavy write load. Never point a read replica at a workflow that reads its own writes immediately, a user updating their profile and then reloading the page a moment later is exactly the case that breaks. Route that traffic to the primary and reserve replicas for reporting, analytics, and background jobs that tolerate a few seconds of staleness.

With Multi-AZ and read replicas both in play, it’s easy to lose track of which option solves which problem. Here’s the quick-reference version:

ConfigurationSolvesTypical Failover / LagReadable?Relative Cost
Single-AZNothing extraNo automatic failoverYes (only copy)1x baseline
Multi-AZ DB instanceAvailability~60-120 secondsNo (standby only)~2x baseline
Multi-AZ DB clusterAvailability + read scalingFaster than 2-node modelYes (both standbys)~3x baseline
Read replicaRead throughputMilliseconds to seconds of lagYes (async copy)+1x per replica

Step 8: Configure Automated Backups and Manual Snapshots

RDS takes automated backups daily during a backup window you configure, and it also streams transaction logs continuously, which is what enables point-in-time recovery to any second within the retention window, according to the RDS automated backups documentation. The default retention is 7 days, and the maximum is 35. For anything running real traffic, push retention to at least 14 days. The cost difference is small relative to what a lost week of data would cost you.

aws rds modify-db-instance \
  --db-instance-identifier app-postgres-prod \
  --backup-retention-period 14 \
  --preferred-backup-window "06:00-06:30" \
  --apply-immediately

Automated backups disappear when you delete the instance unless you explicitly keep a final snapshot. Take manual snapshots before any risky change, a major version upgrade, a large schema migration, a bulk delete, since manual snapshots persist independently of the instance’s lifecycle until you delete them yourself:

aws rds create-db-snapshot \
  --db-instance-identifier app-postgres-prod \
  --db-snapshot-identifier pre-migration-2026-06-13

Step 9: Turn On IAM Database Authentication

IAM database authentication replaces long-lived database passwords with short-lived tokens generated from IAM credentials, and it’s supported on MySQL, PostgreSQL, and MariaDB, per AWS’s IAM database authentication docs. Tokens expire after 15 minutes, which means a leaked token is far less dangerous than a leaked password, and you get IAM’s audit trail for free.

Enable it on the instance, then grant the database role permission to authenticate via IAM:

aws rds modify-db-instance \
  --db-instance-identifier app-postgres-prod \
  --enable-iam-database-authentication \
  --apply-immediately
-- Inside psql, connected as the master user:
CREATE USER iam_app_user WITH LOGIN;
GRANT rds_iam TO iam_app_user;

IAM authentication is a good fit for application roles running on EC2, ECS, or Lambda with an attached IAM role. It’s a worse fit for third-party BI tools that only know how to speak username-and-password, so most teams run both authentication methods side by side rather than ripping out passwords entirely.

Step 10: Monitor Performance With CloudWatch and Performance Insights

Every RDS instance ships basic metrics to CloudWatch automatically: CPU, free storage, freeable memory, read/write IOPS, and connection count. That’s enough to catch an outage but not enough to explain a slow query. Performance Insights fills that gap by showing exactly which SQL statements are consuming database load, down to the individual query and wait event.

Worth knowing before you rely on it long-term: AWS is retiring the classic Performance Insights console experience and its flexible 1-to-24-month retention tiers on June 30, 2026, shifting billing and the primary UI to CloudWatch Database Insights, per the AWS RDS Performance Insights documentation. The underlying API keeps working, so query-level data collection itself isn’t going away, only the console you view it in and how retention is priced.

aws rds modify-db-instance \
  --db-instance-identifier app-postgres-prod \
  --enable-performance-insights \
  --performance-insights-retention-period 7 \
  --apply-immediately

Set a CloudWatch alarm on FreeStorageSpace and CPUUtilization at minimum. A database that silently runs out of disk doesn’t degrade gracefully, it stops accepting writes, so this is not an optional step for anything handling real traffic.

Step 11: Provision RDS With Terraform Instead of the Console

Everything above works fine for a first instance, but clicking through the console or hand-typing CLI commands doesn’t scale past one environment. Once you have a staging environment and a production environment that need to stay in sync, move to infrastructure as code. Here’s a trimmed Terraform configuration that reproduces the setup from Steps 2 through 8:

resource "aws_db_subnet_group" "app" {
  name       = "app-db-subnet-group"
  subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
}

resource "aws_security_group" "rds" {
  name   = "rds-postgres-sg"
  vpc_id = aws_vpc.main.id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }
}

resource "aws_db_instance" "app_postgres" {
  identifier                    = "app-postgres-prod"
  engine                        = "postgres"
  engine_version                = "17.4"
  instance_class                = "db.t4g.small"
  allocated_storage             = 20
  storage_type                  = "gp3"
  storage_encrypted             = true
  db_subnet_group_name          = aws_db_subnet_group.app.name
  vpc_security_group_ids        = [aws_security_group.rds.id]
  multi_az                      = true
  backup_retention_period       = 14
  publicly_accessible           = false
  manage_master_user_password   = true
  iam_database_authentication_enabled = true
  performance_insights_enabled  = true
  skip_final_snapshot           = false
  final_snapshot_identifier     = "app-postgres-final"
}

Store the Terraform state remotely (an S3 backend with DynamoDB locking is the standard pattern) so a second engineer doesn’t overwrite your changes. If you’re new to Terraform on AWS generally, our Terraform for AWS setup guide covers the backend configuration and provider setup this snippet assumes.

Step 12: Clean Up Unused Resources and Control Costs

RDS bills by the hour whether or not anything connects to the database, and Multi-AZ, read replicas, and Provisioned IOPS storage all multiply that base cost. Before you walk away from this tutorial, audit what you actually need running.

For non-production instances, use aws rds stop-db-instance overnight and on weekends, since a stopped instance doesn’t bill for compute (storage charges continue). AWS automatically restarts a stopped instance after 7 days, so pair this with a scheduled Lambda function or EventBridge rule if you want it to stay off longer. For anything you’re tearing down permanently, always take a final snapshot rather than skipping it:

aws rds delete-db-instance \
  --db-instance-identifier app-postgres-dev \
  --final-db-snapshot-identifier app-postgres-dev-final \
  --no-skip-final-snapshot

Check for orphaned resources too: manual snapshots don’t expire on their own and keep billing for storage indefinitely, and an unused read replica left running after a reporting project wraps up is a surprisingly common line item on AWS bills that nobody remembers approving.

A Complete Working Example: Connecting a Node.js App to RDS

Everything so far has been infrastructure. Here’s how it looks from the application side, tying together the instance from Step 3, the Secrets Manager credential from Step 5, and the IAM authentication option from Step 9 into one small Express API that reads and writes real rows.

Install the dependencies first: npm install express pg @aws-sdk/client-secrets-manager. The connection module fetches credentials from Secrets Manager once at startup and reuses a pooled connection for every request, rather than opening a new one per call:

// db.js
const { Pool } = require("pg");
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");

let pool;

async function getPool() {
  if (pool) return pool;

  const client = new SecretsManagerClient({ region: "us-east-1" });
  const secret = await client.send(
    new GetSecretValueCommand({ SecretId: "prod/appdb/app_user" })
  );
  const creds = JSON.parse(secret.SecretString);

  pool = new Pool({
    host: creds.host,
    port: creds.port,
    user: creds.username,
    password: creds.password,
    database: creds.dbname,
    max: 10,
    ssl: { rejectUnauthorized: true },
  });

  return pool;
}

module.exports = { getPool };

The API layer stays deliberately small, a health check that confirms the database is reachable, and one route that reads from the table created back in Step 4:

// server.js
const express = require("express");
const { getPool } = require("./db");

const app = express();

app.get("/health", async (req, res) => {
  const pool = await getPool();
  await pool.query("SELECT 1");
  res.json({ status: "ok" });
});

app.get("/widgets", async (req, res) => {
  const pool = await getPool();
  const result = await pool.query("SELECT id, name, created_at FROM widgets ORDER BY created_at DESC LIMIT 50");
  res.json(result.rows);
});

app.listen(3000, () => console.log("Listening on port 3000"));

Run it locally against the bastion tunnel from Step 4, or deploy it to ECS or Lambda behind the RDS Proxy setup mentioned in the advanced tips below. Hit /health first. A response of {"status":"ok"} confirms the security group, subnet routing, and Secrets Manager permissions are all correctly wired before you debug anything more complex. This same pattern, pooled connections plus Secrets Manager, is exactly what production Node.js services running against RDS look like once the tutorial scaffolding comes off.

Amazon RDS vs Aurora vs Self-Hosted PostgreSQL

RDS isn’t the only way to run a relational database on AWS, and it’s worth being clear-eyed about where it sits between the fully managed, higher-cost option and the fully manual, lower-cost one.

FactorAmazon RDSAmazon AuroraSelf-Hosted on EC2
Setup effortLow (managed provisioning)Low (managed provisioning)High (you configure everything)
Patching & backupsAutomatedAutomatedManual, your responsibility
Failover modelMulti-AZ, ~60-120s typicalFaster, cluster-based replicationDepends entirely on your setup
Storage scalingManual or autoscaling up to a set maxAuto-scales in 10GB incrementsManual, limited by EBS volume
Cost baselineLower than Aurora at small scaleTypically higher per instance-hourLowest sticker price, highest labor cost
Root/OS accessNoNoFull control

Aurora generally wins on raw performance and failover speed for high-throughput production workloads, at a higher price per instance-hour, and AWS keeps shipping routine engine updates to back that up: Aurora MySQL 3.10.5 (compatible with MySQL 8.0.42) shipped July 30, 2026, and Aurora PostgreSQL 15.17.3 followed on July 20, 2026. We go deep on the actual benchmark numbers in our Aurora vs RDS comparison. Self-hosting on EC2 only makes sense if you need OS-level access RDS doesn’t expose, or you’re running an engine or extension RDS doesn’t support. For most teams building a standard application, RDS remains the default starting point, which lines up with why it still outranks Aurora on raw search volume by a wide margin.

5 Common Amazon RDS Pitfalls (and How to Avoid Them)

  • Leaving the instance publicly accessible. Setting Public access to “Yes” during a rushed setup, then forgetting to revisit it, is still one of the most common ways databases end up exposed. Keep it private and connect through a bastion, SSM, or VPN, as shown in Steps 2 and 4.
  • Running production on Single-AZ. A single unplanned failover or maintenance event becomes a full outage. Multi-AZ costs roughly double the compute but the SLA difference is the entire point of paying for a managed database in the first place.
  • Skipping the final snapshot on delete. --skip-final-snapshot is one flag away from permanently losing everything. Make final snapshots the default and only skip them for throwaway dev instances you’re certain you don’t need.
  • Ignoring storage autoscaling limits. RDS can grow storage automatically, but only up to a maximum you configure. Set that ceiling too low and the database hits a hard wall mid-traffic-spike instead of scaling through it.
  • Letting a major version age into Extended Support. Extended Support pricing applies automatically once a PostgreSQL or MySQL major version passes AWS’s standard support window, per AWS’s extended support documentation. Track your engine’s end-of-standard-support date and schedule the upgrade before it hits, not after the bill arrives.
  • Never testing failover until an actual outage forces it. Multi-AZ only helps if the application layer handles the brief connection drop gracefully. A connection pool with no retry logic can stay broken for minutes after the database itself has already recovered, and the only way to catch that is to trigger a failover on purpose during a quiet maintenance window.

Troubleshooting Amazon RDS: 9 Common Errors and Fixes

  1. “Could not connect to server: Connection timed out.” Almost always a security group rule blocking the port, or the instance sitting in a subnet your client can’t route to. Confirm the security group allows your source, and confirm public accessibility matches how you’re connecting.
  2. “FATAL: password authentication failed for user.” Check you’re not accidentally using an expired IAM auth token (15-minute lifespan) where a static password is expected, or vice versa. Regenerate the token or confirm the password in Secrets Manager matches what the app is sending.
  3. Instance stuck in “creating” for a long time. Normal for Multi-AZ instances and larger storage allocations, since AWS is provisioning and syncing a standby. Check describe-db-instances for status rather than assuming something’s broken.
  4. Modified settings not taking effect. Confirm whether you passed --apply-immediately. Without it, most changes wait for the next maintenance window, which can be days away.
  5. “PG::DiskFull” or storage-full errors. Either storage autoscaling is disabled or you’ve hit the configured maximum. Check current allocated storage against the max threshold and raise it, or find what’s consuming space (often bloated indexes or forgotten large tables).
  6. Sudden CPU spikes with no obvious cause. Pull up Performance Insights and sort by top SQL. A missing index or a query that regressed after a schema change is the usual culprit, not “the database needs to be bigger.”
  7. Failover takes longer than expected. A Multi-AZ DB instance typically fails over in roughly one to two minutes; if it’s consistently slower, check whether DNS caching on the client side is holding onto the old endpoint IP after failover.
  8. Read replica lag keeps growing. The replica’s instance class is usually undersized relative to write volume on the primary. Scale the replica up, or reduce the write load it needs to replay.
  9. Endpoint changed after restoring from a snapshot. Restoring a snapshot creates a brand-new DB instance with a new endpoint by design, it does not overwrite the original in place. Update your application configuration (ideally via Secrets Manager) to point at the new endpoint before cutting traffic over.

Advanced Tips for Running Amazon RDS in Production

Once the basic setup is solid, a handful of changes separate a database that merely works from one that holds up under real production load. Reserved DB instances are the easiest cost win for anything running 24/7, since committing to a 1- or 3-year term brings the effective hourly rate down substantially versus on-demand, worth doing as soon as a workload’s baseline size stabilizes.

Look at Blue/Green Deployments for major version upgrades instead of an in-place upgrade. AWS creates a full staging environment (“green”) kept in sync with production (“blue”), lets you validate the new version against real replicated data, then switches over with a short, controlled cutover instead of an unpredictable in-place migration window.

If your application runs on Lambda, put RDS Proxy in front of the database rather than letting every Lambda invocation open its own connection. Serverless functions scale by spinning up new execution environments, and each one opening a fresh database connection is a fast way to exhaust the connection limit on a small instance class. RDS Proxy pools and reuses connections underneath so the database sees a stable, small number of them. This pairs naturally with the pattern in our AWS Lambda API tutorial if you’re building a serverless backend against this database.

Finally, treat the Terraform configuration from Step 11 as the source of truth, not the console. Manual console changes drift out of sync with code fast, and the next terraform apply either silently reverts someone’s emergency fix or fails outright on a state mismatch. If you need an audit trail of who changed what in the AWS account itself, pair this setup with AWS CloudTrail so console-side changes don’t go unnoticed.

For disaster recovery beyond a single region, a cross-region read replica gives you a warm standby you can promote to a full read/write instance if an entire AWS region has a bad day, not just a single AZ. It’s a separate feature from the same-region read replica built in Step 7, with its own replication lag characteristics shaped by the distance between regions, and it’s worth adding once a workload is important enough that “the primary region is down” needs an actual answer rather than a shrug.

Test the failover you configured in Step 6 before you actually need it. Trigger a manual reboot with failover (aws rds reboot-db-instance --db-instance-identifier app-postgres-prod --force-failover) during a maintenance window and watch how long your application takes to recover its connections. A connection pool that doesn’t retry on a dropped connection will sit there failing requests long after the database itself is back, and that gap is far cheaper to find during a planned test than during a real outage.

Frequently Asked Questions About Amazon RDS

How much does Amazon RDS cost per month?

It depends entirely on instance class, storage, and whether Multi-AZ is on. A db.t4g.micro Single-AZ instance runs about $11.68 a month before storage in US East (N. Virginia). A production-grade db.r6g.xlarge with Multi-AZ runs closer to $992 a month for compute alone. Always check the official pricing page for current, region-specific rates.

Is the Amazon RDS free tier still available in 2026?

Yes. New AWS accounts get 750 hours a month on a db.t2.micro, db.t3.micro, or db.t4g.micro instance running MySQL, PostgreSQL, or MariaDB in Single-AZ, for 12 months.

What’s the real difference between RDS and Aurora?

RDS runs the standard, unmodified open-source or commercial engine you’d install anywhere. Aurora is AWS’s own storage and replication layer built to be compatible with MySQL and PostgreSQL wire protocols, generally faster and more resilient, at a higher baseline cost. See our full Aurora vs RDS comparison for benchmark-level detail.

Should I use a Multi-AZ DB instance or a Multi-AZ DB cluster?

Use a Multi-AZ DB instance for most workloads, it’s simpler and cheaper. Move to a Multi-AZ DB cluster when you need faster failover and want the standby capacity to serve read traffic instead of sitting idle, and you’re willing to pay for a third instance to get it.

How many read replicas can I create?

RDS supports multiple read replicas per source instance, and you can even chain replicas of replicas for some engines. Check the current per-engine limit in the AWS console when you provision, since exact caps vary by engine and can change.

Does Amazon RDS support IAM authentication instead of passwords?

Yes, for MySQL, PostgreSQL, and MariaDB. IAM database authentication issues short-lived tokens instead of long-lived passwords, which is worth enabling for any application role running on IAM-integrated compute like EC2, ECS, or Lambda, as covered in Step 9 above.

How long does it take to launch an RDS instance?

A basic Single-AZ instance is typically ready in several minutes. Multi-AZ instances, larger storage allocations, and Provisioned IOPS volumes all add time, since AWS is provisioning and syncing more infrastructure before marking the instance available.

Can I migrate an existing on-premises database into RDS?

Yes, AWS Database Migration Service (DMS) handles both one-time migrations and continuous replication from on-premises or other cloud databases into RDS, including minimal-downtime cutovers for production systems that can’t tolerate an extended maintenance window.

Wrapping Up: Your RDS Database Is Live

You now have a database that reflects how RDS actually gets used in production: private networking, credentials in Secrets Manager instead of source code, Multi-AZ failover, a read replica for reporting load, backups with real retention, IAM authentication as an option, and a Terraform file that can rebuild the entire thing in a new environment on demand. That’s a meaningfully different starting point than the single-click “Easy create” default, and it’s the difference that shows up the first time something actually goes wrong.

From here, the natural next steps are load-testing the instance class you picked before real traffic finds its limits for you, wiring up the alerting thresholds mentioned in Step 10, and deciding whether Aurora’s higher price tag is worth it once you know your actual query patterns.

Elias Virtanen

Elias Virtanen

Cybersecurity Analyst

Elias Virtanen is the Cybersecurity Analyst at Tech Insider, bringing hands-on expertise from his background in penetration testing and security consulting. He previously worked as a security researcher at F-Secure in Helsinki, where he focused on threat intelligence and vulnerability disclosure. Elias covers ransomware trends, zero-trust architecture, and the evolving regulatory landscape including NIS2 and the EU Cyber Resilience Act. He holds a CISSP certification and an MSc in Information Security from Aalto University.

View all articles