sst-deployment
About
This skill helps developers configure and deploy AWS infrastructure using SST v3. It's designed for adding new AWS resources, modifying Lambda functions, updating domain settings, and managing environment-specific configurations. The skill provides access to SST files in the `infra/` directory for deployment tasks across multiple environments.
Documentation
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
- SST Documentation: https://docs.sst.dev
- AWS CDK: https://docs.aws.amazon.com/cdk
- Related files:
infra/- All infrastructure codeinfra/CLAUDE.md- Infrastructure documentation- Root CLAUDE.md - Project documentation
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
Quick Install
/plugin add https://github.com/sgcarstrends/sgcarstrends/tree/main/sst-deploymentCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
sglang
MetaSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
evaluating-llms-harness
TestingThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
llamaguard
OtherLlamaGuard is Meta's 7-8B parameter model for moderating LLM inputs and outputs across six safety categories like violence and hate speech. It offers 94-95% accuracy and can be deployed using vLLM, Hugging Face, or Amazon SageMaker. Use this skill to easily integrate content filtering and safety guardrails into your AI applications.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
