# Flue deploy

> Choose Node or Cloudflare targets, register providers in `app.ts`, build artifacts with `flue build`, and apply blueprint-driven integrations.

- Repository: withastro/flue-with-vercel-eve
- GitHub: https://github.com/withastro/flue
- Human docs: https://www.grok-wiki.com/public/docs/withastro-flue-with-vercel-eve-f4b79875fff6
- Complete Markdown: https://www.grok-wiki.com/public/docs/withastro-flue-with-vercel-eve-f4b79875fff6/llms-full.txt

## Source Files

- `withastro-flue:packages/cli/src/lib/config.ts`
- `withastro-flue:packages/cli/src/lib/build.ts`
- `withastro-flue:examples/cloudflare/src/workflows/skills-from-r2.ts`
- `withastro-flue:examples/hello-world/src/app.ts`
- `withastro-flue:blueprints/database.md`
- `withastro-flue:packages/postgres/README.md`
- `withastro-flue:turbo.jsonc`

---

---
title: "Flue deploy"
description: "Choose Node or Cloudflare targets, register providers in `app.ts`, build artifacts with `flue build`, and apply blueprint-driven integrations."
---

Flue compiles discovered agents, workflows, and channels into a target-specific deployable artifact: a Node `server.mjs` bundle or a Cloudflare Workers output directory with a generated Wrangler config. The build target is mandatory (`node` or `cloudflare`), provider registration belongs in `app.ts` at runtime, and optional integrations (persistence, sandboxes, channels, observability) are added through blueprint guides fetched by `flue add` and `flue update`.

## Targets at a glance

Flue supports two build targets. Each target changes persistence, bundling, and how you ship the artifact.

| Concern | Node (`target: 'node'`) | Cloudflare (`target: 'cloudflare'`) |
| --- | --- | --- |
| Build output | `dist/server.mjs` (Vite SSR bundle) | `dist/<worker-name>/` with generated `wrangler.json` |
| Default persistence | In-memory SQLite per process | Durable Object SQLite (automatic) |
| Custom `db.ts` | Supported — wired at startup | Rejected at build time |
| Optional modules | `app.ts`, `db.ts` | `app.ts`, `cloudflare.ts` |
| Local one-shot runs | `flue run --target node` | Use `flue dev --target cloudflare` (no `flue run`) |
| Default listen port | `3000` (`PORT` env) | Wrangler dev on `3583` via `flue dev` |

<Tabs>
<Tab title="Node">

Node builds a single ESM server entry. Dependencies from your `package.json` are externalized — ship `node_modules` alongside `dist/server.mjs`. The generated server calls `migrate()` and `connect()` on a `db.ts` adapter when present; otherwise it uses built-in in-memory SQLite.

</Tab>
<Tab title="Cloudflare">

Cloudflare builds through the official Cloudflare Vite integration. Flue generates Durable Object classes, bindings, and a merged Wrangler config from your project-root `wrangler.jsonc`. You own the ordered migration history; Flue does not append migrations automatically. Deploy the **generated** config under `dist/`, not the source-root file.

</Tab>
</Tabs>

## Configure the build target

Set `target` in `flue.config.ts` or pass `--target` on every build command. There is no default — Flue throws if `target` is missing from both places.

<ParamField body="target" type="'node' | 'cloudflare'" required>
Build and development target. Required unless `--target` is passed to the CLI.
</ParamField>

<ParamField body="root" type="string">
Project root. Flue resolves authored source from `<root>/.flue/`, then `<root>/src/`, then `<root>/`. Defaults to the config directory.
</ParamField>

<ParamField body="output" type="string">
Build output directory. Defaults to `<root>/dist`. Must not equal `root` or the resolved source root.
</ParamField>

Precedence is inline CLI flags, then `flue.config.ts`, then defaults.

<CodeGroup>
```ts title="flue.config.ts"
import { defineConfig } from '@flue/cli/config';

export default defineConfig({
  target: 'cloudflare',
  root: '.',
  output: 'dist',
});
```

```bash title="CLI override"
flue build --target node --output ./build
```
</CodeGroup>

Scaffold a starter config with `flue init --target <node|cloudflare>`.

:::files
my-flue-app/
├── flue.config.ts          # build-time: target, root, output
├── wrangler.jsonc          # Cloudflare only: user-owned bindings + migrations
├── .env                    # Node local secrets (not bundled into build)
├── .dev.vars               # Cloudflare local secrets
├── src/                    # or .flue/ — first existing wins
│   ├── app.ts              # HTTP composition + provider registration
│   ├── db.ts               # Node only: PersistenceAdapter
│   ├── cloudflare.ts       # Cloudflare only: DO exports, scheduled handlers
│   ├── agents/
│   ├── workflows/
│   └── channels/
└── dist/                   # build output (default)
    ├── server.mjs          # Node target
    └── <worker-name>/      # Cloudflare target
        └── wrangler.json
