Environments & configuration files
Every blogwright command runs against one environment — a name that selects the config file, is stamped into every AWS resource name, and keys the deployment state. Two environments in the same AWS account never share a bucket, a role, or a state file, so you can run production and staging side by side without them touching each other.
Choosing an environment
Section titled “Choosing an environment”The environment defaults to production. Pass another one as the positional [env] argument or with --env:
blogwright deploy # productionblogwright deploy staging # positionalblogwright deploy --env staging # flag — wins over the positionalblogwright status staging # every environment-scoped command takes [env]rollback and logs take a <hash> first, then the optional environment:
blogwright rollback 4f3a2b1c stagingblogwright logs 4f3a2b1c stagingEnvironment names must be lowercase alphanumeric with dashes (^[a-z0-9-]+$); anything else is rejected when the CLI derives resource names, before anything in your account is created or modified.
How the config file is resolved
Section titled “How the config file is resolved”Config lives in the consuming site’s repo, not in blogwright. The CLI first finds the repo root by walking up from the invocation directory to the nearest .git (a directory, or a file in worktrees) or .jj directory — so every command works from any subdirectory of the site. If neither exists above the current directory, the command fails with an error naming the start directory.
From the repo root, the config is loaded from the first candidate that exists:
config/<env>.jsonc— the per-environment file (blogwright initwritesconfig/production.jsonc)ops.config.jsonc— a single shared fallback
When you pass --config <path>, that file is the only candidate — there is no fallback. If no candidate exists, the command fails with an error listing the paths it looked for.
Files are JSONC: // and /* */ comments and trailing commas are allowed. A file that exists but fails to parse is a hard error, never skipped. Only siteName is strictly required (region defaults to us-east-1); everything else has defaults — see the configuration reference for the full schema.
One config for every environment
Section titled “One config for every environment”Because every environment falls back to ops.config.jsonc, a single shared file can serve them all — the environment name still shapes resource names, tags, and behavior. Per-environment files exist for the settings that genuinely differ, such as domain.
What an environment owns
Section titled “What an environment owns”Each environment gets one S3 bucket named <env>-<siteName>-<accountId>, holding the build artifacts, the live site, and the state. Every other derived resource name carries the same environment and site name — the build and exec roles, the builder MicroVM image, the log groups — so environments are fully isolated stacks in one account. The layout is described in the architecture reference.
The topology state lives at state/<env>.json inside the environment’s own bucket. Keeping it in S3 rather than on disk means every machine with credentials — your laptop, a colleague’s, CI — sees the same single source of truth for what has been provisioned.
Setting up a staging environment
Section titled “Setting up a staging environment”Reuse the same siteName — the environment prefix keeps every resource apart. A minimal staging config:
{ "region": "us-east-1", "siteName": "example", // same slug as production — the env prefix isolates resources}Then bootstrap and deploy it exactly like production, with the environment name appended:
blogwright bootstrap stagingblogwright deploy stagingblogwright status stagingAdd a domain to the staging config to give it its own hostname — see custom domains. To let CI deploy staging on pushes to main via a GitHub OIDC role, set githubRepo and see CI deploys with GitHub OIDC.
Resource tagging
Section titled “Resource tagging”Every AWS resource blogwright creates is tagged with two keys:
environment— the environment name.app— the explicitappconfig option, falling back to the site’s domain (from--domainorconfig.domain), then to the repo directory name. The fallback chain guarantees the tag is always something a human can trace back to the project from a billing or resource listing.
Site files get the same two tags as S3 object tags on upload. Preview deploys stamp the PR id into the object tags (environment: preview-pr-42), so per-PR cost and cleanup queries work — see PR previews.