Migrating production infrastructure? Get up to ₹10,000 in migration credits. Apply now
voroaBlog
Web Services

Netlify Backend Hosting: What Functions Can and Cannot Do

· · 13 min read

Netlify backend hosting - what functions can and cannot run, and where the rest of the backend goes

A Netlify backend is usually a set of serverless functions that sit next to your frontend and answer API calls. That works well for small jobs, and it runs into hard limits once your backend needs a long-running process, a queue worker, WebSockets or a database close to Indian users. This guide is for developers whose frontend already lives on Netlify and who want to know what the backend can run there, what it cannot, and where to put the rest.

All Netlify limits below are from Netlify’s own documentation as of November 2026, mainly its functions configuration reference. Netlify changes these numbers over time, so check their docs before you design around one.

Advertisement

Can you host a backend on Netlify?

Yes, with conditions. Netlify runs backend code as functions: short-lived handlers that start for a request and stop after it. A normal request can run for 60 seconds, a background task for 15 minutes, and a scheduled task for 30 seconds. A long-running Node.js server is only available through Netlify Server, which is in Early Access.

So the honest answer depends on what “backend” means for your app. A contact form, a payment webhook or a small JSON API fits well. A chat server, a queue consumer, a job that takes 40 minutes, or an API that keeps a pool of database connections open does not fit functions, and needs either Netlify Server or a host that runs ordinary processes.

The five places backend code can run on Netlify

Netlify has five different runtimes, and each has its own rules. Mixing them up is the most common reason a backend works on a laptop and fails after deploy.

Runtime How long it can run Good for
Synchronous function 60 seconds per request API routes, form handlers, webhooks
Background function 15 minutes, client gets an empty 202 Slow follow-up work after a request
Scheduled function 30 seconds, on a cron schedule in UTC Small periodic jobs
Edge function 50 ms of CPU time per request Redirects, headers, personalisation
Netlify Server (Early Access) Long-lived process; a request up to 30 minutes (2 hours on Pro) Express, Fastify, WebSockets

Synchronous functions

This is the default. A function gets a Request and returns a Response. Netlify lists the synchronous execution limit as 60 seconds and marks it as not configurable. A buffered request or response can be up to 6 MB, and binary uploads are Base64-encoded, which brings the real upload limit closer to 4.5 MB. Functions get 1 GB of memory by default; raising it (up to 4 GB) is a Pro and Enterprise setting.

Older answers on Netlify’s forum give lower limits. The limit has moved over time, so trust the current docs, not an old thread.

Background functions

Add background: true and the function can run for up to 15 minutes. The catch: the caller gets an empty 202 straight away, so the result has to go somewhere else, such as a database row the frontend polls. If the function throws, Netlify retries it after one minute and again two minutes later, so the work must be safe to run twice. The request payload for a background function is capped at 256 KB.

Scheduled functions

A scheduled function runs on a cron expression, has a 30-second limit, and runs in UTC. It cannot receive a payload, cannot be called by URL, and only fires for published deploys, not previews. For Indian teams the UTC part matters: 0 9 * * * is 2:30 PM IST, not 9 AM.

Edge functions

Edge functions run close to the visitor but get 50 ms of CPU time per request. They are built for rewriting a request or a response, not for running your API.

Netlify Server

Netlify Server is the newest option and the one that changes the answer most. It runs a normal Node.js server (Express, Hono or Fastify) without a wrapper, keeps state in memory between requests, and holds WebSocket connections open. The limits are worth reading before you plan around it:

  • It is in Early Access. Netlify says it is “meant for evaluation, experimentation, and low-volume production use”, and that its APIs and behaviour might change.
  • It always scales to zero: an instance stops after 5 minutes without requests. There is no always-on setting yet.
  • It runs up to 3 instances, or 1 on the Free and Personal plans.
  • An instance is replaced after 8 hours even when busy, and an outbound connection, such as one to your database, is closed after 60 seconds with no traffic.
  • It runs in two regions only: US East (Ohio) and EU (Frankfurt).
  • Full-stack frameworks such as Next.js and Astro do not run on it yet.

As of November 2026 it is free during Early Access, and Netlify says pricing in credits will be announced before general availability.

Running Express on Netlify Functions

Netlify’s own Express guide uses the serverless-http package to turn the whole app into one function:

js
// netlify/functions/api.js
import express, { Router } from "express";
import serverless from "serverless-http";

const api = express();
const router = Router();

router.get("/hello", (req, res) => res.send("Hello World!"));
api.use("/api/", router);

export const handler = serverless(api);
toml
# netlify.toml
[functions]
  external_node_modules = ["express"]
  node_bundler = "esbuild"

[[redirects]]
  force = true
  from = "/api/*"
  status = 200
  to = "/.netlify/functions/api/:splat"

