Deployment
VentureKit applications are deployed to AWS using CDK through the vk CLI.
Prerequisites
Section titled “Prerequisites”- AWS credentials configured (
aws configureor environment variables) - Node.js >= 20
- VentureKit project initialized with
vk init
Development
Section titled “Development”Start a local development server with hot-reloading:
vk devThis starts a fully local development server that:
- Serves your routes via HTTP (no AWS credentials needed)
- Starts Docker Compose services for Postgres, Redis, and MinIO
- Loads TypeScript handlers at runtime — no build step
Custom Stage
Section titled “Custom Stage”vk dev --stage my-devDeploying
Section titled “Deploying”Dev Environment
Section titled “Dev Environment”vk deployStaging
Section titled “Staging”vk deploy --stage stageProduction
Section titled “Production”vk deploy --stage prodFree-Tier Deployment
Section titled “Free-Tier Deployment”Deploy VentureKit at zero cost using only AWS free-tier services:
import type { EnvConfigInput } from '@venturekit/core';
export const dev: EnvConfigInput = { preset: 'free', dataSafety: 'relaxed',};The free preset automatically:
- Uses Lambda (1M free requests/month) with 128 MB
- Uses API Gateway HTTP API (1M free calls/month for 12 months)
- Substitutes DynamoDB for RDS databases (25 GB always-free)
- Skips VPC and NAT gateways (not free)
- Skips ElastiCache — uses DynamoDB-based rate limiting instead
- Uses SQS (1M free requests/month) and EventBridge (free)
Environment Selection
Section titled “Environment Selection”The VENTURE_STAGE environment variable maps to your config files:
| Stage | Config | Typical Preset |
|---|---|---|
dev | config/dev.ts | free or nano |
stage | config/stage.ts | micro |
prod | config/prod.ts | medium or large |
# These are equivalent:vk deploy --stage prodVENTURE_STAGE=prod vk deployTearing Down
Section titled “Tearing Down”Remove deployed resources:
# Remove dev (no confirmation needed)vk remove
# Remove staging (requires confirmation)vk remove --stage stage
# Remove production (requires confirmation, skip with --yes)vk remove --stage prod --yesGitHub Actions Example
Section titled “GitHub Actions Example”name: Deployon: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - uses: pnpm/action-setup@v2 with: version: 9
- run: pnpm install - run: vk deploy --stage prod env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: eu-west-1Custom Domains
Section titled “Custom Domains”Configure a custom domain in your environment config:
export const prod: EnvConfigInput = { preset: 'medium', api: { customDomain: { domainName: 'api.example.com', certificateArn: 'arn:aws:acm:us-east-1:...', }, },};Multi-Stack Layout
Section titled “Multi-Stack Layout”vk deploy synthesizes a seven-tier multi-stack layout that splits resources by lifecycle. Every stack is named <project>-<stage>-<tier>, and a tier is only created when the intents that need it are declared:
| Tier | Owns | Created when |
|---|---|---|
network | VPC, subnets, NAT gateways / fck-nat, VPC endpoints, flow logs | databases, caches, or vpc.required |
data | RDS + master secret (+ the opt-in RDS Proxy), ElastiCache, the runtime DynamoDB tables (rate limits, idempotency, sagas), S3 buckets | databases, storage, caches, tables — the runtime tables always |
identity | Cognito User Pools, app clients, federated-provider secrets, custom-sender KMS keys | auth |
config | secrets[] (SSM or Secrets Manager) and billing[] credential placeholders | secrets / billing |
messaging | SQS queues + DLQs, SES identities + configuration sets, SNS events topics, EventBridge buses | queues / notify / events |
edge | HTTP API shell, custom domains + ACM certificates, CloudFront | always (HTTP API) |
app | Every Lambda (routes, functions, queue consumers, crons, WebSocket, notify dispatcher, bounce handler, migration runner), their execution roles, the internal-HMAC secret, route registrations, event-source mappings, EventBridge rules, log groups | always |
Lower tiers publish every cross-stack value to SSM Parameter Store under /venturekit/<project>/<stage>/<tier>/…; inside one deploy CDK wires the references directly, and the SSM copies let independent deploys, external auth intents, cross-venture event buses and operator tooling read VentureKit state.
What every Lambda receives
Section titled “What every Lambda receives”Besides the per-intent variables (AUTH_<ID>_*, STORAGE_<ID>_BUCKET, NOTIFY_<ID>_*, …), the app tier injects a fixed set into every function:
| Variable | Value | Read by |
|---|---|---|
VENTURE_PROJECT_NAME / VENTURE_STAGE | The project and stage | Everything that reconstructs a resource name |
VENTURE_RATE_LIMIT_TABLE | <project>-rate-limits-<stage> | createDefaultRateLimiter() |
VENTURE_IDEMPOTENCY_TABLE / VENTURE_SAGA_TABLE | <project>-idempotency-<stage> / <project>-sagas-<stage> — provisioned by the data tier for every preset, one per project and stage | createDynamoDBIdempotencyStore() / createDynamoDBSagaStore() — see Idempotency and Saga |
VENTURE_INTERNAL_HMAC_SECRET_ARN | The ARN of venturekit/<project>/<stage>/internal-hmac, a 256-bit key generated at deploy | invoke() and handler() fetch it once per container; the value never appears in a function’s configuration, and only the execution roles hold secretsmanager:GetSecretValue on it |
NODE_OPTIONS=--enable-source-maps | Stack traces point at .ts sources | CloudWatch |
Two shapes are deliberately not default because they cost money or widen a trust boundary until you ask: databases[].proxy (RDS Proxy, ≈ $22/month per database — see the Database Intent) and the Cognito admin grants below.
Execution roles and Cognito admin powers
Section titled “Execution roles and Cognito admin powers”By default every Lambda shares one execution role, and when an auth intent is declared that role carries the Cognito admin actions (AdminCreateUser, AdminSetUserPassword, AdminInitiateAuth, …) the sign-up, federated, passwordless and forgot-password helpers need — a synth-time warning reminds you. Declaring auth[].adminRoutes splits it: vk deploy provisions a second role that carries those grants and attaches it only to the functions whose API path ('/auth/**') or id ('fn:cognito-user-migration') matches, so a bug in an unrelated public route can never be turned into “reset anyone’s password”. Every other grant (tables, buckets, queues, the HMAC secret) is applied to both roles. See the Auth Intent for the matching rules and vk doctor --stage <stage> to check a stage’s readiness before deploying.
Why tiers
Section titled “Why tiers”- Independent lifecycles. A typical week churns Lambda code dozens of times but never touches RDS. Only the
apptier turns over on a normal deploy; the stateful tiers stay untouched (and the network tier changes roughly once a year). - Blast-radius isolation. A bug in your edge config can’t take the database with it during a failed rollback — each tier rolls back independently, and
vk deploy’s pre-flight inspects every tier’s state before synthesising. - Cleaner ops boundaries. The data tier is owned by your DBA / SRE; the app tier by your application engineers. The stack split mirrors that ownership.
Retention guarantees
Section titled “Retention guarantees”All tiers apply the same retention rules under dataSafety: 'strict':
- Every stateful resource (RDS, S3, Cognito, Secrets, ElastiCache, EventBridge buses, DLQs) carries
DeletionPolicy: Retain+UpdateReplacePolicy: Retain. - Native deletion-protection is enabled where AWS supports it (RDS, DynamoDB).
vk remove --stage prodorphans those resources rather than destroying them.
The retention guarantees are tested independently per stack tier. See @venturekit/infra’s *-stack.test.ts files for the exact invariants pinned by tests.
Migrating a legacy single-stack deployment
Section titled “Migrating a legacy single-stack deployment”Deployments created by pre-release versions that synthesised one <project>-<stage> stack cannot be flipped to the tiered layout by a config change — CloudFormation does not split one stack into many. The cutover requires cdk import per stateful resource, executed in order. See examples/multistack-migration-runbook.md in the VentureKit monorepo for the canonical pattern (RETAIN-flip → detach → cdk import → smoke test → decommission legacy).
Deploy Outputs
Section titled “Deploy Outputs”After deployment, the CLI displays:
- API URL — your API Gateway endpoint
- WebSocket URL — if WebSocket is enabled
- Stage and environment information
✅ Deployed to prod
API URL: https://abc123.execute-api.eu-west-1.amazonaws.com