Skip to main content

Overview

RdsInstance provisions a managed PostgreSQL database instance with Multi-AZ deployment, storage encryption, automated backups, IAM database authentication, and optional RDS Proxy pooling and read replicas. Pick it over Aurora when you want predictable, fixed-size capacity and a lower floor price. Aurora suits variable or bursty workloads.

Add a database from the CLI

Four inputs are mandatory on fjall add database: Add --tier <name> to expand a preset into the emitted properties, where the name is tinkerer, lightweight, standard, resilient, or enterprise. On fjall add database the tier flag also requires --type, because each tier describes each database type differently.

Import the construct

Basic usage

instanceType takes the bare class.size form (t4g.medium, r7g.large), not the db.-prefixed form. The construct passes the value straight to new InstanceType(...), so a db. prefix produces an invalid instance class.

Configuration options

Core properties

The instance identifier is derived from the construct ID in kebab case, so ProductionDatabase becomes production-database. It is not settable through props.

Instance sizing

When Fjall provisions a database through an application tier, the instance type comes from the tier preset: t4g.micro (Tinkerer), t4g.small (Lightweight), t4g.large (Standard), r7g.large (Resilient), r7g.xlarge (Enterprise). Using the construct directly, set instanceType yourself.

Backup and maintenance

backupRetention must be a whole number of days. A fractional Duration throws at synth.

Security

Snapshot restore

Optional features

Two props are accepted by the TypeScript interface but never read by the construct: securityGroupIds and clusterIdentifier. Passing either changes nothing. Use database.connections or database.databaseSecurityGroup for network wiring, and rely on the derived instance identifier.

Ports and connectivity

The listener port comes from the engine family, so a PostgreSQL instance listens on 5432 unless you set port explicitly. The construct opens a self-referencing ingress rule on that port and exposes a Connections object with the same default. Grant access with the default port rather than a literal, so the wiring stays correct if the port or the proxy changes:
RDS Proxy accepts client connections only on the engine-default port, because the CreateDBProxy API has no port parameter. If you set a custom port and also enable proxy, direct database access uses your custom port while the proxy listens on the engine default. getHostPort() and database.connections then advertise the proxy port, and synth emits a warning naming both.

Security

Encryption

Storage encryption is always on. The default key is the AWS-managed RDS key. Pass encryption.storageKey to request a customer-managed key (CMK), which Fjall creates and retains beyond the stack so final snapshots stay restorable.
The Resilient and Enterprise tier presets request a CMK for both storage and Database Insights. Lower tiers use the AWS-managed key.

IAM database authentication

IAM authentication is on by default. It costs nothing and grants nothing until you call grantIamConnect, and password authentication keeps working alongside it.
The grant scopes rds-db:connect to the exact dbuser:<dbiResourceId>/<username> ARN. Calling it on an instance that opted out with iamAuthentication: false throws at synth.

Credentials

Fjall generates a Secrets Manager secret named <ConstructId>/credentials holding the master username and a generated password. Read it with getCredentials() and import individual fields into a container:
Multi-user secret rotation is opt-in. Pass a configuration object to switch it on, where {} accepts the 30-day default:

Network placement

Instances land in private subnets with egress. Setting publiclyAccessible: true moves them to public subnets, which is how the Tinkerer tier runs without a NAT gateway.

High availability

Multi-AZ is on by default. Set multiAz: false to opt out, which the Tinkerer and Lightweight presets do.
Multi-AZ gives you:
  • Automatic failover, typically in 1 to 2 minutes
  • Synchronous replication to the standby
  • Patching applied to the standby first
  • Higher durability

RDS Proxy

Pass a ProxyConfig object to add connection pooling. Fields: requireTLS, maxConnections, maxIdleConnections, connectionBorrowTimeout (seconds), vpcSubnets.
With a proxy enabled, getHostEndpoint() returns the proxy endpoint and database.connections targets the proxy security group. requireTLS defaults to true and the borrow timeout defaults to 120 seconds. The proxy name is the kebab-case construct ID plus -proxy.

Read replicas

