---
title: "Pulumi"
description: "Manage PgBeam projects, databases, replicas, custom domains, cache rules, spend limits, agent credentials, policy profiles, webhook endpoints, self host enrollments, honeytokens, anomaly rules, approval rules, and query templates as infrastructure using Pulumi and the @pgbeam/pulumi package."
canonical: "https://pgbeam.com/docs/pulumi"
last-updated: "2026-10-30T13:32:17.000Z"
---

# Pulumi

> Manage PgBeam projects, databases, replicas, custom domains, cache rules, spend limits, agent credentials, policy profiles, webhook endpoints, self host enrollments, honeytokens, anomaly rules, approval rules, and query templates as infrastructure using Pulumi and the @pgbeam/pulumi package.

URL: https://pgbeam.com/docs/pulumi

Manage your PgBeam infrastructure as code with Pulumi. The `@pgbeam/pulumi` package provides native Pulumi resources for projects, databases, replicas, custom domains, cache rules, spend limits, agent credentials, policy profiles, webhook endpoints, self host enrollments, honeytokens, anomaly rules, approval rules, and query templates.

## Setup

### Install the package

**npm**

`bash npm install @pgbeam/pulumi @pulumi/pulumi `

**pnpm**

`bash pnpm add @pgbeam/pulumi @pulumi/pulumi `

**yarn**

`bash yarn add @pgbeam/pulumi @pulumi/pulumi `

### Configure credentials

Set your PgBeam API key as a secret in Pulumi config, or through the environment:

**Pulumi config**

```bash
pulumi config set pgbeam:apiKey --secret pgb_your_api_key
```

**Environment variable**

```bash title=".env"
PGBEAM_API_KEY=pgb_your_api_key
```

**Programmatic**

```ts title="index.ts"
import * as pgbeam from "@pgbeam/pulumi";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();

pgbeam.configure({ apiKey: config.requireSecret("pgbeamApiKey") });

```

Pass `configure()` a secret, never a string literal: a literal key ends up in your program source.

### Create a project

```ts title="index.ts"
import * as pgbeam from "@pgbeam/pulumi";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();

const project = new pgbeam.Project("my-app", {
  orgId: config.require("orgId"),
  name: "my-app",
  database: {
    host: "my-rds.us-east-1.rds.amazonaws.com",
    port: 5432,
    name: "mydb",
    username: "pgbeam",
    password: config.requireSecret("dbPassword"),
  },
});

export const proxyHost = project.proxyHost;
```

### Deploy

```bash
pulumi up
```

Pulumi creates the PgBeam project and its primary database atomically. The `proxyHost` output gives you the PgBeam proxy endpoint to use in your application connection string.

## Resources

> **Every policy rule type is managed as code.** Policy profiles, honeytokens,
> anomaly rules, approval rules and query templates are managed resources, so
> the enforcement rules, the decoys, the detection sensitivity, what needs a
> human and the statements a templates-only credential may run all live in your
> reviewed IaC flow and are covered by drift detection. Approving or rejecting
> an individual held statement is an operational decision rather than
> configuration, so it stays in the dashboard, the CLI and the API.

### Project

Manages a PgBeam project with a primary database.

```ts
const res = new pgbeam.Project("example", {
  orgId: "org_abc123",
  name: "my-app",
  description: "Production database proxy",
  tags: ["production", "us-east-1"],
  allowedCidrs: [
    { cidr: "10.0.0.0/8", label: "VPC" },
    { cidr: "2001:db8::/32", label: "IPv6 range" },
  ],
  defaultPolicyProfileId: "pol_01h455vb4pex5vsknk084sn02q",
  residency: "eu",
  agentsDisabled: false,
  status: "active",
});
```

**Outputs:** `proxyHost`, `queriesPerSecond`, `burstSize`, `maxConnections`, `databaseCount`, `activeConnections`, `createdAt`, `updatedAt`, `primaryDatabaseId`

### Database

Manages an upstream database connection within a PgBeam project.

