---
title: "Deployment"
description: "Production prerequisites, secrets, resources, and Worker domains."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.stackship.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment

StackShip deploys four Workers to the Cloudflare account that owns the
`stackship.run` zone.

## Domains

| Worker | Custom domain |
| --- | --- |
| `stackship-web` | `stackship.run` |
| `stackship-api` | `api.stackship.run` |
| `stackship-mcp` | `mcp.stackship.run` |
| `stackship-docs` | `docs.stackship.run` |

Wrangler custom domains create the required DNS records and certificates. A
hostname cannot already have a conflicting CNAME.

## Database

The PlanetScale PostgreSQL database is linked through Hyperdrive configuration
`397048f06dbc43c1a921c92258384a40`, bound to the API Worker as `HYPERDRIVE`.
Query caching is disabled on this configuration because it serves Better Auth
sessions, permissions, organizations, and other consistency-sensitive reads.

Do not use SQL comments as cache hints. Cloudflare does not document them as a
cache-control API. If product reads later benefit from caching, create a second
cache-enabled Hyperdrive configuration and keep identity traffic on this
cache-disabled binding.

The repository root contains an ignored `.env.migrations` file with the direct
administrative connection:

```dotenv
DATABASE_URL=postgresql://…
```

Migration commands load this file explicitly:

```sh
pnpm db:generate
pnpm db:migrate
```

Never point `.env.migrations` at Hyperdrive or commit it to source control.
PlanetScale's `sslrootcert=system` URL parameter is normalized in memory for
`node-postgres`; `sslmode=verify-full` remains enabled.

PlanetScale's stable `postgres` role must own the application tables and the
`drizzle.__drizzle_migrations` schema. The credential in `.env.migrations`
must inherit PlanetScale's `postgres` permission so Drizzle can perform DDL,
but the credential itself must not become the long-lived object owner.

If a Hyperdrive or other integration role bootstraps the schema, use
PlanetScale's **Reassign objects** action to transfer its objects to
`postgres` before running later migrations. Verify the transfer and run
`pnpm db:migrate` before rotating or deleting the original role.

## Secrets

Set Worker secrets without printing them:

```sh
wrangler secret put BETTER_AUTH_SECRET --config apps/api/wrangler.jsonc
wrangler secret put GOOGLE_CLIENT_ID --config apps/api/wrangler.jsonc
wrangler secret put GOOGLE_CLIENT_SECRET --config apps/api/wrangler.jsonc
```

## Deploy

```sh
pnpm install
pnpm check
pnpm build
pnpm deploy
```

The first basic deployment can ship public health surfaces before database and
identity credentials exist. In that state, auth returns `503` and MCP returns
`401`; neither service silently weakens its security posture.

## Production checks

- `GET https://api.stackship.run/health` checks Worker liveness and configured
  bindings.
- `GET https://api.stackship.run/ready` connects through Hyperdrive and checks
  for the core Better Auth tables. It returns `503` with
  `schema: migration_required` until the generated migration is applied.
- `GET https://mcp.stackship.run/health` checks MCP Worker liveness.
- `GET https://mcp.stackship.run/.well-known/oauth-protected-resource` returns
  the OAuth protected-resource metadata.

Source: https://docs.stackship.run/deployment/index.mdx
