Stacktape
Stacktape


Relational (SQL) Databases



Stacktape allows you to deploy a fully managed relational (SQL) database, with support for PostgreSQL, MySQL, MariaDB, Oracle, and SQL Server. The service handles capacity scaling, hardware provisioning, database setup, patching, logging, and backups, so you can focus on your application.

Under the hood, Stacktape uses Amazon RDS. It supports three engine categories with different topologies and scaling behaviors:

  • Basic RDS engines: A single-node database that is the most cost-effective option. It supports optional read replicas for higher performance and a Multi-AZ standby instance for resilience.
  • Aurora engine: A multi-node, highly available cluster with increased durability and fault tolerance. It automatically balances read requests across nodes for better performance.
  • Aurora Serverless engine: Similar to the Aurora engine, but with automatic, usage-based scaling. It can scale down to zero when inactive, saving costs.

Databases are always deployed in a private network within your stack's VPC. You can configure accessibility to control whether the database is accessible from the internet or only from other resources in the VPC.

Basic usage

To connect to a relational database, you typically use a connection string. Stacktape can automatically inject the necessary credentials and connection details into your application's environment when you use the connectTo property.

resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('database.password')
engine:
type: postgres
properties:
version: '16.2'
primaryInstance:
instanceSize: db.t3.micro
apiServer:
type: multi-container-workload
properties:
resources:
cpu: 1
memory: 1024
containers:
- name: api-container
packaging:
type: stacktape-image-buildpack
properties:
entryfilePath: src/main.ts
connectTo:
- myDatabase

A single-node PostgreSQL database.

import express from 'express';
import { Pool } from 'pg';
const pgPool = new Pool({
connectionString: process.env.STP_MY_DATABASE_CONNECTION_STRING // env variable was automatically injected by Stacktape
});
const app = express();
app.get('/time', async (req, res) => {
const result = await pgPool.query('SELECT NOW()');
const time = result.rows[0];
res.send(time);
});
app.listen(3000, () => {
console.info('Server running on port 3000');
});

A container workload connecting to the database.

Database credentials

You can configure the credentials for the database's master user. It's recommended to use a secret to manage these credentials securely.

resources:
myRelationalDatabase:
type: relational-database
properties:
credentials:
masterUserName: $File('.env').DB_USER_NAME # OPTIONAL
masterUserPassword: $Secret('dbCredentials.password')
engine:
type: postgres
properties:
version: '16.2'
port: 5432
primaryInstance:
instanceSize: db.t2.micro
RelationalDatabaseCredentials  API reference
masterUserPassword
Required
masterUserName
Default: db_master_user

Engine

The database engine determines the database type, performance, availability, and pricing.

Depending on the properties they provide, we group engine types into following groups:

  • RDS Engine group - single node fully managed databases.
    • Supported engines:
      • postgres
      • mysql
      • mariadb
      • oracle-ee
      • oracle-se2
      • sqlserver-ee
      • sqlserver-ex
      • sqlserver-se
      • sqlserver-web
  • Aurora Engine group - multi node highly available cluster with replicated storage.
    • Supported engines:
      • aurora-postgresql
      • aurora-mysql
  • Aurora Serverless V2 Engine - serverless engine with replicated storage. Compute resources scale based on actual demand.
    • Supported engines:
      • aurora-postgresql-serverless-v2
      • aurora-mysql-serverless-v2
  • Aurora Serverless V1 Engine - old generation serverless engine. We recommend using V2 instead.
    • Supported engines:
      • aurora-postgresql-serverless
      • aurora-mysql-serverless

Engine version

You must choose a version that is compatible with your chosen engine.

resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: postgres
properties:
version: '16.2'
port: 5432
primaryInstance:
instanceSize: db.t3.micro

Available versions

  • aurora-mysql

  • aurora-mysql-serverless-v2

  • aurora-postgresql

  • aurora-postgresql-serverless-v2

  • mariadb

  • mysql

  • oracle-ee

  • oracle-se2

  • postgres

  • sqlserver-ee

  • sqlserver-ex

  • sqlserver-se

  • sqlserver-web

RDS engine

