Prisma Migrate: Deploy Migration with Docker
Perform database migration with Prisma Migrate using Docker
- Authors
- Marc Stammerjohann
- Published at
One question about the guide Dockerizing a NestJS app with Prisma and PostgreSQL was asked frequently: "How to run database migrations in production?"
Let's dive into this topic and find out how to use prisma migrate deploy
with Docker for your production database.
Prisma recommends to perform migrations for production database in an automated step and advises against performing it locally π ββοΈ
Prisma Migrate with Docker
Let's take a look at how to integrate the command prisma migrate deploy
into the following Dockerfile
.
FROM node:14 AS builder
# Create app directory
WORKDIR /app
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY prisma ./prisma/
# Install app dependencies
RUN npm install
COPY . .
RUN npm run build
# as build stepβ
FROM node:14
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
EXPOSE 3000
# or during execution β
CMD [ "npm", "run", "start:prod" ]
First of all, one command is very important: COPY prisma ./prisma/
. This makes sure to not only copy the schema.prisma
into the Docker image, but also includes the migrations
directory. This is necessary as prisma migrate deploy
only executes the migration history files.
Add a RUN command β
What about adding RUN npx prisma migrate deploy
to the Dockerfile
?
Two issues that come to mind are making it not a good solution.
- Performs migration during build step, not before Docker container is started
- CI/CD needs access to your production database, requires
DATABASE_URL
environment
RUN
commands are executed during the build steps of your the Docker image. Time may pass between the database migration and restarting the Docker container (Nest app), leaving your database and Nest app in different states. Additionally, when you create your Docker image in a CI/CD pipeline, the server needs access to the production database to perform the migration.
Add to CMD β
A better approach is to perform the migration just before starting your Nest app.
Nest app is started with the last command CMD [ "npm", "run", "start:prod" ]
. CMD is not executed during the build steps of your Docker image, rather than during executing the Docker container (docker run ...
or docker-compose up
).
This is a multi-stage build Dockerfile
, you need to copy the prisma
directory including the migration history into the second stage.
FROM node:14 AS builder
# build steps
...
RUN npm run build
FROM node:14
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
# π copy prisma directory
COPY --from=builder /app/prisma ./prisma
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
Add a new script to your package.json
to execute prisma migrate deploy && npm run start:prod
.
{
...
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"start:migrate:prod": "prisma migrate deploy && npm run start:prod", // new script π
},
...
}
Use the new script as CMD
command
FROM node:14 AS builder
# Create app directory
WORKDIR /app
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY prisma ./prisma/
# Install app dependencies
RUN npm install
COPY . .
RUN npm run build
FROM node:14
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/prisma ./prisma
EXPOSE 3000
# π new migrate and start app script
CMD [ "npm", "run", "start:migrate:prod" ]
Now start your Docker containers with docker-compose up
to see the logs and verify that your migrations are performed. In case all migrations have already been applied to your database, Prisma Migrate will complete with "No pending migrations to apply.".
postgresprisma | 2021-11-09 15:24:30.189 UTC [1] LOG: database system is ready to accept connections
nest-api |
nest-api | > nestjs-prisma-docker@0.0.1 start:prod:docker /
nest-api | > npm run migrate:deploy && npm run start:prod
nest-api |
nest-api |
nest-api | > nestjs-prisma-docker@0.0.1 migrate:deploy /
nest-api | > prisma migrate deploy
nest-api |
nest-api | Prisma schema loaded from prisma/schema.prisma
nest-api | Datasource "db": PostgreSQL database "food", schema "public" at "postgresprisma:5432"
postgresprisma | 2021-11-09 15:24:38.959 UTC [34] ERROR: relation "_prisma_migrations" does not exist at character 126
postgresprisma | 2021-11-09 15:24:38.959 UTC [34] STATEMENT: SELECT "id", "checksum", "finished_at", "migration_name", "logs", "rolled_back_at", "started_at", "applied_steps_count" FROM "_prisma_migrations" ORDER BY "started_at" ASC
nest-api |
nest-api | 1 migration found in prisma/migrations
nest-api |
nest-api | Applying migration `20210603130425_init`
nest-api |
nest-api | The following migration have been applied:
nest-api |
nest-api | migrations/
nest-api | ββ 20210603130425_init/
nest-api | ββ migration.sql
nest-api |
nest-api | All migrations have been successfully applied.
nest-api |
nest-api | > nestjs-prisma-docker@0.0.1 start:prod /
nest-api | > node dist/main
nest-api |
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [NestFactory] Starting Nest application...
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [InstanceLoader] PrismaModule dependencies initialized +73ms
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [InstanceLoader] AppModule dependencies initialized +0ms
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [RoutesResolver] AppController {/}: +14ms
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [RouterExplorer] Mapped {/, GET} route +7ms
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [RouterExplorer] Mapped {/foods, GET} route +1ms
nest-api | [Nest] 174 - 11/09/2021, 3:24:40 PM LOG [NestApplication] Nest application successfully started +65ms
postgresprisma | 2021-11-09 16:20:52.899 UTC [1] LOG: database system is ready to accept connections
nest-api |
nest-api | > nestjs-prisma-docker@0.0.1 start:prod:docker /
nest-api | > npm run migrate:deploy && npm run start:prod
nest-api |
nest-api |
nest-api | > nestjs-prisma-docker@0.0.1 migrate:deploy /
nest-api | > prisma migrate deploy
nest-api |
nest-api | Prisma schema loaded from prisma/schema.prisma
nest-api | Datasource "db": PostgreSQL database "food", schema "public" at "postgresprisma:5432"
nest-api |
nest-api | 1 migration found in prisma/migrations
nest-api |
nest-api |
nest-api | No pending migrations to apply.
nest-api |
nest-api | > nestjs-prisma-docker@0.0.1 start:prod /
nest-api | > node dist/main
nest-api |
nest-api | [Nest] 174 - 11/09/2021, 4:20:55 PM LOG [NestFactory] Starting Nest application...
nest-api | [Nest] 174 - 11/09/2021, 4:20:56 PM LOG [InstanceLoader] PrismaModule dependencies initialized +69ms
nest-api | [Nest] 174 - 11/09/2021, 4:20:56 PM LOG [InstanceLoader] AppModule dependencies initialized +1ms
nest-api | [Nest] 174 - 11/09/2021, 4:20:56 PM LOG [RoutesResolver] AppController {/}: +12ms
nest-api | [Nest] 174 - 11/09/2021, 4:20:56 PM LOG [RouterExplorer] Mapped {/, GET} route +8ms
nest-api | [Nest] 174 - 11/09/2021, 4:20:56 PM LOG [RouterExplorer] Mapped {/foods, GET} route +1ms
nest-api | [Nest] 174 - 11/09/2021, 4:20:56 PM LOG [NestApplication] Nest application successfully started +62ms
Hurray π the database migrations are executing successfully with Docker! Test it out on your test, staging and then production environment.
Sponsor us
Did you find this post useful? We at notiz.dev write about our experiences developing Apps, Websites and APIs and develop Open Source tools. Your support would mean a lot to us π. Receive a reward by sponsoring us on Patreon or start with a one-time donation on GitHub Sponsors.