Issue Deploying Next.js / Prisma app

Hello everyone,

I’ve been trying for a few hours to deploy my Next.js / prisma application.

With the generated Dockerfile and fly.toml The build deployment was successful, however my database is not properly connected to my web application.

I created a separate fly postgres database and connected the two with flyctl postgres attach. Attachment was successful, but I still got an error on my application.
I tried first creating the database and then deploy the Next.js application, with no success.

From my research, it seems Next.js requires the DATABASE_URL env variable at build time.
I tried a few things, such as hard coding the DATABASE_URL value in my Dockerfile, fly.io build secrets tutorial, none of which have worked.
Either I get a strange error regarding TLS at build time when I added RUN npx prisma migrate deploy interrupting the deployment, or an issue with locating my database once the application is correctly deployed and running.

The guides on fly.io, prisma or Next.js did not help me fixing the problem.

My questions are the following:

  1. How do I properly set the DATABASE_URL variable at build time?
    Docker was giving me an error because of cat when following fly.io “Build Secrets” tutorial. Hard coding seems to at least deploy the app but this is unsafe.

  2. Do I need to add anything else for prisma beside the default RUN npx prisma generate?
    I can’t find any information regarding this on prisma deployment. I don’t think my database is properly created.

Thank you very much in advance for your help.

What is the error? This should work.

In general, secrets are not available at build time, unless explicitly passed.

1 Like

Thank you for your reply. I started fresh from my git repository. The app runs locally correctly.

Here is what I did to deploy it:

  1. fly postgres create, with the name pg-<app-name>.
Postgres cluster pg-<app-name> created
[...]
Save your credentials in a secure place -- you won't be able to see them again!
[...]
  1. flyctl launch, in the same organization with name <app-name>.
[...]
Platform: machines
✓ Configuration is valid
[...]
Now: run 'fly deploy' to deploy your Next.js/Prisma app.
  1. I followed the build secrets tutorial. Here is my Dockerfile:
# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=19.8.1
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Next.js/Prisma"

# Next.js/Prisma app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install -y build-essential openssl pkg-config python-is-python3

# Install node modules
COPY --link package-lock.json package.json ./
RUN npm ci --include=dev

# Generate Prisma Client
COPY --link prisma .
RUN --mount=type=secret,id=DATABASE_URL \
    DATABASE_URL="$(cat /run/secrets/DATABASE_URL)" npx prisma generate

# Copy application code
COPY --link . .

# Build application
RUN npm run build

# Remove development dependencies
RUN npm prune --omit=dev


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y openssl && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built application
COPY --from=build /app /app

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD [ "npm", "run", "start" ]

My fly.toml config file is unchanged.


  1. I deploy my app.
    flyctl deploy --build-secret DATABASE_URL=postgres://<username>:<password>@pg-<app-name>.flycast:5432 with the actual db username, password and db app name.
[...]
This deployment will:
 * create 2 "app" machines

No machines in group app, launching a new machine
  Machine <number> [app] update finished: success
Creating a second machine to increase service availability
  Machine <number> [app] update finished: success
Finished launching new machines

NOTE: The machines for [app] have services with 'auto_stop_machines = true' that will be stopped when idling

My Next.js app is now online, accessible at the deployed link.

  1. I set the DATABASE_URL secret since it was not set.
    I did so with fly secrets set DATABASE_URL=<db-connection-string>, identical one used for deploying.

When I access my application, I get an error message when I want to say create a new user.
This is the error message when I want to do anything involving the database:

 Invalid `prisma.survey.findUnique()` invocation: Error opening a TLS connection: unexpected EOF

Can I see your schema.prisma file? By any chance does it define sslmode? This needs to be either disable or prefer as the postgres db is not running with ssl/tls.

1 Like

This my schema.prisma file:

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

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

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

model User {
  id                   String                 @id @default(uuid())
  email                String                 @unique
  hash                 String
  salt                 String
  createdAt            DateTime               @default(now()) @map(name: "created_at")
  updatedAt            DateTime               @updatedAt @map(name: "updated_at")
  admin                Boolean                @default(false)
  surveys              Survey[]

  @@map(name: "users")
}
[ all my other models... ]

I have nothing regarding sslmode.

I don’t have much experience with next.js (perhaps others can chime in here?), but if you have postgres installed locally and can set DATABASE_URL, if you run the following in an empty directory you will get a minimal prisma/postgres application that you can deploy:

npx --yes @flydotio/node-demo@latest --prisma --postgres

Other than the model, the schema.prisma file is identical to yours.

More information on the demo app can be found here: Vanilla with Candy Sprinkles · The Fly Blog

1 Like

Thank you for your reply. I will try deploying this example see if it works.

Could this be due to something related to server certificate?
Prisma has a setting to accept self signed certificates with postgres. Is this possibly the case here?
should I accept self signed certificate with sslaccept=accept_invalid_certs?

Edit: accept_invalid_certs is supposedly already the default. But maybe append it to the DATABASE_URL in some way?

How can I manually test and see if the database and the server can communicate together?

Edit 2: I connected to my database with fly pg connect -a pg-<app-name>. I do not see my app database, only postgres, pgmr, template0 and template1.
I believe this may be causing the issue.

With fly postgres, you want no certs, valid or invalid. The database is running on a private network and is not accessible to the internet so none of this is necessary.

