# Deployment

How Docup is deployed: the Worker with the cf CLI, bindings and secrets in code, and tasks on Trigger.dev.

Docup deploys as a Cloudflare Worker plus Trigger.dev tasks. The Worker's settings live in code, in `cloudflare.config.ts`.

## The Worker

`cloudflare.config.ts` defines the Worker with `defineConfig` and `defineWorker`:

| Setting | Value |
| --- | --- |
| Name | `docup` |
| Entry point | `./worker.ts` |
| Compatibility | Date `2026-10-21`, flag `nodejs_compat` |
| Custom domain | `docup.dev` |
| Route | `*/*` in the `docup.dev` zone, for docs sites and custom hostnames |
| `workers.dev` | Off. Preview URLs are on. |
| Placement | Region `aws:ap-south-1`, near the database |
| Observability | Logs, invocation logs, and traces persisted with head sampling rate 1, issues on |
| Assets | `htmlHandling` and `notFoundHandling` off, with the Worker running first for everything except `/_next/static/*`, `/*/_next/static/*`, `/favicon.ico`, `/logo.svg`, and `/marketing/*` |

A docs site needs the route in addition to the custom domain because a custom domain cannot be a wildcard. The `*/*` route pattern catches both `<address>.docup.dev` subdomains and custom hostnames used for docs sites.

## Bindings

| Binding | Type | Purpose |
| --- | --- | --- |
| `ASSETS` | Assets | Static files |
| `VERSION` | Version metadata | The id of the deploy, used in cache keys |
| `IMAGES` | Images | Image optimization |
| `ANALYTICS_ENGINE` | Analytics Engine dataset `docup-docs-analytics` | One data point per docs view |
| `SITE_FILES` | R2 bucket `docup-assets` | Uploaded logos, favicons and social images |
| `HYPERDRIVE` | Hyperdrive | Pooled Postgres connections. In development it points at the local Docker database. |
| Secrets | Secret | Everything in [Environment variables](/development/environment-variables) that the Worker reads |

## Deploying

The Worker deploys from CI with `cf deploy`, which is strict: it stops if the Worker's settings in the Cloudflare dashboard differ from `cloudflare.config.ts`. For a manual deploy, `pnpm run deploy` runs vinext's Cloudflare deploy.

> **Warning:** Never change Worker secrets, variables, bindings, routes or domains in the Cloudflare dashboard. A dashboard edit marks the Worker as edited, and the next CI deploy aborts with "Aborting the upload operation because of conflicts". Change settings in `cloudflare.config.ts` or with the `cf` CLI.

If that happens, run `pnpm exec cf deploy` once from a terminal and answer yes to "Would you like to continue?". That puts the Worker back to the repository's settings. The config lists the domain before the route, while Cloudflare returns the route first, so the two never match once the check runs.

## Secrets

Set each secret with the `cf` CLI. They are declared in the config as `bindings.secret()` and never stored in the repository. Tasks on Trigger.dev read their variables from the Trigger.dev dashboard instead.

## Trigger.dev

```bash
pnpm trigger:deploy
```

This publishes the tasks in `trigger/` to your Trigger.dev project. `trigger.config.ts` sets the task folder, the Node runtime, a hard stop of 3,600 seconds per task and default retries of three attempts with exponential backoff. The AI tasks override the default with a single attempt, because a retry would pay for every model call again.

## Database migrations

Run `pnpm db:migrate` against the target database before deploying code that needs a new column. See [Database](/development/database).

## DNS

The wildcard route works because `*.docup.dev` resolves to Cloudflare. Docs sites on a new address need no DNS change.
