Claude Code Plugins

Community-maintained marketplace

Feedback

Configure or update SST v3 infrastructure resources for AWS deployment. Use when adding new AWS resources, modifying Lambda configurations, or updating domain settings.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name sst-deployment
description Configure or update SST v3 infrastructure resources for AWS deployment. Use when adding new AWS resources, modifying Lambda configurations, or updating domain settings.
allowed-tools Read, Edit, Bash, Grep, Glob

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

  1. Identify last good deployment
  2. Checkout that commit
  3. 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

  1. Use Stages: Separate dev/staging/prod environments
  2. Secrets Management: Use SST secrets for sensitive data
  3. Monitoring: Set up CloudWatch alarms
  4. Cost Optimization: Use ARM, appropriate memory
  5. Naming: Use clear, descriptive resource names
  6. Outputs: Export useful values for reference
  7. Removal Policy: Retain production data
  8. Testing: Test deployments in staging first