Skip to main content

Overview

The RDS Free Tier resource provides a cost-effective PostgreSQL database instance that qualifies for the AWS Free Tier program. It’s optimized for development, testing, and learning environments with minimal configuration and lower resource allocation. Perfect for new AWS accounts, proof-of-concepts, and non-production workloads. RDS Free Tier is ideal for:
  • Development and testing environments
  • Learning and experimentation with PostgreSQL
  • Proof-of-concept projects
  • Demo applications and tutorials
  • Personal projects and side applications
  • New AWS accounts utilizing free tier benefits
Key features:
  • AWS Free Tier eligible (750 hours/month for 12 months)
  • PostgreSQL 17.5 latest engine version
  • db.t3.micro instance (1 vCPU, 1 GB RAM)
  • Single-AZ deployment for cost efficiency
  • 20 GB storage included in free tier
  • Customer-managed KMS encryption for security
  • Secrets Manager integration for credential management
  • 7-day backup retention default

AWS Free Tier Eligibility

Free Tier includes:
  • 750 hours per month of db.t3.micro usage (first 12 months)
  • 20 GB of General Purpose (SSD) storage
  • 20 GB of backup storage
  • No charge for data transfer within same region
After Free Tier expires:
  • db.t3.micro: ~0.017/hour( 0.017/hour (~12/month)
  • Storage: 0.115/GBmonthfor20GB= 0.115/GB-month for 20 GB = ~2.30/month
  • Total: ~$15/month after free tier period

Resource Class

import { RdsFreeTier } from "@fjall/components-infrastructure/lib/resources/aws/database/rdsFreeTier";

Basic Usage

import { Vpc } from "@fjall/components-infrastructure/lib/resources/aws/networking/vpc";

const vpc = new Vpc(this, "Vpc");

const database = new RdsFreeTier(this, "Database", {
  vpc: vpc,
  databaseName: "myapp"
});

Configuration Options

Core Properties

PropertyTypeDescriptionDefault
vpcIVpcVPC for database deployment (required)-
databaseNamestringDatabase name"postgres"
engineIInstanceEnginePostgreSQL engine versionPostgreSQL 17.5
instanceTypeInstanceTypeInstance size (must be free tier eligible)t3.micro
portnumberDatabase port5432

Storage Configuration

PropertyTypeDescriptionDefault
allocatedStoragenumberStorage in GB (20 GB free)20

Backup Configuration

PropertyTypeDescriptionDefault
backupRetentionDurationBackup retention periodDuration.days(7)

Monitoring Configuration

PropertyTypeDescriptionDefault
monitoringIntervalDurationEnhanced monitoring intervalDuration.minutes(1)

Default Configuration

The RDS Free Tier construct includes these defaults:
  • PostgreSQL 17.5 latest stable version
  • db.t3.micro instance (1 vCPU, 1 GB RAM)
  • Single-AZ deployment (no Multi-AZ)
  • 20 GB storage (free tier limit)
  • No storage auto-scaling (cost control)
  • Customer-managed KMS encryption for storage
  • Secrets Manager integration for credentials
  • 7-day backup retention
  • Enhanced monitoring every 1 minute
  • No RDS Proxy (not needed for low traffic)
  • No read replicas (free tier focused)
  • No Performance Insights (cost optimization)

Usage Patterns

Pattern 1: Basic Development Database

const devDatabase = new RdsFreeTier(this, "DevDatabase", {
  vpc: devVpc,
  databaseName: "development"
});

// Free tier eligible configuration
// Perfect for daily development work
// 750 hours/month = full month coverage

Pattern 2: Learning and Tutorials

const learningDb = new RdsFreeTier(this, "LearningDatabase", {
  vpc: vpc,
  databaseName: "tutorial",
  allocatedStorage: 20  // Stay within free tier
});

// Cost-free PostgreSQL experience
// Learn database administration
// Experiment with queries and schemas

Pattern 3: Proof of Concept

const pocDatabase = new RdsFreeTier(this, "POCDatabase", {
  vpc: vpc,
  databaseName: "poc",
  backupRetention: Duration.days(1)  // Minimal backups for POC
});

// Quick setup for demos
// No production requirements
// Easy to tear down

Pattern 4: Side Project

const sideProjectDb = new RdsFreeTier(this, "SideProjectDatabase", {
  vpc: vpc,
  databaseName: "myproject",
  backupRetention: Duration.days(7)
});

// Personal projects
// Hobby applications
// Portfolio demos

Integration Examples

With Lambda Functions

import { Function } from "aws-cdk-lib/aws-lambda";

const database = new RdsFreeTier(this, "Database", {
  vpc: vpc,
  databaseName: "app"
});

const lambda = new Function(this, "Handler", {
  vpc: vpc,
  environment: {
    DB_HOST: database.getHostEndpoint(),
    DB_PORT: database.getHostPort().toString(),
    DB_NAME: "app",
    DB_SECRET_ARN: database.getCredentials().secretArn
  }
});

database.getCredentials().grantRead(lambda);
database.connections.allowFrom(
  lambda,
  ec2.Port.tcp(Number(database.getHostPort()))
);

// Lambda + RDS Free Tier = fully serverless free tier stack

With ECS Services

import { EcsCluster } from "@fjall/components-infrastructure/lib/resources/aws/compute/ecsCluster";

const database = new RdsFreeTier(this, "Database", {
  vpc: vpc,
  databaseName: "app"
});

const cluster = new EcsCluster(this, "DevCluster", {
  vpc: vpc,
  serviceName: "dev-api"
});

database.connections.allowFrom(
  cluster,
  ec2.Port.tcp(Number(database.getHostPort()))
);

// Development container environment
// Connect via standard PostgreSQL drivers

With EC2 Development Instance

import * as ec2 from "aws-cdk-lib/aws-ec2";

const database = new RdsFreeTier(this, "Database", {
  vpc: vpc,
  databaseName: "dev"
});

const devInstance = new ec2.Instance(this, "DevBox", {
  vpc: vpc,
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.T3,
    ec2.InstanceSize.MICRO  // Also free tier!
  ),
  machineImage: ec2.MachineImage.latestAmazonLinux2()
});

