- Published on
- •
How to Get Workflows Off Desktops and Into Production Using n8n, OpenRouter, and Fly.io
- Authors

- Name
- John Moscarillo
The gap between "a workflow that works on my machine" and "a workflow my business runs on" isn't an AI model problem. It's an infrastructure problem.
You've built something genuinely useful in a Claude Project, a Cowork session, or a Gemini notebook. A research pipeline that synthesizes fifty papers into a briefing. A document generator that turns messy client intake into polished proposals. A client-assistant that actually understands your domain.
It's brilliant. And it's trapped.
It lives in one person's browser tab. It depends on them manually re-running steps, copy-pasting between windows, remembering the exact prompt sequence. It can't be scheduled. Can't be shared safely with a colleague or client. Has no audit trail, no error handling, no uptime guarantee. When that person goes on vacation, the workflow goes with them.
This isn't a "which AI tool is best" problem. The fix is to separate the workflow logic from the chat interface it was prototyped in. The three-legged stool: n8n for orchestration, OpenRouter for model routing, Fly.io for hosting and data — plus a private React Router v7 frontend to make it usable without exposing internals.
From Notebook Prototype to Workflow Spec
The translation step is the one people skip. Before touching n8n, extract the actual logic from the chat-based prototype: what are the discrete steps, what's the input, what's the output, where did you manually intervene (that's a node), where did you copy-paste between tools (that's an integration point). A practical technique is to walk back through a Claude Project or Cowork session and literally list each tool call and decision point in order — that ordered list is your first n8n workflow diagram. The mindset shift matters: a chat thread is conversational and forgiving of ambiguity; a workflow needs explicit branching, error states, and retries. This is where "it worked when I did it" prototypes reveal hidden assumptions. Gemini notebooks fit the same pattern — code cells become HTTP Request or Code nodes.
Why n8n as the Orchestration Layer
n8n is the durable, schedulable, observable replacement for "a human clicking through steps in a chat window." Visual workflows are easier to hand off and maintain than a script only the original author understands. Built-in triggers — webhook, schedule, form, email — replace manual kickoff. Native error workflows and retry logic handle the failures that chat interfaces silently swallow. Self-hostable means the logic and credentials stay inside your infrastructure, not a third-party SaaS. The migration pattern is straightforward: one node per step you identified above, credentials stored in n8n's credential store (not hardcoded), and a webhook or scheduled trigger replacing "I open the chat and paste in today's data." Every run is logged — the audit trail chat-based tools don't give you.
Why OpenRouter for Model Routing
Decouple "which AI model" from "how the workflow is built." Instead of hardcoding a single provider's API into every node, you call OpenRouter once and choose the model per task. Cost/speed/capability routing means cheap fast models (smaller tiers) for classification, extraction, and formatting steps, with stronger reasoning models only for steps that actually need them. This is where most production workflow cost savings come from — most steps don't need your most expensive model. Fallback and reliability matter too: OpenRouter's model fallback means a provider outage or rate limit doesn't take down the whole workflow, mapping directly to an n8n retry/error path. The business-use-case differentiator is privacy: OpenRouter's privacy controls and access to models under protected/zero-retention terms mean you can route sensitive business data through models with contractual data handling guarantees. You're not assuming every model provider treats your prompts the same way. The workflow can touch client or business-confidential data because you've chosen models and settings that don't retain or train on it. Practically, this is a single credential in n8n (one HTTP Request or dedicated node) that gives you dozens of models — versus maintaining separate API keys and integrations per provider.
Why Fly.io + Postgres + Your Own Backups
n8n needs somewhere durable to live that isn't your laptop. Fly.io is fast to deploy, runs containers close to users, and is straightforward for a small ops team without a full platform engineering org. Postgres as n8n's backend database (vs. default SQLite) matters once you have real concurrent workflows and history you care about — durability, query-ability, the ability to inspect execution history at scale. Ownership matters: because it's your Fly.io instance and your Postgres, you're not dependent on a SaaS vendor's retention policy, pricing tier, or uptime SLA for your own workflow data. Backups are the "own your recovery" argument: scheduled Postgres dumps shipped to your own S3 bucket (or S3-compatible storage), separate from Fly's infrastructure and separate from n8n's own persistence. If Fly has a bad day, or you make a bad deploy, you can restore. pg_dump + a cron/scheduled job + S3 lifecycle rules for retention — this is insurance. This ties back to the OpenRouter privacy point: hosting your own data layer is the other half of "this is now private." The workflow's execution history and business data sit in infrastructure you control, not just the model calls.
The Frontend: React Router v7 as a Safe Front Door
n8n's editor and webhook URLs are great for building, but you don't want business users hitting raw n8n webhooks directly, and you definitely don't want n8n's UI exposed to the public internet. The solution is a React Router v7 (Remix) app, backed by its own Postgres (or sharing the instance), as the actual interface non-technical users and clients interact with — forms, dashboards, status views. The connection to n8n happens without exposing it: the Remix app's server-side loaders and actions call n8n's webhooks internally (private networking within Fly.io, e.g. Fly's internal 6PN network or an internal-only app). n8n is never reachable from the public internet — only the Remix app is. When you do need to expose a specific n8n webhook externally (e.g., a third-party service triggering a workflow), that's a deliberate, narrow exception rather than the default. This matters practically: better UX than n8n's own forms, proper auth and access control for who can trigger what, and a clean separation of "internal automation plumbing" from "thing people actually use." This is the piece that makes the system feel like a real product instead of exposed infrastructure.
Putting It Together: A Reference Architecture
The request path is simple: user → Remix app (public-facing, on Fly.io) → internal call to n8n webhook (private networking) → n8n workflow runs, calling OpenRouter for whichever model fits each step → results written to Postgres and/or returned to the Remix app → Postgres backed up on a schedule to S3. What this buys you versus the original notebook or chat prototype: scheduling, auth, cost control, data privacy guarantees, disaster recovery, and a real interface — all things a chat window never gave you.
Getting Started
Pick one existing Claude Project, Cowork, or Gemini workflow to migrate first — not your most complex one. Map its steps before opening n8n. Stand up n8n and Postgres on Fly.io. Wire one OpenRouter credential and assign models per step by cost and complexity. Build the thinnest possible Remix front end (even a single form) before adding polish. Set up the S3 backup job on day one, not after something breaks. This isn't about abandoning the tools where ideas start. It's about giving the good ones a place to actually live.