# Deploying



`ccp deploy` bundles your code, uploads it, and activates a deployment on Clusterbase's serverless runtime.

## Deploy to Production [#deploy-to-production]

```bash
ccp deploy --prod
# ◼ Building Function...
# ◼ Creating Deployment...
# ◼ Uploading code...
# ◼ Deploying...
# ◼ my-app deployed!
# › https://my-app.clusterbase.dev
```

The `--prod` flag (alias: `--production`) makes this the active production deployment, served at `https://{function-name}.clusterbase.dev`.

## Preview Deployments [#preview-deployments]

Without `--prod`, you get a preview deployment with its own URL:

```bash
ccp deploy
# ◼ my-app deployed!
# › https://abc123.clusterbase.dev
```

Preview deployments are useful for testing changes before promoting to production.

## Promote a Preview [#promote-a-preview]

Promote any preview deployment to production:

```bash
ccp promote <deployment-id>
```

This swaps the production deployment to the given deployment ID without redeploying.

`promote` reads the linked App's identity directly — it doesn't require build
files or an initialized project. If the current directory isn't linked, it
fails immediately with an instruction to run `ccp link`. When the project has
a custom OIDC callback committed in `cluster.toml`, promotion still registers
it for redirect purposes.

## Undeploy [#undeploy]

Remove a specific deployment:

```bash
ccp undeploy <deployment-id>
```

## List Deployments [#list-deployments]

```bash
ccp list        # or: ccp ls
```

Inside a linked project, shows all deployments for that function with their IDs, status, and timestamps.

Run outside a project (or in one that hasn't been deployed yet) and `ccp ls` instead lists every function in the organization — resolve the org with `--org-id`, the project config, or the interactive picker. See [Functions → Managing Functions](/docs/ccp/functions#managing-functions).

## Deploy Options [#deploy-options]

| Flag                      | Description                                                                                                                                                                                                                                         |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--prod` / `--production` | Deploy as the production deployment                                                                                                                                                                                                                 |
| `--public-dir <path>`     | Include a public directory for static assets                                                                                                                                                                                                        |
| `--client <path>`         | Include a client-side script                                                                                                                                                                                                                        |
| `--org-id <id>`           | Organization ID — skips the org selection prompt                                                                                                                                                                                                    |
| `--app-id <id>`           | App ID — skips the App selection / creation prompt                                                                                                                                                                                                  |
| `-y` / `--yes`            | Skip prompts; if unlinked, create a Project + App named after the project — errors if that name is already taken, rather than attaching to it (single-org auto-picks; pass `--org-id` or set `CCP_ORG_ID` for multi-org)                            |
| `--register-redirect-uri` | Register this deploy's host as an OIDC redirect URI on the linked client. Production deploys already do this automatically; pass the flag to opt a preview deploy in (see [OIDC → Auto-register on Deploy](/docs/ccp/oidc#auto-register-on-deploy)) |
| `<path>`                  | Path to a file or directory (defaults to current directory)                                                                                                                                                                                         |

## How It Works [#how-it-works]

1. **Bundle** — your TypeScript/JavaScript is compiled into a single ESM bundle
2. **Upload** — the bundle (and any assets) are uploaded to Clusterbase's storage
3. **Activate** — Clusterbase publishes the new deployment to the runtime
4. **Route** — routes are created for `{function-name}.clusterbase.dev` and `{deployment-id}.clusterbase.dev`
5. **Serve** — the serverless runtime creates a V8 isolate and starts handling requests

## Client Bundle and CSS [#client-bundle-and-css]

When `client` is set (e.g. `src/main.tsx`) under `[serverless]` in
`cluster.toml`, that entry is
bundled for the browser and served at `/{stem}.js` (`main.js`). CSS imported
from the client (`import "./App.css"` — multiple files, `@import`, and
`*.module.css` all work) is bundled into a single `/{stem}.css` (`main.css`).

The server entry (your handler) may import CSS too — that's how server-side
rendering shares a component with the client. Stylesheets reached from the
handler are discarded (the client is the sole producer of `/{stem}.css`), so
import the same styles from the client entry, which SSR does naturally by
sharing the component. Two things to watch for:

* With **no** `client` entry there's nothing to serve the stylesheet, so a
  CSS import from the handler is still a deploy error.
* `*.module.css` imported from the handler only warns, rather than erroring:
  class names still resolve correctly, but the client and server bundles are
  built as separate graphs, so generated class names can disagree if two
  `*.module.css` files share a basename. Prefer plain CSS, or unique
  basenames, for components shared between server and client.

## First Deploy [#first-deploy]

On your first deploy, if `.ccp/config.json` has no App id, CCP resolves one for you:

* **Interactive:** you're prompted to select an organization, then link an existing App or create a new one.
* **Headless** (no TTY or `--yes`): CCP resolves an organization, then creates a same-named [Project](/docs/ccp/projects) and App. If an App with that name already exists in the organization, CCP **rejects the deploy** instead of attaching to it — names aren't identity, so a same-name collision (for example a renamed or re-cloned directory) requires explicit `--app-id` to select the intended App. The name comes from your project (its `package.json` `name`, else the directory). A single-org account needs no flags; with multiple orgs pass `--org-id` or set `CCP_ORG_ID` (otherwise it errors listing them).

Either way the App, Project, and organization ids are saved to `.ccp/config.json` for future deploys.

## Build Without Deploying [#build-without-deploying]

To test the build step without deploying:

```bash
ccp build
```

This runs the bundler and reports any errors, but doesn't upload or activate anything.

## Headless / CI Deploys [#headless--ci-deploys]

For CI pipelines and AI agents, set `CCP_HEADLESS=1` and deploy never blocks on a prompt. On a single-org account, the first deploy needs no flags at all — it creates a Project and App, and errors if that name is already taken:

```bash
export CCP_HEADLESS=1
ccp deploy --prod
```

With multiple organizations, name the target so CCP doesn't have to guess:

```bash
ccp deploy --prod --org-id "$ORG_ID"
```

Or export `CCP_ORG_ID="$ORG_ID"` once and every org-scoped command resolves the org without the flag.

To pin a **specific** App by id (for example after a directory rename, when the name no longer matches), pass both ids:

```bash
ccp deploy --prod --org-id "$ORG_ID" --app-id "$APP_ID"
```

After the first deploy the ids are written to `.ccp/config.json`, so subsequent deploys **on that machine** reuse them with no flags. That file is gitignored, so a fresh clone or a new CI runner won't have it — there, a bare deploy tries to create a Project and App with the project's name and fails if that name is already taken, since a name match is no longer treated as identity. Pass `--app-id` (and `--org-id`, or set `CCP_ORG_ID`) to select the original App explicitly. `ccp link` seeds the config without uploading code, when you want to link separately from the first deploy.

See [Headless Mode](/docs/ccp/headless) for the full reference.