database.connections.allowFrom(
  devInstance,
  ec2.Port.tcp(Number(database.getHostPort()))
);

// Complete free tier development environment

Security Configuration

Network Isolation

// Deploy in private subnets
const database = new RdsFreeTier(this, "SecureDatabase", {
  vpc: vpc,
  databaseName: "secure",
  vpcSubnets: {
    subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS
  }
});

// Access only from within VPC

Access Credentials

// Programmatic credential access
const secret = database.getCredentials();

// Application usage (Node.js)
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();

const secret = await secretsManager.getSecretValue({
  SecretId: process.env.DB_SECRET_ARN
}).promise();

const credentials = JSON.parse(secret.SecretString);
// credentials.username (always "postgres")
// credentials.password (auto-rotated)
// credentials.host
// credentials.port

Encryption at Rest

// Automatic encryption with customer-managed KMS key
const database = new RdsFreeTier(this, "EncryptedDatabase", {
  vpc: vpc,
  databaseName: "encrypted"
});

// Creates 1 KMS key for storage encryption
// KMS key cost: $1/month (outside free tier)

Limitations and Constraints

Free Tier Limits

// DO NOT exceed these to stay in free tier:
const database = new RdsFreeTier(this, "Database", {
  vpc: vpc,
  databaseName: "app",
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.BURSTABLE3,
    ec2.InstanceSize.MICRO  // MUST be micro for free tier
  ),
  allocatedStorage: 20  // Maximum 20 GB free
});

// Exceeding limits triggers charges:
// - t3.small or larger = immediate charges
// - > 20 GB storage = $0.115/GB-month overage
// - Multi-AZ = 2x instance hours (exceeds 750/month)

Performance Expectations

// t3.micro specifications:
// - 1 vCPU (burstable)
// - 1 GB RAM
// - Moderate network performance
// - Suitable for:
//   - < 100 concurrent connections
//   - Light read/write operations
//   - Small datasets (< 10 GB)
//   - Development workloads

// NOT suitable for:
// - Production workloads
// - High traffic applications
// - Large datasets
// - Complex queries on large tables

Single-AZ Only

// Free tier is single-AZ only
const database = new RdsFreeTier(this, "Database", {
  vpc: vpc,
  databaseName: "app"
  // multiAz: false (implicitly, cannot be enabled)
});

// Implications:
// - No automatic failover
// - Downtime during maintenance
// - AZ failure = database unavailable
// - Suitable for dev/test only

Cost Management

Staying Within Free Tier

// Optimal free tier configuration
const freeDatabase = new RdsFreeTier(this, "FreeDatabase", {
  vpc: vpc,
  databaseName: "free",
  allocatedStorage: 20,              // Max free storage
  backupRetention: Duration.days(7)  // Free up to DB size
});

// Free tier costs:
// - Database: $0/month (first 12 months)
// - KMS key: $1/month (not covered)
// - Secrets Manager: ~$0.40/month (not covered)
// - Total: ~$1.40/month

