# Deploying (/docs/composer/deploying)

> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely.

Location: Composer > Deploying

`deploy` takes your entry file (the one whose default export is the root module) and stands the whole app up on [Prisma Compute](https://www.prisma.io/docs/compute) and [Prisma Postgres](https://www.prisma.io/docs/postgres). You choose the target environment, production or an isolated **stage**, on the command line rather than in your code. Tearing an environment down is the `destroy` operation of the [control API](#driving-deploys-from-code).

| You want to...                    | Run                                                 |
| --------------------------------- | --------------------------------------------------- |
| Deploy to production              | `npx prisma@latest deploy module.ts`                |
| Deploy an isolated environment    | `npx prisma@latest deploy module.ts --stage <name>` |
| Deploy under a different app name | `npx prisma@latest deploy module.ts --name demo-42` |

## Credentials [#credentials]

On your machine, `deploy` uses the session stored by `auth login`, the same sign-in every other CLI command uses:

  

#### bun

```bash
bunx prisma@latest auth login
```

#### pnpm

```bash
pnpm dlx prisma@latest auth login
```

#### yarn

```bash
yarn dlx prisma@latest auth login
```

#### npm

```bash
npx prisma@latest auth login
```

In CI or any headless environment, use a service token instead. Set two environment variables:

* `PRISMA_SERVICE_TOKEN`: create a service token for your workspace in the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=composer).
* `PRISMA_WORKSPACE_ID`: in the workspace's settings.

A fresh checkout with just those two variables set deploys successfully. The CLI finds or creates everything else. Keep the values out of the repo (an `.env` you source at deploy time, or CI secrets). When `PRISMA_SERVICE_TOKEN` is set it takes priority over any stored session.

## Build first [#build-first]

`deploy` does not build for you. It assembles what your build produced:

  

#### bun

```bash
bun run build
bunx prisma@latest deploy module.ts
```

#### pnpm

```bash
pnpm run build
pnpm dlx prisma@latest deploy module.ts
```

#### yarn

```bash
yarn build
yarn dlx prisma@latest deploy module.ts
```

#### npm

```bash
npm run build
npx prisma@latest deploy module.ts
```

Deploy state records what is already provisioned, so a re-deploy applies the difference instead of recreating everything. The platform stores this state with the environment it describes, scoped to that environment's branch inside the app's project. It is not stored on your machine, so your laptop and CI see the same state. Concurrent deploys of the same environment take turns through a lease on that state. While one deploy holds the lease, a second deploy fails immediately with a message naming the holder. If a deploy crashes, its lease expires after about a minute, and the next deploy takes over.

## Production and stages [#production-and-stages]