The piece I don’t understand is what in your configuration is telling prisma to use ssl/tls; if you can figure that out, you will need to turn that off.

1 Like

Alternately, you could use another provider that does require SSL/TLS: This Is Not Managed Postgres · Fly Docs

1 Like

I disabled SSL and added a database name for the deployment, thinking this was maybe the issue.
flyctl deploy --build-secret DATABASE_URL="postgres://<username>:<password>@pg-<app-name>.flycast:5432/<db>?schema=public&sslmode=disable"

This didn’t change the result, I still get the TLS error. The app does deploy however.

I decided to try modifying my Dockerfile, see if that was the issue. In my Dockerfile, I added npx prisma migrate deploy like this:

[...]
RUN --mount=type=secret,id=DATABASE_URL \
    DATABASE_URL="$(cat /run/secrets/DATABASE_URL)" npx prisma generate && npx prisma migrate deploy
[...]

Unfortunately the deployment failed and now I get this error:

=> ERROR [build 5/8] RUN --mount=type=secret,id=DATABASE_URL     DATABASE_URL="$(cat /run/secrets/DATABASE_URL)" npx prisma generate && npx prisma migrate deploy                       3.4s
------                                                                                                                                                                                        
 > [build 5/8] RUN --mount=type=secret,id=DATABASE_URL     DATABASE_URL="$(cat /run/secrets/DATABASE_URL)" npx prisma generate && npx prisma migrate deploy:                                  
#0 1.681 Prisma schema loaded from schema.prisma                                                                                                                                              
#0 2.371                                                                                                                                                                                      
#0 2.371 ✔ Generated Prisma Client (4.14.1 | library) to ./node_modules/@prisma/client in 185ms                                                                                               
#0 2.371 You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client                                                                                              
#0 2.371 ```
#0 2.371 import { PrismaClient } from '@prisma/client'
#0 2.371 const prisma = new PrismaClient()
#0 2.371 ```
#0 3.304 Prisma schema loaded from schema.prisma
#0 3.309 Datasource "db": PostgreSQL database
#0 3.315 
#0 3.316 Error: Prisma schema validation - (get-config wasm)
#0 3.316 Error code: P1012
#0 3.316 error: Environment variable not found: DATABASE_URL.
#0 3.316   -->  schema.prisma:10
#0 3.316    | 
#0 3.316  9 |   provider = "postgresql"
#0 3.316 10 |   url      = env("DATABASE_URL")
#0 3.316    | 
#0 3.316 
#0 3.316 Validation Error Count: 1
#0 3.316 [Context: getConfig]
#0 3.316 
#0 3.316 Prisma CLI Version : 4.14.1
------
Error: failed to fetch an image or build from source: error building: failed to solve: executor failed running [/bin/sh -c DATABASE_URL="$(cat /run/secrets/DATABASE_URL)" npx prisma generate && npx prisma migrate deploy]: exit code: 1

It seems the database URL is not properly found but I pass it as argument when deploying.

That’s odd. Remix blues does this without secrets or errors:

1 Like

I also came across this link. The shell script run at the end of the dockerfile actually contains npx prisma migrate deploy.

I was able to deploy my app using a hard-coded ENV DATABASE_URL. Here is the Dockerfile:

# syntax = docker/dockerfile:1

# Adjust NODE_VERSION as desired
ARG NODE_VERSION=19.8.1
FROM node:${NODE_VERSION}-slim as base

LABEL fly_launch_runtime="Next.js/Prisma"

# Next.js/Prisma app lives here
WORKDIR /app

# Set production environment
ENV NODE_ENV="production"
ENV DATABASE_URL="postgres://<username>:<password>@pg-<app-name>.flycast:5432/<db>?schema=public&sslmode=disable"


# Throw-away build stage to reduce size of final image
FROM base as build

# Install packages needed to build node modules
RUN apt-get update -qq && \
    apt-get install -y build-essential openssl pkg-config python-is-python3

# Install node modules
COPY --link package-lock.json package.json ./
RUN npm ci --include=dev

# Generate Prisma Client
COPY --link prisma .
RUN npx prisma generate

# Copy application code
COPY --link . .

# Build application
RUN npm run build

RUN npx prisma migrate deploy
RUN npx prisma db seed

# Remove development dependencies
RUN npm prune --omit=dev


# Final stage for app image
FROM base

# Install packages needed for deployment
RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y openssl && \
    rm -rf /var/lib/apt/lists /var/cache/apt/archives

# Copy built application
COPY --from=build /app /app

# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD [ "npm", "run", "start" ]

prisma migrate deploy is used after the run build.
It may work if used before building as well, but I decided to follow what remix did with their Dockerfile.

It’s working for now. However if you know how to not hard-code the DATABASE_URL in my Dockerfile that would be great.

Thank you so much for the help.

That is literally what --build-arg is for. What you do is replace that line with an ARG:

ARG DATABASE_URL

And then run fly deploy thus:

fly deploy --build-arg DATABASE_URL=postgres://<username>:<password>@pg-<app-name>.flycast:5432/<db>?schema=public&sslmode=disable

Admittedly this hardcodes the secret in a different place (presumably a script on your development machine), but you can commit your Dockerfile and add that script to your .gitignore and .dockerignore files.