Netlify’s guide is clear that every function limit still applies, and that Express should not be deployed as a background or scheduled function.

What breaks when an Express app becomes a function

Your Express code does not change, but the way it runs does. These are the problems people hit most.

Memory does not survive between requests

A function instance can be reused or thrown away at any time. An in-memory session store, a rate-limit counter in a JavaScript object, or a cache in a global variable will look fine in testing and then lose data in production. Keep state in a database, a key value store or a cookie.

WebSockets have nowhere to live

A function answers one request and ends, so it cannot hold a socket open. Socket.IO and the ws module need a process that stays up. On Netlify, that means Netlify Server, or a separate managed WebSocket service with a function that only issues tokens, which is the approach Netlify’s own blog describes.

Long jobs hit the wall

Generating a large report, resizing a batch of images or calling a slow AI API can pass 60 seconds. Moving it to a background function buys 15 minutes, but you lose the response. Anything longer, or anything that should keep working through a queue all day, needs a worker process.

Database connections pile up

PostgreSQL gives every connection its own process and memory. When many function instances each open their own connection, the database runs out of slots long before it runs out of capacity. Use a connection pooler in front of the database if your backend stays on functions.

The first request is slow

A function that has not run recently has to start before it can answer. A reply on Netlify’s forum to a user asking for an India region in 2023 said cold starts, not distance, were the cause of the slowness that user saw. On a backend built from many small functions, this adds up.

Databases for a Netlify backend

Netlify now has three answers for data:

  • Netlify Database: managed Postgres, built in. Netlify’s launch post says its partnership with Neon powers the new databases. It is on credit-based plans only. The Free plan allows 1 compute unit per database, 48 compute units per billing period and 5 GB of storage, and the database sleeps after 5 minutes of no queries. Netlify says waking it typically adds “well under a second, but this is not guaranteed”. Only Pro and Enterprise can keep it always on. Netlify’s docs do not say which region it runs in.
  • Netlify Blobs: a key/value store for files and unstructured data. Netlify describes it as optimised for frequent reads and infrequent writes, with no concurrency control: the last write to a key wins. It is not a relational database.
  • An external database: any Postgres you can reach over TLS.

Whichever you choose, put the database in the same region as the code that queries it. A single API request often runs several queries one after another, and every one of them pays the round trip.

Where Netlify runs your backend, seen from India

Netlify’s CDN serves static files from many locations, but backend code runs in one region you pick.

  • Functions default to US East (Ohio). Changing the region is a Pro and Enterprise feature.
  • The self-serve function regions in Asia are Singapore, Tokyo and Sydney. There is no India region in the list.
  • Netlify Server runs in Ohio or Frankfurt only.

For an app with users in India, that means every API call travels to another country and back. If the function also talks to a database in a third place, each query adds another long round trip. Moving the function to Singapore helps; it does not remove the distance.

The common split: frontend on Netlify, backend elsewhere

You can keep what Netlify does best, which is building and serving the frontend, and run the API, database and workers on a platform built for long-running processes. The frontend calls the API over HTTPS.

The browser loads the frontend from Netlify and calls an API web service in Mumbai, which uses managed PostgreSQL, hands slow jobs to a background worker, and shares the database with a cron job

Two small changes make the split work. First, the frontend reads the API address from an environment variable set in Netlify’s site settings, so you never hard-code it:

js
// frontend, built with Vite
const API_URL = import.meta.env.VITE_API_URL; // e.g. https://api.example.in

const res = await fetch(`${API_URL}/api/orders`, { credentials: "include" });

Second, the API allows the frontend’s exact origin, including https://:

js
// server.js on the backend
import cors from "cors";

app.use(cors({
  origin: ["https://example.in", "https://your-site.netlify.app"],
  credentials: true,
}));

If you are weighing a full move instead of a split, the Netlify alternatives for Indian teams post compares the options.

If you use cookies across two domains, put the API on a subdomain of your main domain (for example api.example.in), so the browser treats the cookie as same-site.

Running the backend, Postgres and workers in Mumbai

This is where Voroa fits. Each Netlify runtime has a direct equivalent that runs as a normal process in Mumbai:

On Netlify On Voroa What changes
Synchronous function or Express via serverless-http A web service One long-running server that reads PORT; no per-request wrapper
Netlify Server WebSockets WebSockets on the same web service No fixed maximum connection length; send a ping every 30 seconds
Background function A background worker A process that stays running, so a job is not cut off at 15 minutes
Scheduled function A scheduled task or a cron job Schedules run in IST, not UTC
Netlify Database Managed PostgreSQL Runs in Mumbai, next to the API

Turn the function back into a server

Split the Express app from the way it is started. Then the same app can run as a server on Voroa and, while you migrate, still as a Netlify function:

js
// app.js - your routes, unchanged
import express from "express";
export const app = express();
app.use(express.json());
app.get("/api/health", (req, res) => res.json({ ok: true }));
js
// server.js - the start command on Voroa: node server.js
import { app } from "./app.js";

const port = process.env.PORT || 3000;
app.listen(port, "0.0.0.0", () => console.log(`listening on ${port}`));

Voroa sets PORT for you, so reading it is all the server needs. The steps for a first deploy are in the Node.js deploy guide, and the Node.js documentation covers the runtime itself. Express’s own production best practices apply as they would on any server, such as setting NODE_ENV=production.

Add WebSockets to the same service

A Voroa web service accepts WebSocket connections on the same port as HTTP. With the ws module:

js
import { createServer } from "http";
import { WebSocketServer } from "ws";
import { app } from "./app.js";

const server = createServer(app);
const wss = new WebSocketServer({ server, path: "/ws" });

wss.on("connection", (ws) => {
  ws.on("message", (msg) => ws.send(`echo: ${msg}`));
});

server.listen(process.env.PORT || 3000, "0.0.0.0");

A connection with no traffic for about 100 seconds can be closed on its way through the edge, so send a ping every 30 seconds and reconnect after a deploy. The WebSockets guide has the full client side.

Replace background functions with a worker

A worker has no web address. It runs your start command and keeps running, which suits a job queue. Here is a minimal one that uses a Postgres table as the queue, so it needs nothing beyond the database:

js
// worker.js - start command: node worker.js
import pg from "pg";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });

async function next() {
  const { rows } = await pool.query(`
    UPDATE jobs SET status = 'running'
    WHERE id = (
      SELECT id FROM jobs WHERE status = 'queued'
      ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 1
    )
    RETURNING *`);
  return rows[0];
}

for (;;) {
  const job = await next();
  if (!job) { await new Promise((r) => setTimeout(r, 2000)); continue; }
  await handle(job); // your slow work, no 15-minute cut-off
  await pool.query("UPDATE jobs SET status = 'done' WHERE id = $1", [job.id]);
}

The API inserts a row and answers at once; the worker picks it up. SKIP LOCKED lets you run more than one worker without two of them taking the same job. Voroa’s background workers cost the same as a web service of the same size.

If a job is meant to run and finish on a timetable instead, use a scheduled task or a cron job, not a worker. A worker that exits gets started again. The guide on choosing between a worker and a cron job explains the difference.

Put Postgres next to the API

Create a managed PostgreSQL database in India and set its URL as DATABASE_URL on the web service and the worker. Because the API and the database are both in Mumbai, a request that runs five queries does not pay five trips abroad. If part of your backend stays on Netlify Functions for a while, connect those functions through the pooled port when your database lists one; the connection pooling guide explains which endpoint to use when.

What a Mumbai backend costs

You can try the whole setup at Rs 0. The free Nano size has 512 MB of memory and 0.1 vCPU, and a free web service sleeps after 15 minutes without requests. The free tier also includes one managed Postgres with 1 GB of storage, subject to free capacity.

For production, paid web sizes start at Rs 298 a month plus GST for 1 GB and 1 vCPU, always on. Adding the DB Starter Postgres (Rs 299) brings an API with its database to Rs 597 per month before GST. You pay in rupees by UPI, Indian card or netbanking, and get a GST invoice. See Voroa pricing for every size.

Run your Netlify backend on Voroa

Keep the frontend on Netlify if you like it there. Move the API, the database and the workers to Voroa, where they run as normal processes in Mumbai, deploy from GitHub on every push when auto-deploy is on, and are billed in INR. See what a Voroa web service includes, or read the wider backend API hosting guide first.

FAQ

Is Netlify good for backend development?

It is good for small, request-shaped backend work: webhooks, form handlers, and short API routes. It is a weaker fit for long-running servers, queues and WebSockets, unless you use Netlify Server, which is still in Early Access.

Can I run Socket.IO on Netlify?

Not on ordinary Netlify Functions, because a function cannot keep a connection open. Netlify Server supports WebSockets, but an Express app exported as it is answers WebSocket requests with a 501; wrap it in http.createServer() and export the server, or start it with app.listen(process.env.PORT). Any host that runs a long-lived Node.js process also works.

Does Netlify have a server in India?

Netlify’s documented function regions do not include India; the closest are Singapore and Tokyo. Netlify Server lists only Ohio and Frankfurt.

Can I use Netlify for the frontend and another host for the API?

Yes. Set the API URL as a build-time environment variable on Netlify, and allow your Netlify domain in the API’s CORS settings.

Do I need to rewrite my Express app to leave Netlify Functions?

No. Remove the serverless-http wrapper, call app.listen(process.env.PORT), and your routes stay the same.

Advertisement