Skip to content

How to ship a Koa app on Vercel

Deploy a Koa app to Vercel with zero configuration. Learn how to ship from the Vercel CLI or Git, and configure response streaming, middleware, cron jobs, and Observability.

6 min read
Last updated June 15, 2026

Koa is an expressive HTTP middleware framework for building web applications and APIs with Node.js. Built by the team behind Express, it uses async functions and a small, cascading middleware model, with no middleware bundled in core, so you add only what you need.

On Vercel, you can deploy a Koa app with zero configuration: your application runs as a single Vercel Function on Fluid compute, and you get response streaming, preview deployments, Instant Rollback, and observability without extra setup.

This guide walks you through deploying a Koa app to Vercel from the command line or a Git repository, then configuring features such as streaming, middleware, cron jobs, and observability.

Before you begin, make sure you have:

  • A Vercel account
  • Node.js 20+ and a package manager (e.g., npm)
  • An existing Koa app, or a new one you create from scratch
  • A Git repository on GitHub, GitLab, or Bitbucket (if you want Git-based deployments)
  • Vercel CLI installed (npm i -g vercel)

When you deploy a Koa app, Vercel detects the framework and builds it for the Vercel runtime. Your application becomes a single Vercel Function that runs on Fluid compute by default, so it scales with traffic, and you pay only for the compute your function uses, not for idle time.

Because Vercel ships zero-configuration detection for Koa, you don't set a build command or output directory. Vercel reads your project, finds your server entry point, and applies the correct build settings.

Koa doesn't have a starter template on Vercel, so you deploy either a new app you create from scratch or an existing one. Both paths use zero configuration.

Set up a minimal Koa project, then deploy it with the Vercel CLI.

  1. Create a project and install Koa and its router:
    Terminal
    mkdir my-koa-app && cd my-koa-app
    npm init -y
    npm install koa @koa/router
  2. Add a server entry point at src/index.ts:
    src/index.ts
    import Koa from 'koa';
    import { Router } from '@koa/router';
    const app = new Koa();
    const router = new Router();
    router.get('/', (ctx) => {
    ctx.body = { message: 'Hello from Koa!' };
    });
    app.use(router.routes());
    app.use(router.allowedMethods());
    app.listen(3000);
  3. Develop locally with the Vercel CLI, so your app runs the same way it does in production:
    Terminal
    vercel dev
  4. Create a preview deployment. The first run creates a Vercel project link:
    Terminal
    vercel
  5. Promote your deployment to production:
    Terminal
    vercel --prod

If you already have a Koa app, deploy it from Git or from the command line.

From Git: Push your project to GitHub, GitLab, or Bitbucket, then import it at vercel.com/new. Vercel detects Koa automatically and deploys it with zero configuration.

From the CLI: From your project's root directory, run vercel to create a preview deployment, then vercel --prod to go live. To pull project settings and environment variables for local development, run:

Terminal
vercel link
vercel env pull

For Vercel to detect your app, use a recognized server entry point. Name your entry file app, index, or server (with a .ts, .js, or related extension) at your project root or under src/. Keep the call to app.listen(), and let Vercel use your entry point to build and serve the app as a Vercel Function:

src/index.ts
import Koa from 'koa';
import { Router } from '@koa/router';
const app = new Koa();
const router = new Router();
router.get('/', (ctx) => {
ctx.body = { message: 'Hello from Koa!' };
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);

After your app is deployed, you can layer Vercel features onto it. Some work automatically, and others take a few lines of code or configuration.

When you deploy a Koa app, Vercel turns the whole application into a single Vercel Function. Every incoming request flows through your Koa middleware stack and router inside that function, so your middleware, routes, and error handling all run in one place.

This function uses Fluid compute by default, which runs multiple requests concurrently within a single instance to reduce cold starts and the cost of I/O-bound work such as API calls and database queries. You don't configure anything to get this behavior.

Vercel Functions support streaming, so you can send data to the client as you produce it instead of waiting for the full response. Koa streams a response when you assign a readable stream to ctx.body: it pipes the stream to the response, defaults the content type to application/octet-stream, and destroys the stream if the request closes early.

src/index.ts
import { Readable } from 'node:stream';
router.get('/stream', (ctx) => {
ctx.type = 'text/plain';
ctx.body = Readable.from(['Hello', ' ', 'from', ' ', 'Koa']);
});

Streaming pairs well with Fluid compute: while your function waits between chunks, the same instance can serve other requests. Because Koa accepts any Node readable stream as the body, it's straightforward to stream model output, for example, from AI SDK.

Koa and Vercel each have a layer for running code around requests, and they solve different problems. Koa middleware runs inside your app, after the request reaches your function. Because Koa middleware cascades, you can run logic before and after the rest of the stack by awaiting next():

src/index.ts
app.use(async (ctx, next) => {
console.log('Request:', ctx.url);
await next();
console.log('Response sent');
});

Use Koa middleware for app-level concerns such as logging, authentication, and request processing. Vercel Routing Middleware runs at the edge, before the request reaches your Koa app. Use it for rewrites, redirects, and header changes that should happen before any function runs. The two layers work together, with Routing Middleware shaping the request at the edge and Koa's middleware handling it inside your app.

Vercel Cron Jobs trigger a route on a schedule by sending an HTTP GET request to it. Define a route in your Koa app for the task, then register the schedule in vercel.json. On serverless, prefer Vercel Cron Jobs over an in-process scheduler, because your function doesn't run continuously between requests.

Define the route:

src/index.ts
router.get('/api/cron/cleanup', (ctx) => {
if (ctx.headers.authorization !== `Bearer ${process.env.CRON_SECRET}`) {
ctx.status = 401;
return;
}
// Run your scheduled work here
ctx.body = { ok: true };
});

Register the schedule:

vercel.json
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"crons": [{ "path": "/api/cron/cleanup", "schedule": "0 0 * * *" }]
}

Vercel runs cron jobs only on production deployments. To stop anyone else from calling the route, set a CRON_SECRET environment variable in your project settings. Vercel sends it as a Bearer token in the Authorization header on every cron invocation, and your handler compares it before running the task.

Vercel Observability tracks your deployed function automatically, with no setup. Open the Observability page in your project to see invocation counts, error rates, and duration for your Koa app, along with the requests your function makes to external APIs. On Observability Plus, you also get longer retention and a latency breakdown by route.

Vercel finds your Koa app by looking for a server entry point at a fixed set of locations: app, index, or server (with a .ts, .js, or related extension) at your project root or under src/. Put your app at one of these paths, keep the call to app.listen(), and let Vercel build and serve it as a Vercel Function.

Run vercel dev for local development instead of starting Koa with node directly. It serves your app the same way production does, so the behavior you test locally matches what you deploy. Local development with vercel dev requires Vercel CLI 50.4.8 or later.

Was this helpful?

supported.