After Free Tier Expires

// Post-free-tier costs (per month):
// - t3.micro instance: ~$12
// - 20 GB storage: ~$2.30
// - Backups (< 20 GB): $0
// - KMS key: $1
// - Secrets Manager: ~$0.40
// - Total: ~$16/month

// Upgrade path to RdsInstance
// when free tier expires or needs more resources

Cost Monitoring

// Set billing alarm
import * as cloudwatch from "aws-cdk-lib/aws-cloudwatch";

const costAlarm = new cloudwatch.Alarm(this, "CostAlarm", {
  metric: new cloudwatch.Metric({
    namespace: "AWS/Billing",
    metricName: "EstimatedCharges",
    statistic: "Maximum",
    dimensionsMap: {
      Currency: "USD"
    }
  }),
  threshold: 10,  // Alert at $10
  evaluationPeriods: 1,
  alarmDescription: "Alert when estimated charges exceed $10"
});

Upgrade Path

Transitioning to RdsInstance

// When outgrowing free tier
import { RdsInstance } from "@fjall/components-infrastructure/lib/resources/aws/database/rdsInstance";

// Step 1: Create snapshot of free tier database
// (via AWS Console or CLI)

// Step 2: Create larger instance
const productionDb = new RdsInstance(this, "ProductionDatabase", {
  vpc: vpc,
  databaseName: "production",
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.T4G,
    ec2.InstanceSize.MEDIUM
  ),
  multiAz: true,
  allocatedStorage: 100
});

// Step 3: Restore snapshot to new instance
// Step 4: Update application connection strings
// Step 5: Decommission free tier instance

When to Upgrade

Upgrade from Free Tier when:
  • Free tier eligibility expires (12 months)
  • Need more than 1 GB RAM
  • Dataset exceeds 15 GB
  • Require Multi-AZ for high availability
  • Need better performance (CPU burst limits)
  • Moving to production

Methods

getHostEndpoint()

const endpoint = database.getHostEndpoint();
// Returns: Database instance endpoint
// Format: instance-id.region.rds.amazonaws.com

getHostPort()

const port = database.getHostPort();
// Returns: string - Database port (default "5432")
// Note: Returns string type, not number

getCredentials()

const credentials = database.getCredentials();
// Returns: ISecret reference to Secrets Manager
// Contains: username, password, host, port, dbname

credentials.grantRead(lambda);

Complete Example

import { Stack, StackProps, CfnOutput, Duration } from "aws-cdk-lib";
import { Construct } from "constructs";
import { Vpc } from "@fjall/components-infrastructure/lib/resources/aws/networking/vpc";
import { RdsFreeTier } from "@fjall/components-infrastructure/lib/resources/aws/database/rdsFreeTier";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as ec2 from "aws-cdk-lib/aws-ec2";

export class FreeTierStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // Network setup
    const vpc = new Vpc(this, "DevVpc", {
      maxAzs: 2,
      natGateways: 1  // Cost optimization
    });

    // Free tier database
    const database = new RdsFreeTier(this, "DevDatabase", {
      vpc: vpc,
      databaseName: "development",
      allocatedStorage: 20,  // Maximum free tier
      backupRetention: Duration.days(7)
    });

    // Lambda function (also free tier eligible)
    const api = new lambda.Function(this, "API", {
      runtime: lambda.Runtime.NODEJS_20_X,
      handler: "index.handler",
      code: lambda.Code.fromAsset("lambda"),
      vpc: vpc,
      memorySize: 512,  // Stay in Lambda free tier
      environment: {
        DB_HOST: database.getHostEndpoint(),
        DB_PORT: database.getHostPort().toString(),
        DB_NAME: "development",
        DB_SECRET_ARN: database.getCredentials().secretArn,
        NODE_ENV: "development"
      }
    });

    // Grant database access
    database.getCredentials().grantRead(api);
    database.connections.allowFrom(
      api,
      ec2.Port.tcp(5432),
      "Allow Lambda to connect"
    );

    // Development EC2 instance (optional)
    const devBox = new ec2.Instance(this, "DevBox", {
      vpc: vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T3,
        ec2.InstanceSize.MICRO  // Free tier
      ),
      machineImage: ec2.MachineImage.latestAmazonLinux2023(),
      vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }
    });

    database.connections.allowFrom(devBox, ec2.Port.tcp(Number(database.getHostPort())));

    // Outputs
    new CfnOutput(this, "DatabaseEndpoint", {
      value: database.getHostEndpoint(),
      description: "Database endpoint for connections"
    });

    new CfnOutput(this, "SecretArn", {
      value: database.getCredentials().secretArn,
      description: "Database credentials in Secrets Manager"
    });

    new CfnOutput(this, "ConnectionString", {
      value: `postgresql://postgres:<password>@${database.getHostEndpoint()}:5432/development`,
      description: "Connection string template"
    });

    new CfnOutput(this, "FreeTierNote", {
      value: "This stack uses free tier resources. Monitor usage at: https://console.aws.amazon.com/billing/home#/freetier",
      description: "Free tier monitoring"
    });
  }
}

Best Practices

  1. Monitor free tier usage - check AWS Console billing dashboard regularly
  2. Set billing alarms - alert when charges exceed expected amounts
  3. Use for dev/test only - not suitable for production workloads
  4. Keep storage under 20 GB - avoid overage charges
  5. Implement connection pooling - 1 GB RAM limits connections
  6. Use appropriate instance type - only t3.micro is free tier eligible
  7. Plan upgrade path - know when to transition to RdsInstance
  8. Optimize queries - limited CPU and memory require efficiency
  9. Clean up unused resources - avoid charges after free tier expires
  10. Understand limitations - single-AZ, no high availability

Common Patterns

Development Environment

const devEnv = new RdsFreeTier(this, "DevDB", {
  vpc: devVpc,
  databaseName: "dev"
});

// Local development connects via VPN
// CI/CD connects from VPC
// Teardown/recreate as needed

Learning PostgreSQL

const learningDb = new RdsFreeTier(this, "LearningDB", {
  vpc: vpc,
  databaseName: "learn",
  backupRetention: Duration.days(1)
});

// Practice SQL queries
// Learn database administration
// Experiment with schemas and data types

Multi-Tenant Development

// Single free tier instance, multiple databases
const sharedDb = new RdsFreeTier(this, "SharedDevDB", {
  vpc: vpc,
  databaseName: "postgres"  // Default database
});

// Create per-developer databases via SQL:
// CREATE DATABASE dev_alice;
// CREATE DATABASE dev_bob;
// CREATE DATABASE dev_charlie;

Cost Considerations

ComponentFree TierAfter Free TierNotes
db.t3.micro750 hours/month~$12/monthFull month coverage
Storage (20 GB)Included~$2.30/monthgp3 pricing
BackupsIncludedIncludedUp to DB size
KMS KeyNot covered$1/monthRequired for encryption
Secrets ManagerNot covered~$0.40/monthCredential storage
Total~$1.40/month~$16/monthFirst 12 months vs after
Free Tier Eligibility:
  • New AWS accounts only
  • 12 months from account creation
  • 750 hours/month = continuous operation
  • 20 GB storage + 20 GB backups

Troubleshooting

Common Issues

  1. Free tier charges appearing
    • Cause: Instance type not t3.micro, storage > 20 GB, or Multi-AZ enabled
    • Solution: Verify instance type, reduce storage, ensure single-AZ
  2. Connection timeouts
    • Cause: Security group misconfigured, database in isolated subnet without NAT
    • Solution: Check security group rules, verify subnet routing
  3. Out of memory errors
    • Cause: 1 GB RAM insufficient for workload
    • Solution: Optimize queries, reduce connections, upgrade to larger instance
  4. Storage full
    • Cause: 20 GB limit reached, no auto-scaling
    • Solution: Clean up data, or upgrade to RdsInstance with more storage
  5. Slow performance
    • Cause: t3.micro CPU burst credits exhausted
    • Solution: Optimize queries, reduce load, or upgrade instance type

Debug Commands

# Check instance details
aws rds describe-db-instances \
  --db-instance-identifier <instance-id>

# Verify free tier eligibility
aws rds describe-db-instances \
  --db-instance-identifier <instance-id> \
  --query 'DBInstances[0].[DBInstanceClass,AllocatedStorage,MultiAZ]'

# Monitor free tier usage
aws ce get-cost-and-usage \
  --time-period Start=2025-01-01,End=2025-01-31 \
  --granularity MONTHLY \
  --metrics UnblendedCost \
  --filter file://filter.json

# Test connection
psql -h <endpoint> -p 5432 -U postgres -d development

SQL Diagnostics

-- Check database size
SELECT pg_size_pretty(pg_database_size('development'));

-- Monitor connection count (max ~30-40 for t3.micro)
SELECT count(*) FROM pg_stat_activity;

-- Check memory usage
SELECT sum(pg_total_relation_size(schemaname||'.'||tablename))::bigint
FROM pg_tables;

-- Identify large tables
SELECT schemaname, tablename,
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 10;

See Also