Configuring Build Targets for Node.js and Cloudflare Workers Deployment in Flue
Flue compiles agent projects to either Node.js (generating dist/server.mjs) or Cloudflare Workers (generating dist/worker.mjs plus wrangler.jsonc) using the --target flag, while sharing identical source code and project layout between both runtimes.
When deploying applications built with the withastro/flue framework, selecting the appropriate build target determines your runtime environment and scaling characteristics. Configuring build targets for Node.js and Cloudflare Workers deployment requires understanding how Flue adapts the same agent source code into distinct artifacts optimized for traditional servers versus serverless edge functions.
Build Target Overview
Flue supports two primary compilation targets controlled via the --target CLI flag:
| Target | Output Artifact | Runtime | Best For |
|---|---|---|---|
| Node.js | dist/server.mjs |
Hono server on Node.js | Self-hosted services, Docker containers, VPS, Railway |
| Cloudflare Workers | dist/worker.mjs + dist/wrangler.jsonc |
Cloudflare Workers with Durable Objects | Edge functions, ultra-low latency APIs, per-session isolation |
Both targets consume the same project layout—agents live under ./agents, ./roles or ./.flue/agents, ./.flue/roles—and accept identical environment variable configurations via --env <path>.
Configuring the Node.js Build Target
The Node.js target compiles your Flue application into a standalone Hono server that listens on process.env.PORT (defaulting to 3000).
Project Initialization
Initialize a Node project and install the SDK and CLI:
mkdir my-flue-server && cd my-flue-server
npm init -y
npm install @flue/sdk valibot
npm install -D @flue/cli
Create an agent under .flue/agents/:
// .flue/agents/translate.ts
import type { FlueContext } from '@flue/sdk/client';
import * as v from 'valibot';
export const triggers = { webhook: true };
export default async function ({ init, payload }: FlueContext) {
const harness = await init({ model: 'openai/gpt-5.5' });
const session = await harness.session();
const { data } = await session.prompt(
`Translate this to ${payload.language}: "${payload.text}"`,
{ schema: v.object({
translation: v.string(),
confidence: v.picklist(['low','medium','high'])
}) }
);
return data;
}
(Reference: docs/deploy-node.md lines 31-52)
Local Development
Run the development server with hot reloading on port 3583:
npx flue dev --target node --env .env
The dev server implements the same Hono-based routing found in packages/sdk/src/runtime/flue-app.ts, exposing health checks at GET /health, manifest at GET /agents, and invocation endpoints at POST /agents/:name/:id.
Production Builds
Compile for production:
npx flue build --target node
Load environment variables before starting the compiled server:
set -a; source .env; set +a
node dist/server.mjs
The server automatically binds to the port specified in process.env.PORT.
Containerization
Deploy using Docker by wrapping the built artifact:
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist/ ./dist/
ENV PORT=8080
EXPOSE 8080
CMD ["node", "dist/server.mjs"]
(Reference: docs/deploy-node.md lines 90-103)
Configuring the Cloudflare Workers Build Target
The Cloudflare target generates a Worker script compatible with Durable Objects, R2 buckets, and optional sandbox containers.
Project Setup
Install the Workers SDK alongside Flue packages:
mkdir my-flue-worker && cd my-flue-worker
npm init -y
npm install @flue/sdk valibot agents
npm install -D @flue/cli wrangler
Agent code remains identical to the Node.js target, though you may reference Cloudflare-specific bindings:
import type { FlueContext } from '@flue/sdk/client';
export default async function ({ init, env }: FlueContext) {
// Access Cloudflare bindings through env
const harness = await init({
model: 'anthropic/claude-sonnet-4-6',
// Additional Cloudflare-specific configuration
});
// ...
}
Development with Wrangler
The dev server proxies to a local Wrangler runtime:
npx flue dev --target cloudflare --env .env
As implemented in the CLI, this command launches the Wrangler dev environment on port 3583 with automatic reloading on source changes.
Building and Deploying
Generate the Worker artifact:
npx flue build --target cloudflare
This creates dist/worker.mjs and merges your root wrangler.jsonc with Flue's internal Durable Object bindings into dist/wrangler.jsonc.
Deploy via Wrangler:
npx wrangler deploy --secrets-file .env
(Reference: docs/deploy-cloudflare.md lines 91-96)
Advanced Bindings
For knowledge bases using R2 storage, mount a virtual sandbox:
import { getVirtualSandbox } from '@flue/sdk/cloudflare';
import type { FlueContext } from '@flue/sdk/client';
export default async function ({ init, payload, env }: FlueContext) {
const sandbox = await getVirtualSandbox(env.KNOWLEDGE_BASE);
const harness = await init({
sandbox,
model: 'openrouter/moonshotai/kimi-k2.6'
});
const session = await harness.session();
return await session.prompt(
`Search the knowledge base for "${payload.query}" and summarise.`
);
}
Declare R2 buckets and Durable Objects in your root wrangler.jsonc; Flue automatically merges these declarations during the build process.
(Reference: docs/deploy-cloudflare.md lines 86-97)
Shared Configuration Interface
Both targets respect the same configuration surface in flue.config.ts or via CLI flags:
- Target specification:
--target node|cloudflareorexport default defineConfig({ target: 'node' })as shown inexamples/hello-world/flue.config.ts - Environment loading:
--env .envinjects variables before build or runtime - Output directory:
--output path/to/outor theoutputconfig field - Project root: Automatically detects
.flue/subdirectories or bareagents/folders
Universal Agent Patterns
The following agent works identically under both targets, with model selection abstracted via environment variables:
import type { FlueContext } from '@flue/sdk/client';
import * as v from 'valibot';
export const triggers = { webhook: true };
export default async function ({ init, payload }: FlueContext) {
const harness = await init({
model: process.env.DEFAULT_MODEL ?? 'openai/gpt-5.5',
});
const session = await harness.session();
const { data } = await session.prompt(
`Translate this to ${payload.language}: "${payload.text}"`,
{ schema: v.object({
translation: v.string(),
confidence: v.picklist(['low','medium','high'])
}) }
);
return data;
}
Run this unchanged with flue dev --target node or flue dev --target cloudflare.
Summary
- Choose Node.js (
--target node) for self-hosted deployments requiring traditional process-based execution via Hono server (packages/sdk/src/runtime/flue-app.ts) - Choose Cloudflare Workers (
--target cloudflare) for edge deployment with automatic Durable Object support and optional R2 bindings - Both targets read from identical source layouts in
./agentsor./.flue/agents - Always pass sensitive credentials via
--env .envrather than committing them to version control - The Cloudflare target automatically merges user-defined bindings from
wrangler.jsoncinto the generateddist/wrangler.jsonc
Frequently Asked Questions
How do I switch between Node.js and Cloudflare Workers in the same project?
Change the --target flag when running flue dev or flue build. The same agent source code compiles to both targets without modification, though Cloudflare-specific features like env.KNOWLEDGE_BASE will only function in the Workers environment.
Where does Flue store the compiled output for each target?
Node.js builds generate dist/server.mjs (a Hono application), while Cloudflare builds create dist/worker.mjs alongside a merged dist/wrangler.jsonc configuration. Control the output directory using the --output flag or output field in flue.config.ts.
Can I use Durable Objects when developing locally?
Yes. When running flue dev --target cloudflare, the CLI proxies to Wrangler's local dev environment, which simulates Durable Objects and other Cloudflare platform features. The Node.js target does not support Durable Objects, as it runs a standard Hono server without Cloudflare's infrastructure.
How are environment variables handled differently between targets?
Both targets load variables from the file specified by --env. In Node.js, these become process.env variables. In Cloudflare Workers, Wrangler injects them as bindings accessible via the env object passed to FlueContext, matching standard Workers runtime behavior.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →