Search "deploy an AI app" and you get Kubernetes, inference servers, and GPU autoscaling. That is the machinery for running model weights.
If you are calling a hosted model API — and you almost certainly are — none of that is your problem. You need a frontend, a server-side function that holds the key, and a stream that survives every hop.
As of July 2026, the production site runs that shape on Cloudflare Workers through OpenNext. Netlify is the managed preview-and-functions path in this guide, not the production request path. The cost calculator still estimates the token bill. This post gets the prototype onto a URL.
This guide is the hosting layer of the AI App Stack. For the platform comparison behind it, see Best Hosting Platforms for AI Apps, then model the closest decision on Netlify vs Vercel.
Most teams are deploying an app, not a model
Every AI product has two layers, and they have completely different deployment stories.
The model layer is where inference happens: GPU memory holding weights, an inference server batching requests, autoscaling that has to deal with cold starts measured in tens of seconds, quantization decisions, KV-cache management. If you use OpenAI, Anthropic, Google, Mistral, DeepSeek, or any inference provider serving open-weight models, this layer is their problem. You interact with it through an HTTPS endpoint and an API key, and the hardest infrastructure problems in AI are somebody else's pager.
The app layer is everything your users actually touch: the frontend, auth, your data, and, critically, the server-side routes that hold your API key and relay requests to the model. This is a web application. It deploys like a web application. The model dependency changes two runtime constraints: the response must stream end to end, and the request window must outlast generation. Three operational checks complete the host decision: server-side secrets, reviewable previews, and an entry tier whose limit behavior you understand.
The decision rule is short:
- Calling a model API? You are deploying an app layer only. Any modern web platform can host it. The rest of this post is your checklist.
- Self-hosting open-weight model weights? You are also deploying a model layer, which means renting GPUs. That's a real option with real economics (we built a self-host cost calculator precisely because the break-even math is unintuitive), but it's a cost/privacy/control decision you make deliberately, not a default you drift into.
Roughly speaking: if you don't have a concrete reason to hold the weights (compliance, unit economics at serious sustained volume, latency control, air-gapped environments), the API is the right call and your deployment just got an order of magnitude simpler. The teams that get this backwards spend their first quarter building inference infrastructure for a product that doesn't have users yet.
The production shape is boring on purpose
A 2026 AI app has a boring, repeatable shape:
- A frontend: static pages or a server-rendered framework (Next.js, Astro, SvelteKit, plain Vite + React). Nothing AI-specific about deploying this. It goes to a CDN like every other frontend since 2018.
- Server-side functions: the routes that read the API key from the environment, call the model, and return results. This is the only architecturally AI-ish part of the stack, and it's maybe forty lines of code.
- A streaming path: users will not stare at a spinner for 20 seconds while a long generation completes. Tokens must flow to the browser as they're produced, and every hop between the model and the user has to cooperate.
- Sometimes, a data layer: a vector store for RAG, a database for chat history, session state. All of it available as managed services your functions call.
- Sometimes, background work: embedding pipelines, batch jobs, agent runs that outlive a request/response cycle.
The first rule of the app layer, and the one that gets violated constantly: the model call never happens in the browser. Anything shipped to the client is public, including your API key. A key scraped from a JS bundle becomes someone else's free inference until your card gets declined; there are scrapers that do nothing but crawl deployed bundles looking for exactly this. Every model call routes through a function you control, where the key lives in an environment variable and where you can rate-limit, log, and cap spend.
The second rule follows from the first: because all model traffic flows through your functions, those functions are your control point for everything: usage metering, abuse prevention, prompt versioning, model fallbacks. Treat that thin proxy layer as a real component, not glue code.
Streaming and duration fail late
Not every web host can run this shape well. Before you commit, verify two AI-specific runtime constraints and three operational requirements, in order of how painful they are to discover late:
1. End-to-end streaming. This is the sneaky one. Your function can stream perfectly and the platform's proxy layer can still buffer the whole response before releasing it, turning your live token stream into a long blank pause followed by a wall of text. Streaming has to survive every hop: function runtime, CDN, edge network. It also has to finish inside the host's duration limit. Netlify's modern streaming Functions and beta Lambda-compatible response-streaming path are both capped at 10 seconds. Buffered standard Functions run for 60 seconds. Next.js adapters and Edge Functions use distinct runtime paths, so test the generated route with a real slow response rather than a hello-world on localhost.
2. Function timeouts that match generation reality. A long completion from a large reasoning model can run well past 10 seconds. Extended thinking modes can run minutes. Know the synchronous limit and whether the platform counts CPU time or wall-clock duration. Streaming reduces the blank wait but does not override the documented cap. Background jobs fit work measured in minutes when the user does not need one live response.
3. Server-side secrets. Environment variables scoped to functions, not baked into the client bundle. Table stakes, but check how preview and branch deploys handle secrets too: you want your production key in exactly one deploy context, and low-limit test keys everywhere else. A platform that copies all env vars into every PR preview is quietly multiplying your attack surface by the number of open branches.
4. Preview deploys. AI apps need more iteration than most software. Prompts are code that you tune by feel, and "does this feel better?" is a question you answer by sharing a link, not by describing a diff. A URL per branch or PR, with the functions actually running against a test key, is the difference between "tweak the prompt, push, share the link" and a staging-server bottleneck that makes prompt iteration a scheduled event.
5. An entry tier whose failure mode you understand. Netlify Free includes 300 monthly credits and pauses projects at the limit. Cloudflare Workers Free allows 100,000 requests per day with 10 milliseconds of CPU per invocation. Vercel Hobby is limited to personal, non-commercial use. "Free" is not one architecture. Check the quota and what happens after it.
Start static-plus-functions
Within the app-layer world, you have three real choices. All three deploy to the same class of platform.
Static frontend + serverless functions. The frontend is fully pre-built at deploy time; every dynamic AI interaction goes through a function. This is the simplest, cheapest, and fastest-to-first-byte option for a chat interface, document analyzer, or generation tool. The production site uses the same shape: statically generated pages with Workers handling the dynamic edges.
Server-rendered app (SSR). Next.js or SvelteKit rendering pages on demand, with AI calls in route handlers. Choose this when the page content itself is personalized or model-generated per request, not merely when your framework's marketing suggests it. SSR adds a server hop to every page view; take that cost only where you use it.
Edge functions for the latency-sensitive path. Edge runtimes put your proxy code in the region closest to the user. For LLM apps this matters less than people assume (generation time dwarfs network time), but it matters for the first token and for lightweight pre-processing (auth checks, rate limiting, routing between models). A sensible pattern: rate-limit and authenticate at the edge, do the actual model call in a standard function.
If in doubt, start with the first option. Every AI product I've seen ship fast was static-plus-functions. The ones that stalled reached for heavy infrastructure before they had a user to serve with it.
Production runs on Workers. This is the preview path.
The production target is Cloudflare Workers through OpenNext. Static pages, scheduled work, and data APIs ship to that Worker; Netlify is not in the request path. Cloudflare's official Next.js guide documents the same adapter.
Partner disclosure: the Netlify link below is a partner link. It does not affect model rankings, platform coverage, or this guide's limits.
If you want the same static-plus-functions shape with a managed build and branch-preview workflow, Netlify is the implementation covered below. Connect a repository, add secrets, and deploy the function with each branch. Netlify Free includes 300 monthly credits. The example uses Netlify's modern Functions API, not its Lambda-compatibility layer. Returning the upstream stream gives this route a 10-second execution limit, so it fits short responses rather than extended reasoning. A framework-generated Next.js route or Edge Function must be checked against its own runtime limit.
The streaming proxy as a Netlify Function is short:
// netlify/functions/chat.mjs
export default async (req) => {
const { messages } = await req.json();
const upstream = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY, // set in the Netlify UI, never in code
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: process.env.ANTHROPIC_MODEL,
max_tokens: 512,
stream: true,
messages,
}),
});
// Preserve errors instead of turning every upstream response into a 200 event stream.
const contentType = upstream.headers.get("content-type") ?? "application/octet-stream";
return new Response(upstream.body, {
status: upstream.status,
statusText: upstream.statusText,
headers: { "content-type": contentType },
});
};
export const config = { path: "/api/chat" };
That's the whole trick. The browser calls /api/chat on your own domain, the key never leaves the server, and chunks render as they arrive. The response also preserves Anthropic's status and content type, so a JSON 401, 429, or 5xx does not masquerade as a successful event stream. Set ANTHROPIC_MODEL to a currently supported model ID rather than baking a release name into the function. Swap the endpoint and headers for another provider. The proxy shape is the same, but request fields and event formats are not guaranteed to be identical.
- Connect the repo. Push to Git, connect the repo in the dashboard, and the framework is auto-detected, build command and publish directory included for Next.js, Astro, SvelteKit, and friends. No Dockerfile, no YAML.
- Set your secrets. Add
ANTHROPIC_API_KEYandANTHROPIC_MODELas environment variables. Scope the production key to the production context; give previews a separate key with a low spend limit so a leaked preview URL can't hurt you. - Push. Every push builds and deploys; every pull request gets its own preview URL with functions live. Prompt-tuning becomes "push branch, send link, get a yes/no in five minutes."
- Add guardrails before you share the URL. Rate-limit the function (by IP or user), set a max token budget per request, and set a hard spend cap on the provider side. An AI endpoint without limits is a public faucet wired to your credit card.
The setup is short. The acceptance test is not: run a response that is as slow as your real workload. If it crosses the deployed route's limit, move the synchronous route to a longer-lived runtime or turn the task into a background job.
RAG adds a service, not a new deploy
The most common "but my app is more complicated" objection is retrieval-augmented generation, and it's the objection that dissolves fastest under inspection. A RAG app adds exactly three pieces, and all three fit the same architecture:
- The vector store is a managed service: Pinecone, Supabase pgvector, Upstash Vector, Turso, take your pick. Your functions query it over HTTPS with a key from the environment, exactly like the model API. You don't host it, the same way you don't host the model.
- The query path (embed the user's question, retrieve the nearest chunks, stuff them into the prompt) is a few extra awaits inside the same function that was already calling the model. Latency budget: one embedding call plus one vector query, both fast relative to generation.
- The ingestion pipeline (chunking and embedding your corpus) is the one genuinely new piece, and it's a background job, not a service. Run it as a scheduled function for periodic refreshes, or trigger it on content changes. (If your corpus lives on other people's websites, that's an extraction problem. We wrote up how we run that pipeline for our own pricing and catalog data.)
If someone tells you RAG means you've outgrown serverless, they're describing 2023.
Minutes need a queue, not a longer function
Requests that finish in seconds are the easy case. Two workloads don't fit it:
Batch work: re-embedding a corpus, generating summaries for a thousand documents, nightly evaluation runs. These belong in a background job triggered on a schedule or event. Netlify documents background functions that run for up to 15 minutes, return 202 immediately, and do not stream. The pattern to avoid is chaining synchronous functions to dodge timeouts. It works until it doesn't, and it fails invisibly.
Agent runs: multi-step tool-using sessions that might run for minutes and need to survive their own failures. The honest 2026 answer is that a long agent run wants a queue and a worker, which is one managed service more (Upstash QStash, Inngest, Trigger.dev and similar all speak "call my function later" natively). The function receives a job, does one agent step, persists state, enqueues the next step. Each step stays inside serverless limits. The run as a whole can go as long as it needs. Short agent loops can fit in one streamed request when the runtime limit has been measured against the real loop. Do not add the queue until a run hits that limit.
Notice what's still not on the list: your own servers.
The first week breaks the same five ways
Having watched a lot of these ship (and shipped a few), the failure modes are remarkably consistent:
1. Buffered streaming. Covered above, worth repeating: test streaming on the deployed URL, not localhost. If the first token doesn't render until the last token is generated, some layer is buffering, and your users' first impression is a 25-second blank screen.
2. Timeout kills mid-generation. Long completions die at the platform's synchronous limit and the user sees a truncated answer or a 502. Streaming improves perceived latency. It does not erase the documented runtime limit. Shorten the request, choose a longer-lived runtime, or move the job to a queue and poll or push the result.
3. The leaked key. VITE_- and NEXT_PUBLIC_-prefixed variables ship to the browser by design; that's what the prefix means. If your provider key ever had one of those prefixes, even briefly on a preview deploy, rotate it today. Set provider-side spend caps as the backstop for the leak you haven't noticed yet.
4. Unbounded spend. Token costs scale with usage patterns you don't control once the URL is public. One user pasting entire books into your chat box, or one scraper hammering the endpoint overnight, can cost more in a weekend than hosting costs in a year. Rate limits per user, max_tokens caps per request, provider spend limits per key. All three, before launch, not after the invoice.
5. No fallback for provider incidents. Model APIs have bad days. Every provider's status page has a history tab for a reason. A try/catch that degrades to a smaller or alternate model (the leaderboard is useful for picking a fallback in the same capability class) keeps your product up while a provider is down. Even a canned "generation is degraded right now" state beats a raw 500.
A sixth, quieter one: shipping with no observability. Log every model call server-side (model, token counts, latency, truncated prompt hash) from day one. When a user reports "it gave me a weird answer yesterday," that log is the difference between a fix and a shrug. Your function proxy is the natural place for it, which is one more reason the browser-calls-the-API shortcut costs more than it saves. The LLM observability tools guide separates tracing, evaluation, and prompt-management needs before you add another production dependency.
Self-hosting does not rewrite the app layer
Sometimes self-hosting is right. Open-weight models have closed most of the capability gap in several categories, and at sustained high volume the per-token math can flip in favor of rented GPUs. Run your own numbers in the self-host calculator before trusting anyone's blog post, including this one. Privacy and compliance can also force the issue regardless of economics.
When that's your path, the surprising part is that the app-layer guidance above doesn't change at all. Your frontend and functions still deploy exactly as described. The only difference is that the endpoint your function calls is your own inference server on a GPU box instead of a provider's API. The two layers stay cleanly separated, which is precisely why it pays to build them that way from day one. You can start on a hosted API this week, prove the product, and swap the model layer later by changing one URL in one function.
The number to watch is first-token time on the deployed URL, timed against the route's documented cap. Netlify's modern streaming Functions stop at 10 seconds. A localhost stream that dies in production is not a hosting mystery. It is a route you have not measured.
Pick the model on the leaderboards, estimate tokens with the cost calculator, then run one slow request on a real URL. The host that survives that request is the one you chose. Netlify remains the managed preview path in this guide. (Partner link; the disclosure above applies.)
Reader questions
Frequently asked questions
01Do I need a GPU to deploy an AI app?
Usually not. If the product calls a hosted model API from OpenAI, Anthropic, Google, Mistral, or an inference provider, that vendor runs the GPUs. Your app is a frontend plus server-side functions. Rent GPUs only when you self-host open-weight weights, which is a cost, privacy, or control decision rather than a default.
02What is the difference between deploying an AI model and deploying an AI app?
Deploying a model means operating the weights: GPUs, an inference server, autoscaling, and cold starts measured in tens of seconds. Deploying an AI app means shipping UI, auth, data, and routes that call a model endpoint someone else operates. Most teams need the second. It is a standard web deployment around a hosted inference endpoint.
03Can you run an LLM app on a serverless platform?
Yes. Static or server-rendered pages go to a CDN, while functions hold the model key and relay responses. Netlify's modern streaming Functions and beta Lambda-compatible path both stop at 10 seconds. Buffered standard Functions run for 60 seconds. Next.js adapters and Edge Functions use distinct paths, so test the exact deployed route.
04How do you stream LLM responses to the browser?
Enable streaming on the model API from a server-side function, then forward chunks to the client as server-sent events or a streamed fetch response. Every hop — function runtime, CDN, proxy — has to pass those chunks through as they arrive. A buffering layer turns a live stream into a long blank wait followed by a wall of text.
05How much does it cost to host an AI app?
The app layer can start on a free tier, but each vendor draws the line differently. Netlify Free includes 300 monthly credits, Cloudflare Workers Free allows 100,000 daily requests with 10 milliseconds of CPU per invocation, and Vercel Hobby is non-commercial. For many early products, model tokens remain the larger variable cost.
06Where should I put my model API key in a deployed app?
In the host's environment variables, readable only by server-side functions. Never put it in client-side code. Anything in the browser bundle is public, and a scraped key becomes someone else's inference until the spend cap hits. Rotate any key that was ever exposed, and set a provider-side spend limit as a backstop.
07How do you deploy a RAG application?
The same way you deploy any AI app, plus one managed service. A vector store such as Pinecone, Turso, Supabase pgvector, or Upstash is queried over HTTPS from your functions. Embedding calls share that same proxy. Ingestion runs as a scheduled or background job. RAG does not require leaving a serverless platform.
Source ledger
External sources linked in this article
- 01modern streaming Functionsdocs.netlify.com
- 02Lambda-compatible response-streaming pathdocs.netlify.com
- 03official Next.js guidedevelopers.cloudflare.com
Continue with live BenchLM data
Share or save