```ts
const res = new pgbeam.Database("example", {
  projectId: "prj_01h455vb4pex5vsknk084sn02q",
  host: "db.example.com",
  port: 5432,
  name: "mydb",
  username: "pgbeam",
  sslMode: "require",
  role: "primary",
  poolRegion: "us-east-1",
  queryTimeoutMs: 0,
  autoReadRouting: false,
  cacheConfig: {
    enabled: true,
    ttlSeconds: 60,
    maxEntries: 10000,
    swrSeconds: 30,
  },
  poolConfig: {
    poolSize: 20,
    minPoolSize: 5,
    poolMode: "transaction",
    maxActive: 200,
  },
  password: secret.value,
});
```

**Outputs:** `connectionString`, `createdAt`, `updatedAt`

### Replica

Manages a read replica for a PgBeam database.

Replicas are immutable; any property change triggers replacement.

```ts
const res = new pgbeam.Replica("example", {
  databaseId: "db_01h455vb4pex5vsknk084sn02q",
  host: "replica.db.example.com",
  port: 5432,
  sslMode: "require",
});
```

**Outputs:** `createdAt`, `updatedAt`

### CustomDomain

Manages a custom domain for a PgBeam project.

CustomDomains are immutable; any property change triggers replacement.

```ts
const res = new pgbeam.CustomDomain("example", {
  projectId: "prj_01h455vb4pex5vsknk084sn02q",
  domain: "db.example.com",
});
```

**Outputs:** `verified`, `verifiedAt`, `tlsCertExpiry`, `dnsVerificationToken`, `dnsInstructions`, `createdAt`, `updatedAt`

### CacheRule

Manages a per-query cache rule. Deletion disables caching (soft-delete).

```ts
const res = new pgbeam.CacheRule("example", {
  projectId: "prj_01h455vb4pex5vsknk084sn02q",
  databaseId: "db_01h455vb4pex5vsknk084sn02q",
  queryHash: "a1b2c3d4e5f60718",
  cacheEnabled: true,
  cacheTtlSeconds: 300,
  cacheSwrSeconds: 60,
});
```

**Outputs:** `queryHash`, `normalizedSql`, `queryType`, `callCount`, `avgLatencyMs`, `p95LatencyMs`, `avgResponseBytes`, `stabilityRate`, `recommendation`, `firstSeenAt`, `lastSeenAt`

### SpendLimit

Manages the monthly spend limit for an organization.

```ts
const res = new pgbeam.SpendLimit("example", {
  orgId: "org_abc123",
  spendLimit: 500,
});
```

**Outputs:** `orgId`, `plan`, `billingProvider`, `subscriptionStatus`, `currentPeriodEnd`, `enabled`, `customPricing`, `spendCapped`, `spendCappedAt`, `limits`, `createdAt`, `updatedAt`

### AgentCredential

Manages a scoped agent credential (a PgBeam-issued Postgres login plus a hosted MCP token) for an AI agent. The connection string and MCP token are one-time secrets returned only at creation and exposed as sensitive computed outputs; they cannot be retrieved again. To rotate the secrets, taint/replace the resource (or use the rotate endpoint out of band).

```ts
const res = new pgbeam.AgentCredential("example", {
  projectId: "prj_01h455vb4pex5vsknk084sn02q",
  policyProfileId: "pol_01h455vb4pex5vsknk084sn02q",
  name: "Claude Code (analytics)",
  status: "active",
  principalType: "agent",
  expiresAt: "...",
});
```

**Outputs:** `pgUsername`, `authMethod`, `lastUsedAt`, `createdAt`, `updatedAt`, `connectionString`, `mcpUrl`, `mcpToken`

### PolicyProfile

Manages a policy profile: a named bundle of agent-gateway enforcement rules (access mode, table allow/deny lists, statement-kind rules, PII masking rules, per-relation row filters, query/egress budgets, write mode, approvals, and migration safety) attached to agent credentials and enforced in the PG wire protocol. Nested-list fields (masking_rules, row_filters) and the nested statement_rules object are expressed as structured config.