:::

## Register providers in `app.ts`

Provider and model routing are **runtime** concerns. They do not belong in `flue.config.ts` — API keys often come from `process.env` or Cloudflare bindings, so registration runs when the built server starts.

Export a Hono app (or compatible `fetch` handler) from `app.ts`. Mount Flue routes with `flue()` and call `registerProvider()` before requests are served.

```ts
import { registerProvider } from '@flue/runtime';
import { flue } from '@flue/runtime/routing';
import { Hono } from 'hono';

// Brand-new provider for a local OpenAI-compatible server.
registerProvider('ollama', {
  api: 'openai-completions',
  baseUrl: 'http://localhost:11434/v1',
});

// Patch a catalog provider through a gateway.
if (process.env.ANTHROPIC_GATEWAY_URL) {
  registerProvider('anthropic', {
    baseUrl: process.env.ANTHROPIC_GATEWAY_URL,
    apiKey: process.env.ANTHROPIC_API_KEY,
  });
}

const app = new Hono();
app.get('/api/ping', (c) => c.json({ pong: true }));
app.route('/', flue());

export default app;
```

On Cloudflare, the build auto-registers the `cloudflare` AI binding provider when you have not already registered it. A user `registerProvider('cloudflare', …)` in `app.ts` wins because ESM import hoisting runs user code first. Use this to customize or disable the default AI Gateway.

Delete `app.ts` and the build falls back to a default Hono app that mounts `flue()` at `/` with no extras.

## Build deployable artifacts

`flue build` discovers modules under the source root, validates the target plugin, and writes artifacts to `output`.

<Steps>
<Step title="Verify discovery">

The build requires at least one agent or workflow under `agents/` or `workflows/`. It also discovers optional `channels/`, `app.ts`, `db.ts` (Node), and `cloudflare.ts` (Cloudflare).

</Step>
<Step title="Run the build">

```bash
flue build --target node
# or
flue build --target cloudflare
```

`flue build` loads project-root `.env` (or one `--env` file) for configuration and build-time evaluation. The built server reads only the environment supplied at start time — credentials are not packaged into the artifact.

</Step>
<Step title="Ship the output">

<Tabs>
<Tab title="Node">

```bash
set -a; source .env; set +a
node dist/server.mjs
```

The server listens on `PORT` (default `3000`). Do not set reserved CLI variables (`FLUE_MODE`, `FLUE_CLI_*`, `FLUE_INTERNAL_CLI_IPC`) in production.

</Tab>
<Tab title="Cloudflare">

```bash
npx wrangler deploy --dry-run --config dist/<worker-name>/wrangler.json
npx wrangler deploy --config dist/<worker-name>/wrangler.json
```

Configure secrets with `wrangler secret put` before deploy. Use the generated Wrangler config so bindings, entrypoint, and Vite output stay in sync.

</Tab>
</Tabs>

</Step>
</Steps>

### Cloudflare-specific authoring

Beyond `app.ts`, Cloudflare projects may include:

- **`cloudflare.ts`** — named exports become top-level Worker exports (application-owned Durable Objects). A default export object may define `scheduled` and other non-HTTP handlers. Do not export a `fetch` handler here.
- **`wrangler.jsonc`** at the project root — bindings, containers, R2 buckets, and **ordered** `migrations`. Append a uniquely tagged `new_sqlite_classes` entry whenever you add an agent or workflow class.
- **Per-module `cloudflare` export** on agents/workflows — `extend({ base, wrap })` from `@flue/runtime/cloudflare` for native Agents SDK lifecycle hooks or integration wrappers.

## Blueprint-driven integrations

`flue add` and `flue update` fetch Markdown implementation guides for a coding agent to apply in your project. Blueprints are not npm packages — the CLI prints the guide; the agent edits source files.

| Kind | Delivers | Primary file |
| --- | --- | --- |
| `sandbox` | Remote execution adapter | `sandboxes/<provider>.ts` |
| `channel` | Verified webhook ingress + client | `channels/<provider>.ts` |
| `database` | `PersistenceAdapter` (Node only) | `db.ts` |
| `tooling` | Observability, evaluation, etc. | Target-specific (e.g. `sentry.ts`) |

