Setting Up Docker
Fly has docs for deploying from various places, but we are going to deploy with Docker.
We will build our own Docker file, and then we'll configure Fly to use it.
The first thing we will do is create a file that will ensure that none of these files make it into our
Kent C. Dodds: 0:00 Looking at our Fly Dashboard, we just have a bunch of links to Fly Docs so that we know how to deploy to these various places. Feel free to read those, but we're going to deploy with Docker. We'll just build our own Dockerfile ourselves. Then we'll get Fly configured for it.
0:14 The first thing we're going to do is add the easy dockerignore, which will make sure that none of these files make it into our Docker image. Pretty much, we want everything that's in the gitignore.
0:27 Then we also want to add a couple other things, like the Dockerfile that we're about to create, the dockerignore file itself, and the git directory, because we don't need all of our git history in there.
0:39 With those, we're also going to make our Dockerfile itself. This one is going to be a bit of a beast. I'm actually going to just copy a whole Dockerfile for you so you don't have to worry about it.
0:56 We will make changes as needed. I'll walk through what this Dockerfile does and the different pieces of it. First off, we're using the bullseye-slim version of node:18. This is a very slimmed-down base image for us that has node preinstalled for us.
1:15 Then we're going to use apt-get to get a couple dependencies for Prisma, specifically OpenSSL and SQLite 3. Then we're making another layer here so that we can have our deps where we get those installed, all of the dependencies, including dev dependencies.
1:33 Then we'll make another layer for our production dependencies only, which basically copies everything over from the deps that we just installed but then prunes out everything that's the dev dependencies. This will be the dependencies we use for our running app.
1:48 Then it's time to build the app. Based on our base image, we're going to make a directory called app. We'll move into that directory. Then we'll copy all the dependencies from our dev dependencies layer.
2:01 Then we'll add in the Prisma directory so that when we run prisma generate, it'll have our Prisma schema. This will generate the Prisma client for us. Then we'll add the rest of the code to our project so that we can build everything else.
2:16 Then we have our final layer, where we specify some environment variables. Our DATABASE_URL, we're going to put that in /app, which is where our working directory is, /data/sqlite.db. The DATABASE_URL is used by Prisma. It's configured right here, where it's looking for our database by that environment variable.
2:35 Then our PORT is used by our application right here. It pulls that off of the port number, off of the environment variable. We use that in our listen right here. That's what that's for. Then our NODE_ENV variable will be production because we're running in a production environment. We want node to be in production mode.
2:57 Then we're going to move into the app directory and copy a bunch of stuff from our production-deps. We're only going to get the node modules from production. Then, from our build layer, we're going to copy the Prisma client that was generated along with the build directory that is created when we run our build.
3:13 Then we'll add the rest of the files from our source project, just to make sure we cover all of our bases, and then run the npm start script. The npm start script, if we take a look at that, that runs the node start.js.
3:29 In here, we have a very important step that runs before we actually start the application. That is to run npx prisma migrate deploy. What this does is it tells Prisma to look through the migrations and compare those with the database that's actually running and make sure to run all of the missing migrations that that running database is missing.
3:52 That's why we run the prisma migrate deploy before we actually run node on the build directory where our files were built. That's what the npm start command does. That's what we configure Docker to run when the image is ready and we're running in a container.
4:09 To test all of this out and make sure it's working, we're going to run docker build on the current directory. This will run everything in our Dockerfile. Because this is the first time, it may take a little while.
4:21 It has to install all of the apt-get dependencies. It's going to install our npm dependencies. Then one of the layers removes the dependencies on development things, so we only have production dependencies.
4:38 Finally, that Docker image is ready to go. If we say, "docker images," then we see we have our image ID right there. Just to test it out, let's do docker run. We need to forward along the port 8080. Then we're going to run with the flags i and t. Then we'll paste our image there. That gets everything running.
5:00 We can go to port 8080 on our local machine. This is being passed through to our image. We can see that our app is indeed working and running through that image. This image is ready for us to deploy to Fly, now that we have our Dockerfile all created.
5:17 You can see how this could change quite a bit based on the needs of your specific application, which is why I'm not going to go too deep into Docker and Dockerfiles in this tutorial. Hopefully, this is a good starting point for you, and you can adjust it as needed for your application.