sst-deployment

This skill helps you configure and deploy infrastructure using SST v3 in infra/ .

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "sst-deployment" with this command: npx skills add sgcarstrends/sgcarstrends/sgcarstrends-sgcarstrends-sst-deployment

SST Deployment Skill

This skill helps you configure and deploy infrastructure using SST v3 in infra/ .

When to Use This Skill

  • Deploying applications to AWS

  • Adding new Lambda functions or API routes

  • Configuring environment variables per stage

  • Setting up custom domains

  • Adding AWS resources (S3, DynamoDB, etc.)

  • Debugging deployment failures

  • Managing multiple environments (dev, staging, prod)

SST Architecture

infra/ ├── sst.config.ts # SST configuration ├── api.ts # API Lambda configuration ├── web.ts # Next.js web app configuration ├── dns.ts # Domain and DNS configuration └── database.ts # Database resources (optional)

SST Configuration

Main Config File

// infra/sst.config.ts import { SSTConfig } from "sst"; import { API } from "./api"; import { Web } from "./web"; import { DNS } from "./dns";

export default { config(_input) { return { name: "sgcarstrends", region: "ap-southeast-1", // Singapore profile: "default", }; }, stacks(app) { app.stack(DNS).stack(API).stack(Web); }, } satisfies SSTConfig;

Environment Management

Stage-Based Configuration

// infra/sst.config.ts export default { config(input) { return { name: "sgcarstrends", region: "ap-southeast-1", profile: input.stage === "production" ? "prod" : "default", }; }, stacks(app) { // Set default removal policy based on stage app.setDefaultRemovalPolicy( app.stage === "production" ? "retain" : "destroy" );

app.stack(DNS).stack(API).stack(Web);

}, } satisfies SSTConfig;

Environment-Specific Settings

// infra/api.ts import { StackContext, Function } from "sst/constructs";

export function API({ stack, app }: StackContext) { const stage = app.stage;

// Stage-specific configuration const config = { dev: { memory: 512, timeout: "30 seconds", runtime: "nodejs20.x", }, staging: { memory: 1024, timeout: "60 seconds", runtime: "nodejs20.x", }, production: { memory: 2048, timeout: "120 seconds", runtime: "nodejs20.x", }, }[stage] || { memory: 512, timeout: "30 seconds", runtime: "nodejs20.x", };

const api = new Function(stack, "api", { handler: "apps/api/src/index.handler", ...config, environment: { NODE_ENV: stage === "production" ? "production" : "development", DATABASE_URL: process.env.DATABASE_URL!, UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL!, UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN!, }, });

stack.addOutputs({ ApiUrl: api.url, });

return { api }; }

Lambda Configuration

API Lambda

// infra/api.ts import { StackContext, Function, use } from "sst/constructs"; import { DNS } from "./dns";

export function API({ stack, app }: StackContext) { const { hostedZone } = use(DNS);

const api = new Function(stack, "api", { handler: "apps/api/src/index.handler", runtime: "nodejs20.x", architecture: "arm64", // Graviton2 - cheaper and faster memory: 1024, timeout: "60 seconds", nodejs: { esbuild: { minify: app.stage === "production", sourcemap: app.stage !== "production", }, }, environment: { DATABASE_URL: process.env.DATABASE_URL!, UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL!, UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN!, GOOGLE_GEMINI_API_KEY: process.env.GOOGLE_GEMINI_API_KEY!, QSTASH_TOKEN: process.env.QSTASH_TOKEN!, DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL!, TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN!, TELEGRAM_CHAT_ID: process.env.TELEGRAM_CHAT_ID!, }, url: { // Custom domain for API domain: { domainName: api.${app.stage === "production" ? "" : ${app.stage}.}sgcarstrends.com, hostedZone: hostedZone.zoneName, }, }, });

stack.addOutputs({ ApiUrl: api.url, ApiDomain: api.url, });

return { api }; }

Next.js Web App

// infra/web.ts import { StackContext, NextjsSite, use } from "sst/constructs"; import { DNS } from "./dns";

export function Web({ stack, app }: StackContext) { const { hostedZone } = use(DNS);

const web = new NextjsSite(stack, "web", { path: "apps/web", buildCommand: "pnpm build", environment: { NEXT_PUBLIC_API_URL: https://api.${app.stage === "production" ? "" : ${app.stage}.}sgcarstrends.com, DATABASE_URL: process.env.DATABASE_URL!, UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL!, UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN!, }, customDomain: { domainName: app.stage === "production" ? "sgcarstrends.com" : ${app.stage}.sgcarstrends.com, hostedZone: hostedZone.zoneName, }, });

stack.addOutputs({ WebUrl: web.url, WebDomain: web.customDomainUrl, });

return { web }; }

DNS and Domains

Hosted Zone Configuration

// infra/dns.ts import { StackContext } from "sst/constructs"; import * as route53 from "aws-cdk-lib/aws-route53";

export function DNS({ stack }: StackContext) { // Import existing hosted zone const hostedZone = route53.HostedZone.fromLookup(stack, "HostedZone", { domainName: "sgcarstrends.com", });

stack.addOutputs({ HostedZoneId: hostedZone.hostedZoneId, HostedZoneName: hostedZone.zoneName, });

return { hostedZone }; }

Domain Mapping

Environment-specific domains:

  • Production: sgcarstrends.com , api.sgcarstrends.com

  • Staging: staging.sgcarstrends.com , api.staging.sgcarstrends.com

  • Dev: dev.sgcarstrends.com , api.dev.sgcarstrends.com

Deployment Commands

Deploy to Environments

Development

pnpm deploy:dev

or

cd infra && npx sst deploy --stage dev

Staging

pnpm deploy:staging

or

cd infra && npx sst deploy --stage staging

Production

pnpm deploy:prod

or

cd infra && npx sst deploy --stage production

Deploy Specific Stack

Deploy only API

cd infra && npx sst deploy --stage dev API

Deploy only Web

cd infra && npx sst deploy --stage dev Web

Remove Stack

Remove dev stack

cd infra && npx sst remove --stage dev

Remove specific stack

cd infra && npx sst remove --stage dev API

Environment Variables

Local Development

.env.local (not committed)

DATABASE_URL=postgresql://... UPSTASH_REDIS_REST_URL=https://... UPSTASH_REDIS_REST_TOKEN=... GOOGLE_GEMINI_API_KEY=... QSTASH_TOKEN=...

SST Secrets

Use SST secrets for sensitive values:

Set secret for specific stage

npx sst secrets set DATABASE_URL "postgresql://..." --stage production

List secrets

npx sst secrets list --stage production

Remove secret

npx sst secrets remove DATABASE_URL --stage production

Access secrets in code:

import { Config } from "sst/node/config";

export function handler() { const databaseUrl = Config.DATABASE_URL; // Use secret... }

Parameter Store

// infra/api.ts import { StringParameter } from "aws-cdk-lib/aws-ssm";

export function API({ stack }: StackContext) { // Store parameter const param = new StringParameter(stack, "DatabaseUrl", { parameterName: /sgcarstrends/${stack.stage}/database-url, stringValue: process.env.DATABASE_URL!, });

const api = new Function(stack, "api", { handler: "apps/api/src/index.handler", environment: { DATABASE_URL: param.stringValue, }, }); }

Adding AWS Resources

S3 Bucket

// infra/storage.ts import { StackContext, Bucket } from "sst/constructs";

export function Storage({ stack, app }: StackContext) { const bucket = new Bucket(stack, "uploads", { cors: [ { allowedMethods: ["GET", "PUT", "POST", "DELETE", "HEAD"], allowedOrigins: [""], allowedHeaders: [""], }, ], });

stack.addOutputs({ BucketName: bucket.bucketName, });

return { bucket }; }

DynamoDB Table

// infra/database.ts import { StackContext, Table } from "sst/constructs";

export function Database({ stack }: StackContext) { const table = new Table(stack, "sessions", { fields: { userId: "string", sessionId: "string", }, primaryIndex: { partitionKey: "userId", sortKey: "sessionId" }, timeToLiveAttribute: "expiresAt", });

stack.addOutputs({ TableName: table.tableName, });

return { table }; }

EventBridge Cron

// infra/cron.ts import { StackContext, Cron, use } from "sst/constructs"; import { API } from "./api";

export function Cron({ stack }: StackContext) { const { api } = use(API);

new Cron(stack, "DataUpdateCron", { schedule: "rate(1 hour)", job: { function: { handler: "apps/api/src/cron/update-data.handler", environment: { API_URL: api.url, }, }, }, }); }

Debugging Deployments

Check Deployment Status

List all stacks

npx sst stacks list --stage dev

Get stack info

npx sst stacks info API --stage dev

View Logs

Tail logs for API function

npx sst logs --stage dev --function api

Tail logs with filter

npx sst logs --stage dev --function api --filter "ERROR"

Console Access

Open SST console

npx sst console --stage dev

Check Outputs

Get stack outputs

npx sst outputs --stage dev

Common Issues

Deployment Failures

Issue: Deployment fails with timeout Solution: Increase Lambda timeout or check network issues

const api = new Function(stack, "api", { timeout: "120 seconds", // Increase from 30s });

Issue: Out of memory errors Solution: Increase memory allocation

const api = new Function(stack, "api", { memory: 2048, // Increase from 1024 });

Issue: Domain not working Solution: Verify DNS propagation and Route53 configuration

Check DNS records

dig sgcarstrends.com dig api.sgcarstrends.com

Check certificate status in AWS Console

Environment Variable Issues

Issue: Environment variables not available Solution: Check they're set in SST config

environment: { DATABASE_URL: process.env.DATABASE_URL!, // Ensure all required vars are listed }

Issue: Secrets not found Solution: Set secrets for the correct stage

npx sst secrets set DATABASE_URL "value" --stage production

Monitoring

CloudWatch Metrics

SST automatically creates CloudWatch metrics for:

  • Lambda invocations

  • Errors

  • Duration

  • Throttles

  • Concurrent executions

Access in AWS Console: CloudWatch → Metrics → Lambda

Alarms

// infra/monitoring.ts import { StackContext, use } from "sst/constructs"; import { Alarm } from "aws-cdk-lib/aws-cloudwatch"; import { API } from "./api";

export function Monitoring({ stack }: StackContext) { const { api } = use(API);

new Alarm(stack, "ApiErrorAlarm", { metric: api.metricErrors(), threshold: 10, evaluationPeriods: 1, alarmDescription: "Alert when API has more than 10 errors", }); }

Cost Optimization

Use ARM Architecture

const api = new Function(stack, "api", { architecture: "arm64", // 20% cheaper than x86 });

Appropriate Memory

// Don't over-provision const api = new Function(stack, "api", { memory: 1024, // Start here, adjust based on metrics });

Enable Minification

const api = new Function(stack, "api", { nodejs: { esbuild: { minify: app.stage === "production", }, }, });

CI/CD Integration

GitHub Actions

.github/workflows/deploy-prod.yml

name: Deploy Production

on: push: branches: [main]

jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4

  - uses: pnpm/action-setup@v2
    with:
      version: 8

  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: "pnpm"

  - run: pnpm install

  - name: Configure AWS credentials
    uses: aws-actions/configure-aws-credentials@v4
    with:
      aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
      aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      aws-region: ap-southeast-1

  - name: Deploy to production
    run: pnpm deploy:prod
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
      UPSTASH_REDIS_REST_URL: ${{ secrets.UPSTASH_REDIS_REST_URL }}
      UPSTASH_REDIS_REST_TOKEN: ${{ secrets.UPSTASH_REDIS_REST_TOKEN }}

Rollback Strategy

Rollback Deployment

List recent deployments

aws cloudformation describe-stacks --stack-name sgcarstrends-api-production

Rollback to previous version

npx sst deploy --stage production --rollback

Manual Rollback

  • Identify last good deployment

  • Checkout that commit

  • Redeploy

git log --oneline git checkout <commit-hash> npx sst deploy --stage production

Best Practices

Resource Naming

// ✅ Good - clear, scoped names new Function(stack, "ApiHandler", { ... }); new Bucket(stack, "UserUploads", { ... });

// ❌ Bad - generic names new Function(stack, "Function1", { ... }); new Bucket(stack, "Bucket", { ... });

Environment Management

// ✅ Good - environment-specific config const config = getConfigForStage(app.stage);

// ❌ Bad - hardcoded values const config = { memory: 1024 };

Outputs

// ✅ Good - add useful outputs stack.addOutputs({ ApiUrl: api.url, BucketName: bucket.bucketName, });

References

Best Practices

  • Use Stages: Separate dev/staging/prod environments

  • Secrets Management: Use SST secrets for sensitive data

  • Monitoring: Set up CloudWatch alarms

  • Cost Optimization: Use ARM, appropriate memory

  • Naming: Use clear, descriptive resource names

  • Outputs: Export useful values for reference

  • Removal Policy: Retain production data

  • Testing: Test deployments in staging first

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

framer-motion-animations

No summary provided by upstream source.

Repository SourceNeeds Review
General

shadcn-components

No summary provided by upstream source.

Repository SourceNeeds Review
General

api-testing

No summary provided by upstream source.

Repository SourceNeeds Review
General

design-language-system

No summary provided by upstream source.

Repository SourceNeeds Review