```bash
flue add                              # list available blueprints
flue add database postgres            # named blueprint
flue add sandbox daytona
flue add channel slack
flue add sandbox https://e2b.dev      # generic guide from URL
flue update channel slack             # refresh an existing integration
```

Named guides stamp generated files with a version marker:

```ts
// flue-blueprint: database/postgres@1
```

`flue update` compares your existing implementation against the current guide version and applies mechanical upgrades from the cumulative Upgrade Guide section.

### Target constraints for blueprints

- **Database blueprints** apply only to Node. Cloudflare uses Durable Object SQLite and rejects `db.ts` at build time.
- **Sandbox and channel blueprints** work on both targets but must verify signed payloads and bindings against your configured target.
- **Tooling blueprints** (for example Sentry) install target-specific SDKs — `@sentry/node` on Node, `@sentry/cloudflare` on Cloudflare.

For Postgres-backed durable state on Node:

```bash
flue add database postgres
```

Then default-export the adapter from source-root `db.ts`. `migrate()` runs once at server startup; `connect()` supplies execution, run, and event-stream stores.

## End-to-end deploy workflow

<Steps>
<Step title="Initialize">

```bash
npm init -y
npm install @flue/runtime valibot
npm install -D @flue/cli
flue init --target node   # or cloudflare
```

</Step>
<Step title="Author runtime surface">

Create agents and/or workflows under the source root. Add `app.ts` for custom routes and `registerProvider()` calls. On Cloudflare, configure `wrangler.jsonc` migrations before the first deploy.

</Step>
<Step title="Add integrations">

```bash
flue add database postgres    # Node: durable persistence
flue add channel github       # webhook ingress
flue add tooling sentry       # error reporting
```

Pipe blueprint output to your coding agent or use `--print` to inspect the raw Markdown.

</Step>
<Step title="Build and verify locally">

```bash
flue dev --target node          # watch + reload on port 3583
flue dev --target cloudflare    # Cloudflare Vite dev server
```

For production-style Node verification:

```bash
flue build --target node
node dist/server.mjs
```

</Step>
<Step title="Deploy">

Ship `dist/` to your host (Node) or run `wrangler deploy --config dist/<worker-name>/wrangler.json` (Cloudflare). Confirm HTTP routes: `/workflows/<name>`, `/agents/<name>/<id>`, `/channels/<name>`, and `/runs/<runId>`.

</Step>
</Steps>

## Troubleshooting

<AccordionGroup>
<Accordion title="Missing required target">

```
[flue] Missing required `target`. Set it via `--target <node|cloudflare>`
or in `flue.config.ts` as `target: "node"` (or `"cloudflare"`).
```

Set `target` in config or pass `--target` on every `flue build`, `flue dev`, `flue run`, and `flue connect` invocation.

</Accordion>
<Accordion title="db.ts on Cloudflare">

```
[flue] Custom persistence (db.ts) is not supported on the Cloudflare target.
```

Remove `db.ts` from the source root or switch the project to `target: 'node'` for external database persistence.

</Accordion>
<Accordion title="No agents or workflows found">

The build requires at least one file in `agents/` or `workflows/` under the resolved source root. Check that `root` and source-root precedence (`.flue/` vs `src/`) point at your authored modules.

</Accordion>
<Accordion title="Cloudflare migration errors">

Flue generates class names like `FlueTranslateWorkflow` with bindings `FLUE_TRANSLATE_WORKFLOW`. Keep deployed migration tags in order. When renaming generated classes, append `renamed_classes` entries — do not rewrite history that has already shipped.

</Accordion>
</AccordionGroup>

## Related pages

<Card href="/flue-configuration-reference" title="Flue configuration reference" icon="settings">
`flue.config.*` keys, source-root precedence, env loading, and CLI override rules.
</Card>

<Card href="/flue-cli-reference" title="Flue CLI reference" icon="terminal">
Commands, flags, and exit behavior for `flue build`, `flue dev`, `flue add`, and `flue init`.
</Card>

<Card href="/flue-project-layout" title="Flue project layout" icon="folder">
Source-root resolution, `app.ts` composition, and discovery boundaries.
</Card>

<Card href="/flue-persistence-reference" title="Flue persistence reference" icon="database">
`PersistenceAdapter` contract, built-in adapters, and `db.ts` wiring for Node deploys.
</Card>

<Card href="/flue-channels" title="Flue channels" icon="webhook">
Mount verified webhook routes and dispatch inbound events to agents.
</Card>

<Card href="/flue-examples" title="Flue examples" icon="code">
Target-specific fixtures including Cloudflare R2 hydration and provider registration.
</Card>