Pass a ReadReplicaConfig object to add one replica. Fields: instanceType, availabilityZone. Pass {} to inherit the primary’s instance type.
The replica inherits the primary’s storage settings, Multi-AZ flag, monitoring interval, maintenance window, deletion protection, and IAM authentication setting. Its identifier is the kebab-case construct ID plus -read-replica. Replication is asynchronous, so replica reads lag the primary. A deletion waiter custom resource holds stack deletion until the primary reports available, because deleting a replica puts the primary into modifying and the final snapshot would otherwise fail.

Database Insights

Database Insights (formerly Performance Insights) is on in standard mode by default. Pass a config object to change the mode, or false to switch it off. Fields: mode ("standard" or "advanced"), encryptionKey.

Backups and restore

Automated backups run daily in the backup window and support point-in-time recovery. Transaction logs are captured continuously. Automated backups are retained when the instance is deleted, and the removal policy takes a final snapshot on stack deletion.

Restoring from a snapshot

The same restore configured from the CLI:
Constraints when restoring from a snapshot:
  • The master username is immutable and baked into the snapshot. snapshotUsername must match the original database’s username exactly. Omitting it falls back to credentials.username or the engine default and raises a synth warning.
  • The engine version is inherited from the snapshot. A mismatch between the snapshot’s engine version and the infrastructure code causes a CloudFormation failure.
  • Storage cannot be smaller than the snapshot’s allocated storage.
  • The master password is reset shortly after the restore. Fjall generates a new credentials secret and CloudFormation applies its password once the instance becomes available (the instance briefly reports resetting-master-credentials). The snapshot-era password stops working at that point, so applications must read credentials from the generated secret.
The password reset applies only to restores performed through infrastructure code (snapshotIdentifier). Restores performed through AWS Backup (fjall restore rds) run outside CloudFormation and keep the snapshot-era password. Reconcile the credentials, then run fjall rollout <app> so running containers pick up the new values. See fjall restore.

Methods

Two properties are also public: connections (a CDK Connections object) and databaseSecurityGroup.

Complete example

Cost optimisation

Instance sizing

*Rough on-demand estimates for us-east-1, single-AZ, storage excluded. Multi-AZ roughly doubles the instance charge. Check the AWS pricing page for current rates.

Ways to cut spend

  1. Use Graviton instance families (t4g, m7g, r7g) for better price-performance.
  2. Buy Reserved Instances for steady 1 to 3 year workloads.
  3. Right-size from CloudWatch metrics rather than from the tier default.
  4. Consider Aurora Serverless v2 for spiky or intermittent traffic.
  5. Start allocatedStorage small and let autoscaling raise it, rather than over-provisioning.
  6. Switch databaseInsights to false on non-production instances that nobody inspects.

Best practices

  1. Keep Multi-AZ on for production, which is the default.
  2. Use memory-optimised types (r7g) for production write workloads.
  3. Grant network access with allowDefaultPortFrom, not a hardcoded port.
  4. Read credentials from the generated secret, never from a checked-in value.
  5. Use grantIamConnect for application roles so no long-lived password is needed.
  6. Test a restore before you rely on backups.
  7. Wire alertsTopic so alarm notifications reach a real inbox.

Limitations

  • securityGroupIds and clusterIdentifier are accepted but ignored.
  • The read replica inherits almost everything from the primary. Only instanceType and availabilityZone are configurable.
  • RDS Proxy cannot listen on a custom port, so it always uses the engine default.
  • Deletion protection is on by default, so a stack destroy needs it turned off first.
  • The removal policy takes a final snapshot, so destroying a stack leaves a snapshot (and its CMK, when one was requested) behind.
  • Cross-region failover is not automatic. Use Global Aurora for that.
  • Read replicas lag the primary because replication is asynchronous.
  • Maintenance windows cause brief downtime on single-AZ instances.

Next Steps

Database Factory

Provision databases through the Fjall factory API.

RDS Aurora

Scale to an Aurora cluster with serverless and global options.

RDS Free Tier

Run a free-tier-eligible PostgreSQL instance during your first 12 months.

Secrets Manager

Manage database credentials and rotation.