The RDS engine is the default, single-node, and most cost-effective option. While it's not highly available by default, you can configure a standby instance in a different Availability Zone or add read replicas to improve performance and resilience.

RdsEngine  API reference
type
Required
properties.primaryInstance
Required
properties.version
Required
properties.dbName
properties.port
properties.storage
properties.readReplicas
properties.disableAutoMinorVersionUpgrade

Instance size

You can configure the instance size for both the primary instance and its read replicas.

  • Allows you to choose the database instance size. Each instance size offers different combination of CPU, memory, storage, and networking capacity.
  • To see a list of available instances, refer to AWS docs
  • Not every instance size is supported for every database engine, version or region.
  • Be aware that some instance families (such as t3 or t4) are mostly meant for testing and not production workloads
  • Refer to AWS Docs for detailed breakdown of supported combinations.
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: postgres
properties:
version: '16.2'
port: 5432
primaryInstance:
instanceSize: db.t3.micro

Multi-AZ mode

You can enable Multi-AZ mode for both primary instances and read replicas.

  • When enabled, the data is replicated to a standby instance in a different AZ (Availability Zone).
  • If the default (primary) instance fails, the failover to the standby instance in another AZ is performed.
  • The failover to the standby instance is synchronous (highly durable).
  • The standby instance can not be directly accessed (doesn't have its own database endpoint).
  • In contrast to using replicas, standby instance can fully take-over the responsibilities of the primary instance, while replicas can only be used for read operations.
  • Reduces the impact of maintenance. The maintenance is performed on the standby instance first, promotes the standby instance to a primary instance, and then performs maintenance on the old primary instance which is now a standby replica.
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: postgres
properties:
version: '16.2'
port: 5432
primaryInstance:
instanceSize: db.t2.micro
multiAz: true
RdsEnginePrimaryInstance  API reference
instanceSize
Required
multiAz

Read replicas

  • Read replicas help to decrease the load on the primary instance by serving a read-only database requests.
  • Replication uses database's native asynchronous replication to update the read replicas whenever there is a change to the primary instance.
  • Each read replica has its own database endpoint.
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: postgres
properties:
version: '16.2'
port: 5432
primaryInstance:
instanceSize: db.t3.micro
readReplicas:
- instanceSize: db.t3.micro
- instanceSize: db.t3.micro
RdsEngineReadReplica  API reference
instanceSize
Required
multiAz

Storage

  • When you run out of free database space, your storage will automatically scale up.
  • By default, scales between 20GB and 200GB.
  • The scaling process happens when the following conditions are met:
    • Free available space` is less than 10 percent of the allocated storage
    • The low-storage condition lasts at least five minutes.
    • At least six hours have passed since the last storage modification.
  • To learn more about storage autoscaling, refer to AWS Docs
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: postgres
properties:
version: '16.2'
port: 5432
primaryInstance:
instanceSize: db.t3.micro
storage:
initialSize: 40
maxSize: 400
RdsEngineStorage  API reference
initialSize
Default: 20
maxSize
Default: 200

Aurora engine

The Aurora engine is a fully managed, AWS-developed database engine that offers clustering, high availability, and increased performance. It replicates storage six ways across three Availability Zones and automatically load-balances read operations between nodes. If a primary instance fails, a read replica is automatically promoted to take its place.

resources:
auroraSlsPostgres:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbSecret.password')
engine:
type: aurora-postgresql
properties:
version: '16.2'
instances:
- instanceSize: db.t3.medium
port: 5432
AuroraEngine  API reference
type
Required
properties.instances
Required
properties.version
Required
properties.dbName
properties.port
properties.disableAutoMinorVersionUpgrade
AuroraEngineInstance  API reference
instanceSize
Required

Aurora Serverless V2 engine

The Aurora Serverless V2 engine is similar to the standard Aurora engine but adds automatic, usage-based scaling. It's more responsive and less disruptive when scaling than the V1 engine. The database can scale down to zero, so you don't pay for compute capacity when it's not in use.

Scaling is measured in Aurora Capacity Units (ACUs), where each ACU provides approximately 2GB of RAM and one virtual CPU.

AuroraServerlessV2Engine  API reference
type
Required
properties.version
Required
properties.dbName
properties.minCapacity
properties.maxCapacity
Default: 10
properties.disableAutoMinorVersionUpgrade
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbSecret.password')
engine:
type: aurora-postgresql-serverless-v2
properties:
version: '16.4'
maxCapacity: 8

Aurora Serverless V1 engine

We recommend using the newer Aurora Serverless V2 engine instead of this one.

The Aurora Serverless V1 engine is the previous generation of the serverless Aurora engine. It also provides automatic, usage-based scaling and can scale to zero, but it is less responsive than the V2 engine.

AuroraServerlessEngine  API reference
type
Required
properties.version
properties.dbName
properties.minCapacity
Default: 2
properties.maxCapacity
Default: 4
properties.pauseAfterSeconds
properties.disableAutoMinorVersionUpgrade
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbSecret.password')
engine:
type: aurora-postgresql-serverless
properties:
version: '13.12'
minCapacity: 4
maxCapacity: 8
pauseAfterSeconds: 600

Backups

  • Databases are automatically backed up once a day.
  • Maximum retention period is 35 days.
  • You can disable automated backups by setting the value to 0 (works only for RDS engines).
  • You can also take manual backup snapshots (in the console or using the API). The retention is not applied to manual backups.
  • By default, backups are retained for 1 day.
  • To learn more about RDS engine backups, refer to RDS engine backups AWS Docs.
  • To learn more about Aurora engine backups, refer to Aurora engine backups AWS Docs.
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: my_secret_password
engine:
type: postgres
properties:
version: '16.2'
primaryInstance:
instanceSize: db.t3.micro
automatedBackupRetentionDays: 5

Logging

  • By default, logging is enabled and logs are preserved for 90 days.
  • Logged data depend on the used engine. You can log information about connections, disconnections, executed queries & much more.
RelationalDatabaseLogging  API reference
disabled
retentionDays
Default: 90
logTypes
engineSpecificOptions
logForwarding
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: my_secret_password
engine:
type: postgres
properties:
version: '16.2'
primaryInstance:
instanceSize: db.t3.micro
logging:
retentionDays: 30
engineSpecificOptions:
log_connections: true

Forwarding logs

You can forward logs to third-party services. See Forwarding Logs for more information.

Closing zombie connections

Connections from resources that are no longer running (like stopped containers) can become "zombie" connections. In modern, ephemeral architectures, it's important to have a strategy for handling them.

From container workloads

When connecting from a container, you should close the connection before the container exits. You can do this by listening for the SIGTERM signal.

const connectionPool = createConnectionPool();
// remember to close the connection even on errors
process
.on('uncaughtException', () => {
connectionPool.close();
process.exit(1);
})
.on('unhandledRejection', () => {
connectionPool.close();
process.exit(1);
});
process.on('SIGTERM', () => {
connectionPool.close();
process.exit(0);
});

From batch jobs

Similarly, for batch jobs, you should close the connection before the job finishes.

const connectionPool = createConnectionPool();
connectionPool.connect();
// remember to close the connection even on errors
process
.on('uncaughtException', () => {
connectionPool.close();
process.exit(1);
})
.on('unhandledRejection', () => {
connectionPool.close();
process.exit(1);
});
doSomethingWithYourConnection();
connectionPool.close();

From Lambda functions

For Lambda functions, you have two options:

1. Initialize and close the connection inside the handler.

This prevents zombie connections but can be slow, as creating a new connection for each invocation can add significant latency.

import { Client } from 'pg';
const handler = async (event, context) => {
const pgClient = new Client({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT
});
await pgClient.connect();
const result = await pgClient.query('SELECT NOW()');
const time = result.rows[0];
await pgClient.end();
return { result: time };
};
export default handler;

2. Initialize the connection outside the handler.

This reuses the connection across invocations, which is more performant. However, it can lead to zombie connections because you can't hook into the Lambda container's shutdown process. To mitigate this, you should:

  • Lower your database's connection timeout settings.
  • Add logic to your application to re-establish the connection if it's been closed by the database.
import { Client } from 'pg';
const pgClient = new Client({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT
});
(async () => {
await pgClient.connect();
})();
const handler = async (event, context) => {
const result = await pgClient.query('SELECT NOW()');
const time = result.rows[0];
return { result: time };
};
export default handler;

Accessibility

You can control which resources can access your database. In addition to requiring user credentials, you can restrict access based on the network.

DatabaseAccessibility  API reference
accessibilityMode
Default: internetRequired
forceDisablePublicIp
whitelistedIps

Internet mode

This is the default and least restrictive mode. The database can be accessed from anywhere on the internet.

VPC mode

The database can only be accessed from resources within the default VPC, such as functions, batch jobs, and container workloads. You can also whitelist specific IP addresses to allow access from the internet.

resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: aurora-postgresql
properties:
version: '16.2'
instances:
- instanceSize: db.t3.medium
port: 5432
accessibility:
accessibilityMode: vpc
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my/function.ts
joinDefaultVpc: true

Scoping workloads in VPC mode

This mode is similar to VPC mode but more restrictive. In addition to being in the same VPC, a resource must explicitly list the database in its connectTo property to gain access.

resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: aurora-postgresql
properties:
version: '16.2'
instances:
- instanceSize: db.t3.medium
port: 5432
accessibility:
accessibilityMode: scoping-workloads-in-vpc
myFunction:
type: function
properties:
packaging:
type: stacktape-lambda-buildpack
properties:
entryfilePath: path/to/my/function.ts
joinDefaultVpc: true
connectTo:
- myDatabase

Whitelisted IPs only mode

The database can only be accessed from the IP addresses and CIDR blocks in the whitelistedIps list.

resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: $Secret('dbPassword')
engine:
type: aurora-postgresql
properties:
version: '16.2'
instances:
- instanceSize: db.t3.medium
port: 5432
accessibility:
accessibilityMode: whitelisted-ips-only
whitelistedIps:
- '147.25.33.12'

Maintenance window

  • The maintenance window is the time period during which the database is available for maintenance.
  • During this time, your database instance can be updated with modifications like OS patching or database engine version upgrades.
  • The database might be unavailable for a short period of time during maintenance. To avoid service interruptions, you should use multiAz deployment for RDS engines or use an Aurora engine.
  • The maintenance window is specified in UTC.
  • The maintenance window is specified in the format of day:time-day:time. Example: Sun:02:00-Sun:04:00
  • By default, the maintenance window is set to Sunday 02:00 - Sunday 04:00 adjusted to the region's timezone (but does not account for daylight savings time). Examples:
    • In us-east-1 the maintenance window is set to Sunday 07:00 - Sunday 09:00 UTC which translates to Sunday 02:00 - Sunday 04:00 in the region's timezone
    • In eu-west-1 the maintenance window is set to Sunday 01:00 - Sunday 03:00 UTC which translates to Sunday 02:00 - Sunday 04:00 in the region's timezone
resources:
myDatabase:
type: relational-database
properties:
credentials:
masterUserPassword: my_secret_password
engine:
type: postgres
properties:
version: '16.2'
primaryInstance:
instanceSize: db.t3.micro
preferredMaintenanceWindow: Sun:04:00-Sun:05:00

Referenceable parameters

The following parameters can be easily referenced using $ResourceParam directive directive.

To learn more about referencing parameters, refer to referencing parameters.

connectionString
  • Fully-formed connection string that can be used to access the primary instance. For aurora databases, this is connectionString to cluster endpoint, which can be used for both reads and writes. Includes host, port, username, password and dbName.

  • Usage: $ResourceParam('<<resource-name>>', 'connectionString')
jdbcConnectionString
  • Fully-formed connection string in JDBC form that can be used to access the primary instance. Includes host, port, username, password and dbName.

  • Usage: $ResourceParam('<<resource-name>>', 'jdbcConnectionString')
host
  • Hostname (address) of the primary instance that can be used for both reads and writes. For aurora databases, this is hostname of a cluster endpoint, which can be used for both reads and writes.

  • Usage: $ResourceParam('<<resource-name>>', 'host')
port
  • Port of the database.

  • Usage: $ResourceParam('<<resource-name>>', 'port')
dbName
  • Name of the automatically created database (can be configured using the dbName property).

  • Usage: $ResourceParam('<<resource-name>>', 'dbName')
readerHost
  • Hostname (address) used for reads only. (only available for aurora-postgresql and aurora-mysql engines). If you have multiple instances, it is advised to use readerHost for reads to offload the primary (read/write) host. ReaderHost automatically balances requests between available instances. Connections are auto-balanced among available reader hosts.

  • Usage: $ResourceParam('<<resource-name>>', 'readerHost')
readerConnectionString
  • Same as connectionString but targets readerHosts (only available for aurora-postgresql and aurora-mysql engines). Connections are auto-balanced among available reader hosts.

  • Usage: $ResourceParam('<<resource-name>>', 'readerConnectionString')
readerJdbcConnectionString
  • Same as readerConnectionString but in JDBC format (only available for aurora-postgresql and aurora-mysql engines).

  • Usage: $ResourceParam('<<resource-name>>', 'readerJdbcConnectionString')
readReplicaHosts
  • Comma-separated list of read replica hostnames (only available if read replicas are configured). Read replicas can only be used for read operations.

  • Usage: $ResourceParam('<<resource-name>>', 'readReplicaHosts')
readReplicaConnectionStrings
  • Comma-separated list of connection strings (URLs) used to connect to read replicas (only available when read replicas are configured). Read replicas can only be used for read operations.

  • Usage: $ResourceParam('<<resource-name>>', 'readReplicaConnectionStrings')
readReplicaJdbcConnectionStrings
  • Same as readReplicaConnectionStrings but in JDBC format (only available when read replicas are configured).

  • Usage: $ResourceParam('<<resource-name>>', 'readReplicaJdbcConnectionStrings')

Pricing

Pricing depends heavily on the engine you choose.

RDS engines:

  • Instances: Prices vary by instance size and region. See the AWS pricing pages for PostgreSQL, MySQL, MariaDB, Oracle, and SQL Server.
  • Storage: $0.115 - $0.238 per GB per month.
  • Backups: Free for automated backups that don't exceed your total database storage. Additional backup storage is $0.095 per GB per month.

Aurora engines:

  • Instances: Prices start at $0.073 per hour. See the Aurora pricing page for details.
  • Storage: $0.10 - $0.19 per GB per month.
  • I/O Rate: $0.20 - $0.28 per million read/write operations.
  • Backups: Free for automated backups that don't exceed your total database storage. Additional backup storage is $0.021 - $0.037 per GB per month.

Aurora Serverless:

  • ACUs (Aurora Capacity Units): Each ACU costs $0.06 - $0.10 per hour.
  • Storage, I/O, and Backups: Same as the standard Aurora engine.

Data transfer charges (all engines):

  • Inbound: Free.
  • Outbound (same AZ): Free.
  • Outbound (different AZ): $0.01 per GB.
  • Outbound (internet): First 1GB is free, then $0.09 - $0.15 per GB.

Free Tier (first 12 months):

  • 750 hours of db.t2.micro usage per month.
  • 20GB of SSD storage.
  • 20GB of backup storage.

API reference

RelationalDatabase  API reference
type
Required
properties.credentials
Required
properties.engine
Required
properties.accessibility
properties.deletionProtection
properties.automatedBackupRetentionDays
Default: 1
properties.preferredMaintenanceWindow
properties.alarms
properties.disabledGlobalAlarms
properties.logging
overrides
RdsEngineProperties  API reference
Parent:RdsEngine
primaryInstance
Required
version
Required
dbName
port
storage
readReplicas
disableAutoMinorVersionUpgrade
AuroraEngineProperties  API reference
instances
Required
version
Required
dbName
port
disableAutoMinorVersionUpgrade
AuroraServerlessEngineProperties  API reference
version
dbName
minCapacity
Default: 2
maxCapacity
Default: 4
pauseAfterSeconds
disableAutoMinorVersionUpgrade
PostgresLoggingOptions  API reference
log_connections
log_disconnections
log_lock_waits
log_min_duration_statement
Default: 10000
log_statement
Default: ddl
MysqlLoggingOptions  API reference
server_audit_events
Default: [QUERY_DDL]
long_query_time
Default: 10

Contents