Skip to content

AWS Lambda

Tested with: Lambda Node.js 22 runtime · AWS SDK v3 · Secrets Manager · PostgreSQL 17

  1. Store the credential in Secrets Manager and grant the function’s role secretsmanager:GetSecretValue on it. Read it once per container, not per invocation.

    src/db.ts
    import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
    import { Pool } from "pg";
    let pool: Pool | undefined;
    export async function getPool() {
    if (pool) return pool;
    const sm = new SecretsManagerClient({});
    const { SecretString } = await sm.send(new GetSecretValueCommand({ SecretId: "databasezy/pg-prod" }));
    const s = JSON.parse(SecretString!);
    pool = new Pool({
    host: `${s.host.replace(".us-east", "-pool.us-east")}`, // pooled endpoint
    port: Number(s.port),
    user: s.username,
    password: s.password,
    database: s.database,
    ssl: { rejectUnauthorized: true, servername: s.host },
    max: 2,
    });
    return pool;
    }
  2. Give the function a fixed egress address. Attach it to private subnets whose route to the internet goes through a NAT gateway with an Elastic IP, and allow that IP.

    shell
    zb api PUT /v1/orgs/{org}/instances/pg-7f3k/network -d '{"allow_cidrs": ["52.0.0.10/32"]}' # replaces the list

    Functions outside a VPC use AWS’s shared, changing addresses; that requires 0.0.0.0/0 on the allow-list.

  3. Use the pooled endpoint (transaction mode) so concurrent invocations do not exhaust max_connections, and keep max per container tiny. Set context.callbackWaitsForEmptyEventLoop = false if you keep the pool open.

Run them from CI or from a one-off Lambda invoked in the deploy pipeline, against the direct endpoint.