This guide walks through bootstrapping a local DevStride development environment from a clean checkout. There is no single onboarding command that does this for you — it's a short, one-time sequence of shell config, AWS SSO setup, and a hand-filled .env file, followed by two commands (ds run backend and ds run ui) that you'll use every day after that.
ds setup, ds deploy, or similar all-in-one bootstrap command. Every ds invocation maps to one of seven Commander programs under cli/commands/ (run, migrations, data, golden, script, stripe, g) — see the Command Reference for the full list. Bootstrapping is the manual sequence below.| Tool | Purpose | Notes |
|---|---|---|
| Node.js 22 | Runtime for backend, frontend, and the CLI | Install via nvm: nvm install 22 && nvm alias default 22 && nvm use 22 |
| pnpm | Package manager for the monorepo | Pinned by the packageManager field in package.json — get the matching version via Corepack (bundled with Node 22): corepack enable |
| AWS CLI v2 | Required for AWS SSO auth | brew install awscli, plus pip3 install aws-sso-credential-process aws-export-credentials so SST can use SSO credentials |
| AWS SSO access | Every ds command (except when passed -u) authenticates against AWS SSO before running | Provisioned by a team lead — you'll get an email to set up SSO |
| Docker | Runs local DynamoDB for the backend test suite only | Not needed for local development — see the callout below |
ds run backend does not start Docker or any local services — it runs pnpm install, cleans up orphaned CloudFormation stacks, and then pnpm exec sst dev. In normal (non-test) code, DynamoDB access always points at real AWS. Docker/local DynamoDB is only spun up by the backend test suite itself (NODE_ENV=test), and only if you run cd backend && pnpm test. You don't need Docker running to develop locally — only to run tests.Do this once, in order, from the repo root.
Add to your shell config (e.g. .zshrc):
export DEVSTRIDE_STAGE=${your_name} # e.g. phil
export DEVSTRIDE_DEV_PROFILE=devstride-${your_name} # e.g. devstride-phil
export DEVSTRIDE_REGION=eu-central-1 # or whichever region is nearest you
export AWS_CONFIGURE_SSO_DEFAULT_SSO_START_URL=https://devstride.awsapps.com/start
export AWS_CONFIGURE_SSO_DEFAULT_SSO_REGION=us-east-1
sso(){
unset AWS_PROFILE
export AWS_PROFILE=$1
aws sts get-caller-identity &> /dev/null || aws sso login || (unset AWS_PROFILE && aws-configure-sso-profile --profile $1)
eval $(aws-export-credentials --env-export)
}
alias ds='./ds'
DEVSTRIDE_STAGE becomes the name of your own personal cloud stage — every developer deploys to their own isolated stage, keyed off this value.
Install the AWS CLI v2, then configure SSO with the same profile name as DEVSTRIDE_DEV_PROFILE:
aws configure sso
SSO start URL [None]: https://devstride.awsapps.com/start
SSO Region [None]: us-east-1
# after logging in via the browser:
CLI default client Region [None]: eu-central-1 # nearest region for you
CLI default output format [None]: json
CLI profile name [AWSAdministratorAccess-xxxxxxxxxxxx]: devstride-${your_name}
Then install the two helper packages SST needs to consume SSO credentials:
pip3 install aws-sso-credential-process
pip3 install aws-export-credentials
./ds wrapper runs aws sts get-caller-identity (falling back to aws sso login) before every command, unless you pass -u to skip auth. It also (re)generates a stage/region bind cache at .ds/bind/<stage>-<region>.env — pass -b to force a fresh bind even if a cached one exists. Usage: ds [-b] [-r] [-u] <command> [subcommand] [args].nvm install 22
nvm alias default 22
nvm use 22
corepack enable
pnpm install
.env fileCopy the sample and fill in real values by hand:
cp .env.sample .env
.env.sample ships with changeme placeholders — you get the real values from a teammate and paste them in yourself. Nothing in ds reaches into AWS Secrets Manager (or anywhere else) to populate your local .env..env.sample lists the following, all needing real values before the app will run correctly:
| Group | Variables |
|---|---|
| Domain | DOMAIN |
| Giphy | GIPHY_API_KEY |
| GitHub App | GITHUB_APP_CERT, GITHUB_APP_ID, GITHUB_APP_NAME, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_WEBHOOK_SECRET |
| Pusher | PUSHER_APP_CLUSTER, PUSHER_APP_ID, PUSHER_APP_KEY, PUSHER_APP_SECRET |
| Stripe | STRIPE_PUBLIC_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET |
| OpenAI | OPENAI_API_KEY |
| Database | DB_CONNECTION_STRING, DB_CONNECTION_STRING_READ_ONLY |
| Encryption | ENCRYPTION_KEY (see step 5) |
| SMTP (optional) | USE_SMTP, SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM |
ENCRYPTION_KEYENCRYPTION_KEY must be exactly 32 bytes (64 hex characters) — it's used to encrypt sensitive data such as database credentials in transit:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
Paste the result into .env as ENCRYPTION_KEY. Never commit this value — each environment should have its own unique key.
ds run backendds run backend
This is both how you run the backend day-to-day and how your personal SST stage gets created the first time — ds run backend runs pnpm install, cleans up any orphaned CloudFormation stacks, then runs pnpm exec sst dev, which provisions your stage's AWS resources on first run. Once it finishes deploying, stop it (Ctrl+C) — you need to run migrations and push config before it's actually usable.
ds migrations run
ds migrations run-sql currently does exactly the same thing as ds migrations run (both run Drizzle SQL migrations to the latest version) — it's a leftover alias from a historical split of the migration path. Either works; ds migrations run is the one documented going forward.ds script set-configds script set-config
This reads your local .env, validates that every required variable is set (DB connection strings, Stripe, OpenAI, GitHub App, Pusher, Slack, encryption key, and more), and pushes them up into an SST DEVSTRIDE_CONFIG secret (base64-encoded JSON) that your running Lambda / sst dev process reads at runtime.
ds script set-config is a one-way push from your local .env up to SST. It does not fetch or sync anything down. The running backend never reads .env directly at runtime — the CLI-side dotenv.config() only feeds the CLI/build-time process used to synth sst.config.ts; the deployed Lambda/sst dev process gets its secrets exclusively through the SST Config binding that this command populates. If you change a value in .env, you must re-run ds script set-config for it to take effect.Ask a teammate to set up a Stripe test account for you, then:
STRIPE_SECRET_KEY / STRIPE_PUBLIC_KEY in .env.https://api.{stage}.devstride.dev/v1/subscriptions/stripe/webhooks, listening for customer.subscription.created, customer.subscription.deleted, customer.subscription.trial_will_end, and customer.subscription.updated. Copy the signing secret into STRIPE_WEBHOOK_SECRET.ds script set-config so the updated values are pushed up.ds stripe add-products
Once bootstrapping is done, this is all you run day to day, in two terminal tabs:
ds run backend
ds run ui
ds run backend starts SST's live-lambda dev mode against your personal stage. ds run ui spawns the Vite dev server in frontend/, wired up with VITE_* env vars derived from your bound stage.
The backend test suite needs Docker for a local DynamoDB — this is the only place Docker is required:
docker pull amazon/dynamodb-local
cd backend && pnpm test
./ds resolves and runs a commandds command, organized by categoryIntroduction to the ds CLI
What the ds CLI actually wraps, how ./ds resolves and runs a command, the global auth/bind flags, and the seven real top-level commands.
Local Development
The real day-to-day dev loop: running the backend and UI, scaffolding CQRS code, migrations, and managing local data with the ds CLI.