AWS 上でフルスタック app を ship するには、AWS Console を truth source にしない方がよい。Frontend、secrets、domains、stage rules は TypeScript で app の横に置き、pull request でレビューし、すべての environment で同じ方法で適用する。
それが SST が提供するものである。app 全体をコードで定義する——通常は単一の sst.config.ts から——SST が underlying AWS resources を自動化する。このノートは本サイトの infrastructure と DevOps モデルを扱う:Bun + Turborepo monorepo、OpenNext 上の Next.js、local、shared preview、production の stages。
3 つの infra ストーリーは混同しやすいので、ここでは分けて扱う:
- 今日 deploy されているもの —
sst.config.tsは Next.js / OpenNext stack のみ import する。 - repo 内に定義済みだが未 import — container API infra(
Vpc→Cluster→Service+Router)はpackages/infraにあり、その stack を deploy すべきになるまでrun()からコメントアウトされたまま。 - 他の場所の関連パターン — Hono API 向け Lambda + API Gateway は Hono、Drizzle、Zod OpenAPI、SST による Backend API で扱う。本サイトの
sst.config.tsが実行している内容ではない。
このノートはこれらの境界を取り巻く infra レイアウトと DevOps loop を担当する。
1. この Stack で SST を選ぶ理由
SST は、低レベルの resource を手で組み立てずに、Components として app の機能を定義できるよう設計されている。この stack では次の 3 点が最も重要——SST が Pulumi や CDK for Terraform などの汎用 IaC ツールと比較する際に強調する点と同じ(SST FAQ):
- ここで使う高レベル components:
sst.aws.Nextjs、sst.Secret、(API module を接続するとき)sst.aws.Service、sst.aws.Router、sst.aws.Vpc、sst.aws.Cluster。 - Resource linking — runtime code が hardcoded names や ARNs ではなく typed
ResourceSDK 経由で infrastructure を読める。 sst devを統一ローカル environment として——infra watcher、同一 CLI からの frontend(および後の container)devprocesses、stage に Lambda handlers があるときの Live。
本サイトでは、選んだ AWS region 上の OpenNext 経由 Next.js、stage ベースの domains(example.com、dev.example.com、{stage}.example.com)、non-prod の edge Basic Auth secrets、application packages と packages/infra を分けた monorepo に対応する。
2. SST と他の IaC の比較
これらのツールは同じ目標を持つ:infrastructure as code。primary audience と日常の friction が異なる。SST 自身の位置づけが有用である:SST は developers 向け、CDKTF と Pulumi は主に DevOps engineers 向け。SST は open-source Pulumi と bridged Terraform providers を内部で使う。Pulumi account は不要で、app state はチームの cloud account に残る(SST docs)。
Terraform と Vercel は他プロジェクトでも有用である。この フルスタック AWS app では SST がデフォルトである。これは workflow の選択であり、すべての org が Terraform を捨てるべきだという主張ではない。
| Tool | Mental model | Strength | ここで SST が app stacks に合う理由 |
|---|---|---|---|
| Terraform / OpenTofu | Declarative HCL、provider 中心 | 汎用、成熟した state と workflow;org 全体の platform teams に強い | App と infra が分離;この monorepo 向け first-class Live、linking、Nextjs DX がない |
| AWS CDK | TypeScript constructs → CloudFormation | 深い AWS カバレッジと Constructs エコシステム | AWS-native modeling は強い;この workflow 向け統一 app loop(linking、sst dev、frontend + backend multiplexer)は弱い |
| Pulumi / CDKTF | 実言語による汎用 IaC | 柔軟な providers;platform team が program を所有するときに良い | SST は Pulumi 上に構築されつつ app components、linking、sst dev を追加;feature ごとに full Pulumi program は不要 |
| Serverless Framework | serverless apps 向け framework | 馴染みの Lambda packaging | ここで使う full-stack Next/OpenNext、containers、stage workflows より狭い |
| Vercel / PaaS | Git push → hosted platform | managed frontend だけなら高速 DX、AWS surface が小さい | managed frontend のみなら強い;AWS resources、domains、secrets、optional ECS/API を TypeScript で所有したいときは SST |
表の外:SST components は common app patterns をエンコードし、低レベル制御が必要なら transform と 150+ providers も使える。Linking は Terraform や CDK が手書き env vars と IAM で埋める runtime gap を閉じる。sst dev は自分で組む local stack ではなく product feature である。SST にも state がある——local と backup bucket——managed resources を Console で手編集すべきではない(Basics)。
platform team が Terraform や OpenTofu で shared networking を既に所有している、Pulumi や Terraform が multi-cloud control planes の team language である、CloudFormation-native CDK construct libraries が hiring と review の surface である場合、SST は poor fit になりうる。その場合でも SST は app delivery にはよく機能し、org レベル IaC はチームが既に leverage を持つ場所に残す。
3. プロジェクト形状:sst.config.ts と packages/infra
SST は drop-in mode——Next.js app の横に単一 sst.config.ts——と monorepo——config を root に置き infrastructure を modules に分割——をサポートする。このプロジェクトは monorepo 形状を使う。
sst.config.ts app name, region, protect / removal, imports
packages/infra/ Next.js, secrets, domain, edge, optional API/VPC
apps/web Next.js site
apps/api Hono / Mastra API
packages/* shared auth, db, content, design systemconfig ファイルは app identity と safety rules を定義する。run function が infra modules を load する。今日 import されているのは Next.js のみ。API module はその stack を deploy すべきになるまでコメントアウト:
export default $config({
app(input) {
return {
name: "my-app",
removal: input?.stage === "production" ? "retain" : "remove",
protect: ["production"].includes(input?.stage),
home: "aws",
providers: {
aws: {
region: "us-east-1",
},
},
}
},
async run() {
// await import("./infra/api")
await import("./infra/nextjs")
},
})packages/infra は compose される pieces を保持する:
nextjs— OpenNext deployment と domains(今日 import)secrets— edge Basic Auth credentials など stage secrets、API path 向け database と auth secretsdomain/edge— apex domain と non-prod edge Basic Authapi/router/vpc/cluster— container API path、定義済みだが未 import
business logic はこれらのファイルから除外する。Infra modules は resources を create と link する。application packages が consume する。
4. この Stack の Components
OpenNext による Next.js
サイトは sst.aws.Nextjs で deploy する。production は apex domain。他 stage は {stage}.example.com。production は warm server instance を 1 つ。non-prod は warm: 0。edge prop で non-prod Basic Auth。dev block は sst dev が local Next process をどう起動するかを指示する。
const isProd = $app.stage === "production"
const domain = "example.com"
export const nextjs = new sst.aws.Nextjs("Web", {
path: "apps/web",
domain: {
name: isProd ? domain : `${$app.stage}.${domain}`,
redirects: isProd ? [`www.${domain}`] : [],
},
warm: isProd ? 1 : 0,
environment: {
NEXT_PUBLIC_SITE_URL: `https://${domain}`,
SST_STAGE: $app.stage,
},
openNextVersion: "4.0.3",
// buildCommand: sync assets, then OpenNext build
edge,
dev: {
command: "bun run dev",
directory: "apps/web",
url: "http://localhost:3000",
},
})Secrets
Secrets は first-class resources であり、deployment model に入らない loose .env ではない。Edge Basic Auth は non-prod で username/password secrets。Database と auth secrets は API path 用に定義:
export const username = new sst.Secret("Username")
export const password = new sst.Secret("Password")
export const databaseUrl = new sst.Secret("DatabaseUrl")
export const betterAuthSecret = new sst.Secret("BetterAuthSecret")
export const aiGatewayApiKey = new sst.Secret("AiGatewayApiKey")Container API(定義済み、未 import)
API module を import すると path は Vpc → Cluster → Service、Router で api.example.com または {stage}.api.example.com。Database、Better Auth、AI Gateway secrets が service に link される。sst.config.ts で await import("./infra/api") を有効にするまで、その stack は deploy されない。
別形状——Lambda authorizer 付き API Gateway の背後の Lambda——は Hono、Drizzle、Zod OpenAPI、SST による Backend API のパターン。sst.aws.Function と sst.aws.ApiGatewayV2 を使う。Hono API 向けの related SST approach であり、本サイト config が現在 load している infrastructure ではない。
5. Hardcoded Env の代わりに Linking
Resource linking は infrastructure と runtime の橋である。resource を create し、link で渡し、SDK で読む。Linking は values を inject し、types(sst-env.d.ts)を生成し、component がサポートする範囲で permissions を付与する。
本サイトでは今日、Next.js stack が edge configuration 経由で edge Basic Auth credentials(username/password secrets)を pull する。web app 自体は database 用に Resource.* を読まない。
API module を import すると、secrets が container service に link され、application code は次のように読める:
import { Resource } from "sst"
const client = postgres(Resource.DatabaseUrl.value, {
ssl: "require",
})frontend では linked values は server 側のみ。client components が見えるのは明示的に渡したものだけ。
stage secrets が必要な CLI——Drizzle migrations、Better Auth schema generation——には sst shell が linked stage environment を load し、2 つ目の secrets path を発明しない。
6. DevOps Unit としての Stages
SST では stage は environment:app の namespaced copy。本サイトの practical map は次のとおり:
| Stage | Purpose | Domain pattern |
|---|---|---|
local / personal | 日常の sst dev | local Next URL;その stage の cloud resources |
dev | Shared preview | dev.example.com |
production | Live site | example.com |
production は protected。誤った sst remove で critical data を消さないよう、config は protect と production で removal: "retain" を使う。SST 推奨 template pattern と一致(removal policies)。
non-production stage には practical ops detail もある:CloudFront Basic Auth を edge viewerRequest injection で、preview URL をデフォルトで public にしない。
日常コマンドは短い:
aws sso login # refresh AWS credentials
sst dev --stage local # personal / local stage
sst deploy --stage dev # shared preview
sst deploy --stage productioncredentials は local AWS credential chain(profile または SSO session)に残る。SST はその account と region に deploy する。Next.js と secret resources は Console をクリックして手作業では作らない。
7. ローカル Workflow:sst dev、Live、Mode Discipline
sst dev は local control plane である。本サイトの wired Next.js stack では日常価値がある部分は:
- infrastructure changes を deploy する watcher。
- Next.js
devblock——local Next、stage の deployed resources に link。 - 後で API module を import すると、container
devcommand と optional tunnel で VPC resources へ。
Live——AWS の stub Lambda が local machine に proxy——は Lambda-heavy stages で最も重要。backend APIs ノートの Function / API Gateway pattern を含む。本サイトが OpenNext のみ import している間は less central だが、deployment shape によって OpenNext server paths も Lambda を含みうる。
sst dev は personal または local stage 向け。shared dev と production は sst deploy。同一 stage を Live stub と real deploy で flip すると遅く混乱する:CLI が kill されると stub が残り、remote invoke が local machine を待って timeout する(Live quirks)。
shared URL は real stage を deploy する意味。Lambda handler を breakpoint で debug するなら personal stage で Live——Node handlers の debug には VS Code Auto Attach。
8. Production Safety と CI Boundary
IaC は state と process が honest なら役に立つ。
- Console-edit しない SST が manage する resources。SST は config から state への diff を apply する。手編集は state を drift させる(Basics)。
- production を protect —
protectとremoval: "retain"で accidental production remove 時に SST-managed stateful resources を消さない。API path の Postgres はsst.aws.Postgresではなくsst.Secret経由の external URL。 - verify してから CI で deploy。 GitHub Actions は install、lint、format check、typecheck、tests、build を先に実行。checks 成功後のみ workflow が target stage(
devまたはproduction)でsst deploy。localdeploy:dev/deployscripts は intentional one-off deploy に有用だが、default path は CI:green checks、then deploy。
SST Console は git-push autodeploy、preview environments、monitoring の別経路。本サイトは verify-then-sst deploy sequence に GitHub Actions を使い、その loop に Console を必須にしない。
tenant isolation と SaaS boundary design は別問題——Hono、Better Auth、Drizzle、Postgres RLS による Multi-Tenant Backend を参照。
9. Mental Model
sst.config.ts
→ packages/infra/nextjs (imported today)
→ Nextjs / OpenNext + edge Basic Auth secrets
→ packages/infra/api (defined, not imported)
→ Vpc / Cluster / Service / Router + linked secrets
→ link (when a consumer is linked)
→ Resource.* in app runtime
→ sst shell for CLIs
stages: local | dev | production
local: sst dev
ci: checks → tests → build → sst deploy --stage …operating loop は小さく保つ:
- infra TypeScript または application code を変更。
- iterate 中は personal stage で
sst dev。 - pull request を開く。GitHub Actions が checks、tests、build を実行。
- 成功時、CI が target stage(
devまたはproduction)にsst deploy。
Final Thoughts
SST は AWS infrastructure を app の横で review 可能な TypeScript に保つ。Components は common shipping features をエンコードする。Linking は env-var と IAM glue の class を除去する。Stages は environments を first-class DevOps unit にする。sst dev は local stack を 1 コマンドに畳む。
Terraform、CDK、Pulumi は org-wide platform ownership や team が既に standardize している問題には better fit。OpenNext 上 Next.js、stage-based domains、secrets、必要になれば container または Lambda API path には SST が strong default——developer loop を最適化し、resource graph だけではない。
Hono API on Lambda または Fargate——authorizer wiring、WAF、alarms 含む——は Hono、Drizzle、Zod OpenAPI、SST による Backend API を続ける。current component と CLI details は SST docs から。