```ts
const res = new pgbeam.PolicyProfile("example", {
  projectId: "prj_01h455vb4pex5vsknk084sn02q",
  name: "Read-only analytics",
  accessMode: "read_only",
  statementRules: {
    allow: ["..."],
    deny: ["..."],
  },
  tableAllowlist: ["..."],
  tableDenylist: ["..."],
  maskingRules: ["..."],
  budgetQueriesPerHour: 0,
  budgetQueriesPerDay: 0,
  maxRows: 0,
  statementTimeoutMs: 0,
  rowFilters: ["..."],
  writeMode: "normal",
  approvalMode: "off",
  approvalAutoMaxRows: 0,
  approvalTimeoutSeconds: 300,
  migrationSafety: "off",
  egressBytesPerDay: 0,
  maxAffectedRows: 0,
  contentScanMode: "off",
  contentScanMaxBytes: 0,
  queryMode: "sql",
});
```

**Outputs:** `createdAt`, `updatedAt`

### WebhookEndpoint

Manages a webhook endpoint that receives project audit and anomaly event deliveries. The signing secret is write-only and never returned by the API.

```ts
const res = new pgbeam.WebhookEndpoint("example", {
  projectId: "prj_01j9x8y7z6w5v4u3t2s1r0q9p8",
  url: "https://example.com/hooks/pgbeam",
  format: "json",
  eventTypes: ["query_blocked", "canary_tripped"],
  enabled: true,
  description: "Read-only access for the reporting agent",
  secret: "whsec_do-not-log-this",
});
```

**Outputs:** `createdAt`, `updatedAt`

### SelfHostEnrollment

Manages a self-host (BYOC) enrollment: a token a self-hosted proxy uses to authenticate to the control plane's config/audit stream. The token is a one-time secret returned only at creation and exposed as a sensitive computed output; it cannot be retrieved again. To rotate the token, replace the resource. Deletion revokes the enrollment.

SelfHostEnrollments are immutable; any property change triggers replacement.

```ts
const res = new pgbeam.SelfHostEnrollment("example", {
  orgId: "org_abc123",
  regionLabel: "customer-vpc-us-east",
  description: "prod cluster",
  expiresAt: "...",
});
```

**Outputs:** `createdBy`, `createdAt`, `lastSeenAt`, `revokedAt`, `token`

### Honeytoken

Manages a honeytoken: a decoy (canary) relation that no legitimate query should ever touch. Any agent statement referencing it is blocked and recorded as a canary_tripped audit event; the kill action additionally disables the tripping credential via the kill-switch. The relation does not have to exist in the upstream database: enforcement is by name, in the wire protocol, before the statement reaches Postgres.

```ts
const res = new pgbeam.Honeytoken("example", {
  projectId: "prj_01j9x8y7z6w5v4u3t2s1r0q9p8",
  schemaName: "public",
  relationName: "customer_ssns",
  action: "audit_only",
});
```

**Outputs:** `createdAt`, `updatedAt`

### AnomalyRule

Manages an anomaly rule: the sensitivity of one detection metric for one project, or for one agent credential in it. Without a rule every metric resolves to the deployment default, so a credential that legitimately runs at 3am cannot be exempted and a noisy one cannot be loosened. Note the polarity of enabled: the rule IS the switch, so enabled=false silences the metric for that scope rather than deactivating the rule. One rule exists per (project, credential, metric); a second one for the same scope is a conflict. Detection runs in the control plane and re-reads the rules on every pass, so a write takes effect at the next detection pass rather than being pushed to the proxy.

```ts
const res = new pgbeam.AnomalyRule("example", {
  projectId: "prj_01j9x8y7z6w5v4u3t2s1r0q9p8",
  credentialId: "agt_01j9x8y7z6w5v4u3t2s1r0q9p8",
  metric: "bytes_per_hour",
  sigmaThreshold: 4.5,
  floor: 100,
  enabled: true,
});
```

**Outputs:** `createdAt`, `updatedAt`

