Deploy a project to Vercel with Prisma and Postgres for free: step-by-step guide

Deploy a project to Vercel with Prisma and Postgres for free: step-by-step guide
Software engineering
Alejandro Muñoz del Prado

Alejandro Muñoz del Prado

Equipo técnico

November 09, 2026
6 min read
Share:

Setting up a side project and keeping it published without spending a single euro is perfectly possible. The Next.js + Prisma + Postgres stack deployed on Vercel covers the typical case: an app with a relational database, automatic branch deployments, and zero upfront cost. At Julditec we've spent years putting software into production with larger variants of this same scheme (connection pooling, per-environment environment variables, CI/CD), so we're going to explain how to set it up properly from the very first commit, because the mistakes made in a side project are the same ones that hurt later in a serious project.

Open laptop on a desk with code visible on screen

Starting the project: create-next-app and free Postgres

The starting point is the usual one:

Bash
npx create-next-app@latest mi-proyecto
cd mi-proyecto
npm install prisma --save-dev
npm install @prisma/client
npx prisma init

This generates the prisma folder with a schema.prisma file and a .env file. The next thing is to decide where the database lives. For a reasonable free tier there are two clear options:

Neon or Supabase

  • •Neon: serverless Postgres with autoscaling and a "pooled" connection ready to use, specifically designed for stateless environments like Vercel functions.
  • •Supabase: managed Postgres with extras (auth, storage, automatic REST API) that might be more than you need for a small project, but are there if it grows.

For an unpretentious side project, Neon tends to be the more straightforward option because connection pooling comes solved out of the box. If you're already planning to use authentication or file storage, Supabase saves you from adding another service later on.

Schema, migrations, and the command that fails if you forget it

With the connection string in the .env file, define the schema:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String
  content   String?
  createdAt DateTime @default(now())
}

Locally, migrations are created with npx prisma migrate dev. The problem shows up in production: Vercel runs the build on every deployment, but it doesn't apply migrations on its own. You have to tell it explicitly in package.json:

JSON
{
  "scripts": {
    "build": "prisma migrate deploy && next build",
    "postinstall": "prisma generate"
  }
}

The order matters: pending migrations are applied against the real database first, and only then is the app compiled. If you forget the migrate deploy step in the build, the deployment succeeds but the app crashes as soon as it tries to read a table that doesn't exist yet.

The typical Prisma problems in serverless

Most side projects get stuck at this point the first time something fails intermittently.

Too many connections: pooling and client singleton

Each Vercel serverless function can spin up a new instance, and each instance that creates its own PrismaClient opens a connection to Postgres. With moderate traffic, the free plan's connection limit gets exhausted within minutes. Two complementary solutions:

  • •Use the pooled connection string that Neon or Supabase gives you (usually with a different port or parameter than the direct connection, designed for PgBouncer).
  • •Reuse the client with a singleton instead of instantiating it on every request.
TypeScript
// lib/prisma.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

export const prisma =
  globalForPrisma.prisma ?? new PrismaClient()

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.prisma = prisma
}

This pattern prevents development hot reload from opening a new client every time you save a file, and in production it keeps the number of active instances to the minimum necessary.

prisma generate in postinstall

If prisma generate doesn't run after npm install, the client your code imports won't match the current schema, and Vercel will throw type errors right in the middle of the build. That's why the postinstall step in the example above isn't optional: without it, the deployment fails in a cryptic way.

Cold starts

Serverless functions that haven't received traffic for a while "go to sleep." The first request after that dormancy takes longer: the runtime has to initialize, the Prisma client has to load, and if the database connection was also idle, it has to be re-established. In a side project this shows up as a slow first load after a period without visitors. There's not much of a fix for this on the free tier beyond keeping the client reused (the singleton already helps) and avoiding heavy logic on each function's startup.

Environment variables and branch preview deployments

In the Vercel dashboard, the DATABASE_URL variable is configured per environment: Production, Preview, and Development. This allows for a convenient workflow:

  • •The main branch points to the production database.
  • •Each pull request generates a preview deployment with its own URL, which can point to the same database (if you only want to see UI changes) or to a separate test database (if you're going to touch the schema).

For small projects, a single database is usually enough, as long as you're careful with destructive migrations in preview. For something a bit more serious, creating a separate database branch (Neon supports this natively with database "branching") avoids nasty surprises.

Stacked building blocks symbolizing a scalable architecture

The limits of the free tier and when they stop being worth it

All of this works at zero cost until the project stops being small. The usual limits to keep an eye on:

  • •Compute hours or storage on Neon or Supabase's free plan, which run out with sustained traffic or databases that grow in size.
  • •Concurrent connection limits, which poorly configured pooling can saturate even with moderate traffic.
  • •Function execution time on Vercel, which is shorter on the free plan and can cut off slow queries or batch processes.
  • •Automatic database pauses due to inactivity, which add extra latency on top of the latency already caused by cold starts.
When the project starts to have real users, needs to schedule background tasks, or requires stable connections outside the serverless cycle, the free tier stops being a technical foundation and becomes a product limitation.

Imagine a small internal tool for managing room bookings in an office: it works perfectly for free while fifteen people use it a couple of times a day. As soon as it becomes the application the whole company uses daily, the connection and compute limits start to show, and that's the moment to consider a paid plan or a dedicated database — not before.

The practical recommendation is simple: build the side project with this architecture without fear, but set up the client singleton and the pooled connection string from the very first commit. They're two lines of code that cost nothing at the start and that save you a painful migration the day the project actually starts taking off.

Tags:devopsnext.jsvercelprismapostgresneonsupabase

Ready to take your project to the next level?

At JULDITEC we transform ideas into innovative digital solutions. Let's work together.