# Deploy Next.js with Postgres and Prisma

Source: https://naijacloud.com/guides/nextjs-postgres-prisma

This guide deploys a Next.js App Router app to Naijacloud with a managed Postgres database. At the end you’ll have a small notes app at your own naijacloud.app address that saves to Postgres through Prisma, with migrations applied every time a new version starts and every push to your branch deploying automatically. The stack is Next.js 16, Prisma 7 with the pg driver adapter, and Postgres 18, deployed from a GitHub repository.

_15 min · Beginner · Last verified 2026-10-11 · Stack: nextjs, prisma, postgres_

**What you'll have at the end:** A Next.js notes app on your own naijacloud.app address. Type a note, press Save, and it’s written to Postgres and read back on every request.

## Before you start

- A Next.js app in a GitHub repository, with its `package-lock.json` committed. Starting fresh? Run `npx create-next-app@latest` and push it.
- Node.js 20 or newer on your machine, and Docker if you want a Postgres to develop against.
- A Naijacloud account. The free tier covers this guide: one web service and one database.

## Add Prisma

Install the Prisma CLI, the client and the driver adapter for `pg`. Prisma 7 talks to Postgres through the adapter rather than its own engine.

```sh
$ npm install @prisma/client@7 @prisma/adapter-pg@7
$ npm install --save-dev prisma@7
```

Create the schema. The client is generated into `app/generated/prisma`, so add that folder to `.gitignore`: it’s rebuilt on every build.

```prisma title="prisma/schema.prisma"
generator client {
  provider = "prisma-client"
  output   = "../app/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

model Note {
  id        Int      @id @default(autoincrement())
  body      String
  createdAt DateTime @default(now())
}
```

Prisma 7 reads the connection string from `prisma.config.ts`, not from the schema:

```ts title="prisma.config.ts"
import { defineConfig } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: { path: "prisma/migrations" },
  datasource: { url: process.env.DATABASE_URL },
});
```

## Create the first migration

Run a Postgres locally and create the migration against it. The migration files are what Naijacloud applies to the real database later, so commit them.

```sh
$ docker run -d --name notes-db -p 5432:5432 -e POSTGRES_PASSWORD=pw -e POSTGRES_DB=notes postgres:18-alpine
$ export DATABASE_URL=postgresql://postgres:pw@localhost:5432/notes
$ npx prisma migrate dev --name init
```

Then make `npm run build` generate the client before Next.js builds, in `package.json`:

```json title="package.json"
"scripts": {
  "dev": "next dev",
  "build": "prisma generate && next build",
  "start": "next start"
}
```

## Read and write from a page

Create one Prisma client for the whole process:

```ts title="lib/db.ts"
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "@/app/generated/prisma/client";

const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };

export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
  });

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
```

Then a page that lists notes and saves new ones with a server action. `await connection()` keeps the query at request time, so the build never needs the database.

```tsx title="app/page.tsx"
import { revalidatePath } from "next/cache";
import { connection } from "next/server";
import { Suspense } from "react";
import { prisma } from "@/lib/db";

async function addNote(formData: FormData) {
  "use server";
  const body = String(formData.get("body") ?? "").trim();
  if (body) await prisma.note.create({ data: { body } });
  revalidatePath("/");
}

async function Notes() {
  // Read the database on every request, never at build time.
  await connection();
  const notes = await prisma.note.findMany({ orderBy: { createdAt: "desc" } });
  return (
    <ul>
      {notes.map((note) => (
        <li key={note.id}>{note.body}</li>
      ))}
    </ul>
  );
}

export default function Home() {
  return (
    <main>
      <h1>Notes</h1>
      <form action={addNote}>
        <input name="body" placeholder="Write a note" />
        <button type="submit">Save</button>
      </form>
      <Suspense fallback={<p>Loading…</p>}>
        <Notes />
      </Suspense>
    </main>
  );
}
```

```sh
$ git add .
$ git commit -m "Notes on Postgres with Prisma"
$ git push
```

> **Note: DATABASE_URL is there when the app starts, not while it builds**
>
> Naijacloud passes your variables to the running app, and only the public ones (`NEXT_PUBLIC_…`) to the build as well. Keep anything that needs `DATABASE_URL` out of the build: that is why the page reads at request time and the migration runs in the start command below.

## Create the database

In the dashboard, open your project and choose **New service** → **Database**. Name it `notes-db`, pick **Postgres**, choose the **Free** size and click **Create database**. On the **notes-db is ready.** screen, click **Reveal secrets** and copy the **Internal URL**. The password is shown once on this page.

## Create the web service

Choose **New service** → **Web service**, name it `notes` and, under **Source**, pick your repository and branch on the **GitHub** tab. Naijacloud detects Next.js and fills in `npm ci && npm run build` and `npm start`. Keep the build command and change the start command so migrations run first:

- **Build command**: `npm ci && npm run build`
- **Start command**: `npx prisma migrate deploy && npm start`

Under **Environment variables**, click **+ Add variable** and add one:

- **DATABASE_URL**: The Internal URL you copied from the database

Leave the size on **Free** and click **Create web service**. Free apps may sleep when nobody visits for a while and wake on the next request; paid apps never sleep.

## Deploy and try it

Open the service. The **Deploys** tab streams the build; when it goes live, the runtime log shows the migration and Next.js starting:

```sh title="Runtime log · notes" nocopy
Applying migration `20261007033409_init`
All migrations have been successfully applied.
> next start
▲ Next.js 16.4.0
✓ Ready in 55ms
```

Open `https://notes.naijacloud.app`, write a note and press **Save**. It’s in Postgres: reload the page and it’s still there. Every push to your branch now builds, migrates and goes live.

> **Warning: The free database allows 20 connections**
>
> The `pg` adapter keeps a pool of up to 10 connections per process, which fits. If you scale up or run several processes, lower the pool with `new PrismaPg({ connectionString, max: 5 })` or move to a larger database size.

## Reference

- [Deploy a web service](https://naijacloud.com/docs/deploy/web-services.md)
- [Builds](https://naijacloud.com/docs/deploy/builds.md)
- [Connecting to a database](https://naijacloud.com/docs/databases/connecting.md)
- [Environment variables](https://naijacloud.com/docs/deploy/environment-variables.md)

All guides: https://naijacloud.com/guides · Index for agents: https://naijacloud.com/llms.txt