### ApprovalRule

Manages an approval rule: a named, project-scoped rule that sends the agent statements it matches to a human for approval, whatever the policy profile's approval_mode says. A rule can only add approval; no rule can exempt a statement from approval_mode. Match on statement kinds, a schema, a relation, and optionally a minimum affected-row count. The name is shown to the reviewer on every approval request the rule holds and must be unique within the project. enabled=false switches the rule off. A write is pushed to connected data planes immediately.

```ts
const res = new pgbeam.ApprovalRule("example", {
  projectId: "prj_01j9x8y7z6w5v4u3t2s1r0q9p8",
  name: "payments deletes",
  statementKinds: ["..."],
  schemaName: "public",
  relationName: "payments",
  minAffectedRows: 1000,
  enabled: true,
});
```

**Outputs:** `createdAt`, `updatedAt`

### QueryTemplate

Manages a query template: a named, parameterized statement, scoped to a project, that a policy profile set to admit templates only lets its credentials run in place of raw SQL. A template admits a statement; it never grants access, so a statement it admits still meets the credential's whole policy. The sql is validated on every write and must be exactly one SELECT, INSERT, UPDATE or DELETE whose placeholders ($1, $2, ...) match params in order. The name must be unique within the project. enabled=false withdraws the template. A write is pushed to connected data planes immediately.

```ts
const res = new pgbeam.QueryTemplate("example", {
  projectId: "prj_01j9x8y7z6w5v4u3t2s1r0q9p8",
  name: "open_orders_for_customer",
  description: "Open orders for one customer, newest first.",
  sql: "SELECT id, total FROM orders WHERE customer_id = $1 AND status = 'open' ORDER BY created_at DESC",
  params: ["..."],
  includeSql: false,
  enabled: true,
});
```

**Outputs:** `statementKind`, `createdAt`, `updatedAt`

## Configuration

| Setting          | Source        | Description                                      |
| ---------------- | ------------- | ------------------------------------------------ |
| `pgbeam:apiKey`  | Pulumi config | API key (recommended: use `--secret`)            |
| `pgbeam:baseUrl` | Pulumi config | API base URL (default: `https://api.pgbeam.com`) |
| `PGBEAM_API_KEY` | Environment   | Fallback API key                                 |
| `PGBEAM_API_URL` | Environment   | Fallback base URL                                |

Config resolution order: `configure()` call > Pulumi stack config > environment variables. A `configure()` call replaces both settings: if it omits `baseUrl`, the default URL applies, not `pgbeam:baseUrl` or `PGBEAM_API_URL`.

## Replacement vs update

Some property changes trigger resource replacement (delete + create) rather than in-place updates:

| Resource           | Replacement triggers                                                 |
| ------------------ | -------------------------------------------------------------------- |
| Project            | `orgId`, `cloud`, `selfHosted`                                       |
| Database           | `projectId`                                                          |
| Replica            | Any property change (immutable)                                      |
| CustomDomain       | Any property change (immutable)                                      |
| CacheRule          | `projectId`, `databaseId`, `queryHash`                               |
| SpendLimit         | `orgId`                                                              |
| AgentCredential    | `projectId`, `policyProfileId`, `name`, `principalType`, `expiresAt` |
| PolicyProfile      | `projectId`                                                          |
| WebhookEndpoint    | `projectId`                                                          |
| SelfHostEnrollment | Any property change (immutable)                                      |
| Honeytoken         | `projectId`                                                          |
| AnomalyRule        | `projectId`                                                          |
| ApprovalRule       | `projectId`                                                          |
| QueryTemplate      | `projectId`                                                          |

## Further reading

* [Connection Pooling](/docs/pooling): pool modes and sizing
* [Caching](/docs/cache): query caching and SWR
* [Read Replicas](/docs/replicas): replica routing
* [Custom Domains](/docs/custom-domains): DNS setup and verification
* [API Keys](/docs/api-keys): managing API credentials
* [Plans](/docs/plans): plan limits and pricing