Deploying with no `--stage` targets **production**. In platform terms, the app is a project (named after your root module), and production lives at the project level. The name is looked up in your workspace: a project this module deployed before is reused, and redeploying converges it. A project with the same name whose hosted state the CLI cannot identify or verify (one created outside this module's deploys, for example) stops the command with `HostedStateBootstrapError`. Use `--name` to deploy under a different name, or rename the module.

`--stage <name>` deploys a complete, isolated copy of the app as a [preview branch](https://www.prisma.io/docs/compute/branching) of that same project: its own services, its own databases, its own configuration. The only thing a stage shares with production is the code. Composer's *stage* and Compute's *preview branch* are the same object seen from two tools, which is why the stage's variables are the branch's [environment variables](https://www.prisma.io/docs/compute/environment-variables):

  

#### bun

```bash
bunx prisma@latest deploy module.ts                  # production
bunx prisma@latest deploy module.ts --stage staging  # a persistent staging environment
bunx prisma@latest deploy module.ts --stage pr-42    # one environment per PR
```

#### pnpm

```bash
pnpm dlx prisma@latest deploy module.ts                  # production
pnpm dlx prisma@latest deploy module.ts --stage staging  # a persistent staging environment
pnpm dlx prisma@latest deploy module.ts --stage pr-42    # one environment per PR
```

#### yarn

```bash
yarn dlx prisma@latest deploy module.ts                  # production
yarn dlx prisma@latest deploy module.ts --stage staging  # a persistent staging environment
yarn dlx prisma@latest deploy module.ts --stage pr-42    # one environment per PR
```

#### npm

```bash
npx prisma@latest deploy module.ts                  # production
npx prisma@latest deploy module.ts --stage staging  # a persistent staging environment
npx prisma@latest deploy module.ts --stage pr-42    # one environment per PR
```

Re-deploying any environment is idempotent: it updates the resources in place. A stage name must be a valid git ref name. An invalid name fails the deploy rather than being renamed.

## What a deploy prints [#what-a-deploy-prints]

A deploy ends by printing what it made, in your own terms: the names you authored, what each one became on the platform, and the public URLs:

```text no-copy
storefront-auth
├─ auth
│  └─ api   compute-service cps_abc123
│           https://xyz.ewr.prisma.build
├─ db       postgres-database db_def456
└─ web      compute-service cps_ghi789
            https://uvw.ewr.prisma.build
```

The tree is your module structure: `auth.api` is the `api` service inside the `auth` Module. Under each name is the platform resource it became and its ID, the thing to search for in the Console when you need it.

URLs appear for publicly reachable endpoints. That is why a Compute service prints one and a database does not: a database has a connection string, not a public endpoint. Secrets are left out entirely, so a node whose only output is a keypair prints no resource line. A node that deployed but published nothing reportable still appears in the tree, marked `(no entities reported)`.

## Removing resources [#removing-resources]

Your module is the source of truth for what exists. To remove a resource, delete its provision from the code and deploy again: the deploy compares the declaration against the environment's deploy state and deletes whatever is no longer declared, alongside any creates and updates the same deploy needs.

```text no-copy
Plan: 3 to update, 4 to delete, 1 to noop
[web2-deploy] deleted
[COMPOSER_WEB2_PORT-var] deleted
[COMPOSER_WEB2_ORIGIN-var] deleted
[web2-svc] deleted
```

A removed service takes its deployment and its wiring with it, and the resources that remain are untouched: a two-service app whose second service is deleted from the module keeps serving from the first without interruption.

Two things a deploy never removes:

1. **The last service.** An app is at least one service; a module that provisions nothing fails with `ASSEMBLE.SERVICE_MISSING` before anything runs, so you cannot empty an environment by emptying the module.
2. **The project and its branches.** Those are the containers an environment deploys into: created if absent on deploy, and never deleted by one. Tearing down a whole environment is the [`destroy` operation](#destroying); deleting an entire project is [`project delete`](https://www.prisma.io/docs/cli/project), which asks you to repeat the project id with `--confirm <project-id>` because it destroys the project's databases too.

## Destroying [#destroying]

The unified CLI has no `destroy` command. Tear an environment down with the `destroy` operation from `@prisma/composer/control` (see [Driving deploys from code](#driving-deploys-from-code)). It requires an explicit target, `{ kind: 'stage', stage }` or `{ kind: 'production' }`, and there is no default, so a teardown always names what it destroys. Unlike the CLI, the control API does not read the session stored by `auth login`. Export `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` (see [Credentials](#credentials)) before running the script, or it stops with `environment variable PRISMA_WORKSPACE_ID is required`:

```ts title="destroy-staging.ts"
import { destroy } from '@prisma/composer/control';

const result = await destroy({ entry: 'module.ts', target: { kind: 'stage', stage: 'staging' } });
if (!result.ok) throw new Error(`${result.failure.code}: ${result.failure.message}`);
```

If you deployed under a different name with `--name`, pass the same `name` here so the operation finds the right project.

Destroying a stage removes its resources, then deletes its branch along with the stage's deploy state. Destroying production removes the resources and, if the project is empty afterwards, deletes the project too, so empty projects do not pile up in your workspace. A project still holding another stage's resources stays in place. Tearing down a stage that was never deployed fails with "nothing deployed". It does not provision one first.

## CI [#ci]

On GitHub, connect the repository and add [prisma/cloud-deploy-action](https://github.com/prisma/cloud-deploy-action) to a workflow instead of scripting the deploy yourself. The action derives the target from the branch: the default branch deploys production, and every other branch deploys a stage named after it. Connected repositories authenticate through GitHub's OIDC tokens, without a stored secret. [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push) walks through the setup.

Any other CI runs the same commands as your machine: set the two credential variables as CI secrets, build, and deploy. The per-PR environment pattern:

  

#### bun

```bash
bunx prisma@latest deploy module.ts --stage "pr-$PR_NUMBER"    # on push
```

#### pnpm

```bash
pnpm dlx prisma@latest deploy module.ts --stage "pr-$PR_NUMBER"    # on push
```

#### yarn

```bash
yarn dlx prisma@latest deploy module.ts --stage "pr-$PR_NUMBER"    # on push
```

#### npm

```bash
npx prisma@latest deploy module.ts --stage "pr-$PR_NUMBER"    # on push
```

When the PR closes, tear the stage down with the `destroy` operation, targeting `{ kind: 'stage', stage: 'pr-<number>' }` from a small script.

One extra step applies if your app binds input fields with `envSecret` or `envParam` (see [Service input](https://www.prisma.io/docs/composer/service-input)). Each stage stores its own copy of those platform variables. The deploying shell only seeds them. A fresh stage, such as a new `pr-42`, starts with none of them, so CI must export the values alongside the two credential variables. The first deploy then copies any missing values from the shell up to the stage. If a name is absent from both the platform and the shell, the deploy fails early and names the missing variable.

## When a deploy stops on an effect version conflict [#when-a-deploy-stops-on-an-effect-version-conflict]

Before doing anything else, every Composer command verifies that the installed dependency tree gives the deploy engine the exact `effect` version `@prisma/composer` pins. When it does not, the command stops immediately:

```text no-copy
Error: Dependency conflict: alchemy resolves effect@<found>, but
@prisma/composer requires effect@<required>.
```

This happens when another dependency floats to a newer `effect` and your package manager hoists that copy where the deploy engine resolves it. The fix is what the error says: pin `effect` in your app's `package.json` to the exact version the error names, as shown in [Getting started](https://www.prisma.io/docs/composer/getting-started#1-set-up-the-project), and reinstall. The pin goes stale when you upgrade `@prisma/composer`; the error names the new version each time.

## When a deploy stops on a missing connection value [#when-a-deploy-stops-on-a-missing-connection-value]

A dependency's connection declares the values it needs, by name, and the node on the other end has to supply them. When one does not, the deploy stops and names the edge rather than standing the app up:

```text no-copy
Connection input "auth.db" declares param "url", but its producer "db" did not
supply it — the producer's outputs carry [host].
```

Fix it at whichever end is wrong: add the name to the outputs the producer returns, or mark the param `optional` on the connection if absent is a valid value (the consumer then reads `undefined`). Do not make the param optional just to route around the error: that reinstates a silent `undefined`. You will only meet this error if you authored the connection or an extension on one side of the wire. Every block that ships with the framework supplies what it declares.

## Production behavior [#production-behavior]

Behavior you will run into once the app is deployed, and what to do about it:

* **Compute scales to zero, and idle database connections get closed.** A long-lived client that treats a dropped connection as fatal will crash-loop through 502s. Keep the pool small and reconnect-friendly, and do not let an async error kill the process:

  ```ts
  const sql = new SQL({ url: db.url, max: 1, idleTimeout: 10 });
  process.on('uncaughtException', (err) => console.error('uncaughtException', err));
  process.on('unhandledRejection', (err) => console.error('unhandledRejection', err));
  ```

* **Bind `0.0.0.0`, not loopback.** Compute routes external HTTP to the VM; a `localhost` listener is unreachable from outside.

* **A deployed `/rpc/<method>` returns `401` to you.** Calls are [authenticated for you](https://www.prisma.io/docs/composer/services-and-contracts#calls-are-authenticated-for-you), and your `curl` is not one of the services the app connected. Reach it through a consumer instead.

* **Calls into a sleeping service can get `ECONNRESET`** while it cold-starts. Retry them; the generated RPC client already does.

* **The `COMPOSER_*` variables in your project belong to the deploy.** Config, secret pointers, and service keys all land there, and every deploy rewrites them. An edit made by hand does not survive the next deploy.

* **Streaming responses do not stream.** The platform's HTTP front door buffers a response until it completes, so an open SSE tail delivers nothing and times out at 60 seconds. Do not build on streamed HTTP responses.

* **Next.js pages that call `service.load()` need `export const dynamic = 'force-dynamic'`.** The runtime environment does not exist at build time, and Next.js will not re-read it for prerendered routes.

## Driving deploys from code [#driving-deploys-from-code]

Everything the CLI does is also callable in-process, from `@prisma/composer/control`: typed `deploy`, `destroy`, `dev`, and `log` operations that return structured results instead of printing and exiting. The operations and their result shapes are documented in [the control API](https://www.prisma.io/docs/cli/deploy#the-control-api) on the `deploy` command reference.

## Next steps [#next-steps]

* [Local development](https://www.prisma.io/docs/composer/local-development): the same pipeline, no credentials.
* [Service input](https://www.prisma.io/docs/composer/service-input): how per-stage configuration and secrets travel.
* [`deploy` command reference](https://www.prisma.io/docs/cli/deploy): every flag, plus the control API for destroy and log.

## Related pages

- [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together.
- [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself.
- [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model.
- [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma 8 contract with managed migrations.
- [`Getting started`](https://www.prisma.io/docs/composer/getting-started): Build a two-service Prisma App from an empty directory and run it on your machine with one command.