Config-Driven Infrastructure
Infrastructure in Slingshot follows the same config-driven philosophy as the application layer. You declare what you need in TypeScript; the CLI generates and applies everything else.
Status: Deferred. This package documents and drives infra generation, but it is not currently on the production-hardening track.
Two config files drive the entire deploy pipeline:
slingshot.platform.ts— org-level infrastructure: cloud provider, shared resources (databases, caches), secrets backend, deployment stacks, stages, DNSslingshot.infra.ts— app-level config: which stacks to deploy to, container sizing, scaling policy, health check, which resources the app uses
slingshot.platform.ts defines what infrastructure existsslingshot.infra.ts defines how this app uses itslingshot deploy generates and applies everythingInstall
Section titled “Install”bun add --dev @lastshotlabs/slingshot-infraslingshot-infra is a dev dependency — it has no runtime footprint. It is a deploy-time tooling package, not a runtime package.
slingshot.platform.ts
Section titled “slingshot.platform.ts”definePlatform() declares org-level infrastructure. It is shared across all services that deploy to the same platform.
import { definePlatform } from '@lastshotlabs/slingshot-infra';
export default definePlatform({ org: 'acme', provider: 'aws', region: 'us-east-1',
// Where to store deployment metadata (registry) registry: { provider: 's3', bucket: 'acme-slingshot-registry', },
// Secrets backend — where secrets are pushed/pulled secrets: { provider: 'ssm', pathPrefix: '/acme/', },
// Shared resources (databases, caches, etc.) provisioned once and used by all services resources: { db: { type: 'postgres', provision: true, stages: { dev: { instanceClass: 'db.t3.micro', storageGb: 20 }, prod: { instanceClass: 'db.r6g.large', storageGb: 200, multiAz: true }, }, }, cache: { type: 'redis', provision: true, }, search: { type: 'opensearch', provision: true, stages: { dev: { instanceType: 't3.small.search' }, prod: { instanceType: 'r6g.large.search', dedicatedMaster: true }, }, }, },
// Deployment stacks — choose ECS (managed) or EC2 (self-managed) stacks: { main: { preset: 'ecs' }, },
// Stages — dev, staging, prod — each with a domain suffix and optional overrides stages: { dev: { domainSuffix: '.dev.acme.com' }, staging: { domainSuffix: '.staging.acme.com' }, prod: { domainSuffix: '.acme.com' }, },
// Org-wide defaults — overridable per service in slingshot.infra.ts defaults: { scaling: { min: 1, max: 3, targetCpuPercent: 70 }, logging: { driver: 'cloudwatch', retentionDays: 30 }, },
// DNS management dns: { provider: 'cloudflare', apiToken: process.env.CLOUDFLARE_API_TOKEN, },});Resources
Section titled “Resources”The resources map declares shared infrastructure. Each resource has a type and a provision flag:
| Type | What it provisions |
|---|---|
postgres | RDS Aurora PostgreSQL (SST/CloudFormation) |
redis | ElastiCache Redis (SST/CloudFormation) |
mongo | MongoDB Atlas cluster (Atlas API) |
documentdb | AWS DocumentDB cluster (SST/CloudFormation) |
opensearch | AWS OpenSearch domain (SST/CloudFormation) |
When provision: true, slingshot platform deploy --stage <name> creates the resource if it doesn’t exist. Subsequent runs update it in place.
Stacks
Section titled “Stacks”Stacks map to deployment targets. Each stack has a preset:
| Preset | Description |
|---|---|
ecs | AWS ECS Fargate — managed containers, auto-scaling ALB |
ec2-nginx | EC2 + Docker Compose + Caddy (or nginx) |
Registry
Section titled “Registry”The registry tracks deployed images, service metadata, and previous image tags (for rollback). Available backends:
| Provider | Config |
|---|---|
s3 | { provider: 's3', bucket: '...' } |
redis | { provider: 'redis', url: '...' } |
postgres | { provider: 'postgres', connectionString: '...' } |
local | { provider: 'local', path: '.slingshot-registry' } |
For multi-repo organizations, the S3, Redis, or Postgres backends provide cross-repo locking and visibility. The local backend is sufficient for single-repo setups.
Secrets
Section titled “Secrets”Secrets backends manage where slingshot secrets push/pull/check reads and writes:
| Provider | Notes |
|---|---|
ssm | AWS SSM Parameter Store — standard choice for AWS deployments |
env | Read from environment variables — no push/pull, values provided externally |
file | Read from a local .env file — not for production |
slingshot.infra.ts
Section titled “slingshot.infra.ts”defineInfra() declares how this specific service deploys. It references stacks, resources, and scaling config from the platform.
import { defineInfra } from '@lastshotlabs/slingshot-infra';
export default defineInfra({ stacks: ['main'],
// Domain — resolves to api.dev.acme.com / api.acme.com per stage domain: 'api',
// Container config port: 3000, size: 'medium', // small | medium | large | xlarge
// Which shared resources this service uses — auto-wires env vars uses: ['db', 'cache'],
// Health check path healthCheck: '/health',
// Scaling overrides (inherits platform defaults when omitted) scaling: { min: 2, max: 10, targetCpuPercent: 60, },});Container sizes
Section titled “Container sizes”| Size | CPU | Memory | Good for |
|---|---|---|---|
small | 256 | 512 MB | Dev, low traffic |
medium | 512 | 1 GB | Moderate traffic |
large | 1024 | 2 GB | Production workloads |
xlarge | 2048 | 4 GB | High traffic, memory-intensive |
Resource auto-wiring
Section titled “Resource auto-wiring”uses tells the deploy pipeline which platform resources this service needs. Connection strings are injected into the container automatically — no manual env var setup:
uses: ['db', 'cache'];// → DATABASE_URL (from provisioned RDS/Postgres output)// → REDIS_URL (from provisioned ElastiCache/Redis output)Reference them in your app config:
import { defineApp } from '@lastshotlabs/slingshot';
export default defineApp({ db: { postgres: process.env.DATABASE_URL, redis: process.env.REDIS_URL, },});Multi-service apps
Section titled “Multi-service apps”When an app has multiple services (API, worker, scheduler), declare them in a services map:
export default defineInfra({ services: { api: { path: './services/api', stacks: ['main'], domain: 'api', port: 3000, size: 'medium', uses: ['db', 'cache'], healthCheck: '/health', scaling: { min: 2, max: 8 }, }, worker: { path: './services/worker', stacks: ['main'], size: 'small', uses: ['db', 'cache'], scaling: { min: 1, max: 3 }, }, },});Each service gets its own Dockerfile, task definition, and scaling policy.
The deploy pipeline
Section titled “The deploy pipeline”Running slingshot deploy --stage prod executes this sequence:
- Load configs — reads
slingshot.platform.tsandslingshot.infra.ts, validates both - Generate files — emits
Dockerfile,sst.config.ts(ECS) ordocker-compose.yml+Caddyfile(EC2), and.github/workflows/deploy.yml - Resolve secrets — pulls required secrets from the configured backend and confirms all required values are present
- Auto-wire connections — reads provisioned resource outputs (RDS endpoint, ElastiCache endpoint) and injects them as container env vars
- Build and push image — builds the Docker image and pushes to ECR (or a configured container registry)
- Deploy — runs
bunx sst deploy --stage prod(ECS) or SSHes to the EC2 host and updates the Docker Compose stack - Update DNS — creates or updates A/CNAME records via the configured DNS provider
- Update registry — records the deployed image tag per service per stage (enables rollback)
Preview before deploying
Section titled “Preview before deploying”# Show the deploy plan without executingslingshot deploy --stage prod --plan
# Generate config files without deployingslingshot infra generate --stage prod
# Validate config without startingslingshot start --dry-runThe generated .github/workflows/deploy.yml runs on push to main:
on: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1 - run: bun install - run: slingshot deploy --stage prod --yes env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}Provisioning shared resources
Section titled “Provisioning shared resources”Create or update the shared resources for a stage:
slingshot platform deploy --stage devThis provisions any resources with provision: true that don’t already exist. Safe to re-run — existing resources are updated in place, not recreated.
Destroy a stage
Section titled “Destroy a stage”slingshot platform destroy --stage devTears down all provisioned resources for the stage. Prompts for confirmation before proceeding.
Rollback
Section titled “Rollback”The registry tracks previous image tags per service per stage. Roll back to the prior deploy:
slingshot rollback --stage prodslingshot rollback --stage prod --service apislingshot rollback --stage prod --service api --tag v1.2.3Secrets management
Section titled “Secrets management”Push local secrets to the configured backend before deploying:
slingshot secrets push --stage prodVerify all required secrets are present:
slingshot secrets check --stage prodPull production secrets to a local .env:
slingshot secrets pull --stage prodSee Secrets for the full reference.
Generated files
Section titled “Generated files”All generated files use # --- section:name --- / # --- end:name --- markers so you can override specific sections without replacing the whole file:
# --- section:base ---FROM oven/bun:1-alpine AS baseWORKDIR /app# --- end:base ---
# --- section:deps ---FROM base AS depsCOPY package.json bun.lockb ./RUN bun install --frozen-lockfile# --- end:deps ---Add your own content outside the markers and it survives regeneration.
EC2 deployments
Section titled “EC2 deployments”For self-managed EC2 with Docker Compose:
import { definePlatform } from '@lastshotlabs/slingshot-infra';
export default definePlatform({ org: 'acme', provider: 'aws', region: 'us-east-1', registry: { provider: 'local', path: '.slingshot/registry.json', }, stages: { dev: { env: { NODE_ENV: 'development', }, }, }, stacks: { main: { preset: 'ec2-nginx', network: { sshKeyName: 'my-key-pair', }, }, },});Register your EC2 host:
slingshot servers add --host 1.2.3.4 --stage devThe ec2-nginx preset generates Dockerfile, docker-compose.yml, and a Caddyfile with automatic TLS via Let’s Encrypt. Pass proxy: 'nginx' to use nginx instead of Caddy.
The runtime-to-infra bridge
Section titled “The runtime-to-infra bridge”Slingshot can detect which resources your app uses from your app.config.ts and compare against slingshot.infra.ts:
slingshot infra checkThis reads both configs and warns if your app declares db.postgres but uses doesn’t include 'db', or if uses declares a resource that isn’t in slingshot.platform.ts. Run it during development to catch wiring mismatches before deploy.
See also
Section titled “See also”- Deployment Guide — step-by-step deploy walkthrough
- Horizontal Scaling — multi-instance configuration with shared state
- Secrets — managing secrets across environments
- slingshot-infra — full API reference for
definePlatformanddefineInfra