Deploy to AWS with Pulumi
Generate, preview, apply, inspect, and destroy Pulumi stacks from the checked application graph.
Deploy an application after its local checks and production build pass. Pulumi is the deployment engine and AWS is the first host integration, but engine, host, application resources, access, and runtime wiring remain separate roles. See supported providers for the full coverage table and local Docker for the default development and container verification path.
Pre-setup
Start from an existing application that passes its local module setup. Install the host/engine below, configure deployment roles, and choose a separate provider profile for each production resource. A local-only Docker profile cannot supply a production connection.
Supply application secrets through the deployment’s runtime environment and declare required
application fields in defineEnv. Supply AWS credentials and Pulumi backend access to the deployment
process through their native credential mechanisms; do not add them to the app’s public environment
or bundle. Run bun run relkit doctor --pulumi to check local tooling before selecting a stack.
Run a built application
Run bun run build in the development installation first. The build publishes the executable
.relkit/build/server/index.js, its graph and manifests, public assets, and any selected immutable
job workers. Copy the complete .relkit/build directory into the same relative path in your
production application root.
The build’s package.json declares exact observed versions of selected runtime integrations and
their required SDKs. Required dependency patches and overrides travel with this manifest; keep the
complete output, including patches/ when present. Install it before starting from the application root:
cd .relkit/build
bun install --production --network-concurrency 1
cd ../..
NODE_ENV=production bun run --no-env-file .relkit/build/server/index.js
The framework runtime is bundled. This production installation does not need the development CLI,
compiler, Inspector application, or unselected SDKs. relkit start remains a convenience command
where the CLI is installed; the generated start script uses the JavaScript command above.
Supply required application environment values through the process environment. Startup does not
automatically read dotenv files, and build output does not inline their values. PORT overrides the
configured listener port. Run from the application root so default local provider state remains at
.relkit/state; public assets are resolved beside the built server. Keep existing state and
immutable job generations when replacing build output.
The generated Dockerfile uses .relkit/build as its build context. It installs the selected production
manifest and copies the complete server, public assets, graph, and selected worker history. Environment
files, existing local state, and temporary build module links are excluded from that context.
Install deployment tooling for an existing app
Cloud and deployment default to none. The --cloud aws --deploy pulumi creation tuple is
currently rejected before staging because public certification is incomplete. Configure deployment
only on an existing app; installing these packages does not make that creation tuple available.
From its development root, install the selected host/engine and deploy-only SDK peers:
From this guide’s application/package root, install only its selected requirements:
bun add --dev @pulumi/aws@7.43.0 @pulumi/pulumi@3.259.0 @relkit/aws@0.8.1 @relkit/pulumi@0.8.1
Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in dependencies; build/type tooling belongs in devDependencies. Installing an adapter does not supply its optional SDK peers or enable the feature’s configuration.
These development tools generate/preview infrastructure. They do not belong in the built server’s production dependency closure, and none of these installation steps provisions cloud resources.
Configure deployment roles
Merge the role imports and deployment selection into your existing relkit.config.ts.
Retain its application ID, environment contract, and provider profiles. This minimal complete
example selects the host and engine; it does not create any resources during import:
/** Select AWS hosting and Pulumi without provisioning resources during import. */
import "@relkit/aws";
import "@relkit/pulumi";
import { defineApp, defineEnv } from "@relkit/app/config";
export default defineApp({
env: defineEnv({}),
deployment: { engine: "pulumi", host: "aws" },
});
The complete commerce configuration also binds storage, caching, jobs, and telemetry explicitly.
Keep the existing application’s resources and runtime integrations separate from these deployment roles. Checking and building provision nothing.
Preview before applying
From that existing configured project, after authorization for the selected account/stack:
bun run check
bun run test
bun run build
bun run relkit doctor --pulumi
bun run relkit deploy init --stack development
bun run relkit deploy preview --stack development
Use a separate stack for each isolated environment. Supported backends are Pulumi Cloud, local state, and object
storage URLs. Use repeatable --config key=value options for non-secret values and --config-secret key=value
for Pulumi secrets.
Preview generates and validates the Pulumi program from the graph. Review create, update, replace, and delete operations before applying:
bun run relkit deploy up --stack development
bun run relkit deploy outputs --stack development
--non-interactive (alias --yes) approves protected operations for automation; it does not replace review or
authorization.
Understand resource ownership
An infrastructure wrapper such as aws(s3(...)) or aws(redis(...)) may contribute supported lifecycle and
least-privilege access operations. A configured adapter passed directly to defineApp contributes runtime wiring
but no resource lifecycle. docker(adapter) is development-only and never becomes cloud infrastructure.
Job deployment publishes the immutable task worker and registers schedules only after readiness. Keep the previous worker generation and its routing keys while it owns accepted runs; deployment does not rewrite run history or turn a provider without native support into a durable fallback.
The AWS host owns ECS/Fargate hosting, networking, the workload role, load balancing, and CloudWatch routing for redacted structured stdout. It does not implicitly own application jobs, events, model providers, Sentry, or OTLP.
Reconcile and clean up
For an isolated temporary stack:
bun run relkit deploy refresh --stack development
bun run relkit deploy destroy --stack development
Confirm the stack before destruction and verify cleanup independently. Never edit the generated Pulumi program
to bypass graph or role validation; repair configuration or integration support, run check, and preview again.