How to use Next.js in Docker on DigitalOcean with NextAuth
Keeping this short and sweet for now because I just ran into this issue that happens infrequently to the point where I have a hard time remembering how to fix it, so here it goes.
Running your Next.js application in Docker on a VPS like DigitalOcean can be a bit of a brain exercise when it comes to think in more abstract terms about how to set it up correctly.
Now this post is probably going to grow as things become more fleshed out so let's consider this a journal of sorts.
Onto the first piece of information to remind my silly-self in the future.
Set the proper URL environment variables
This bit me the last couple days until one day after a nice pump at the gym and a shower, I clued into what the solution was.
The issue was, the UI portion of my Next.js app wasn't able to communicate to the API within the same application. This comes down to 2 (in my case) environment variables that need to be properly set: NEXT_URL and NEXTAUTH_URL.
The second variable may be unnecessary if you're not using NextAuth.
Anyway, in local development, NEXT_URL will probably be set to something like
and this tells the UI portion of the framework what url to hit the api with.
When you're running this in docker, this url needs to be the name of the docker container it's running in. For example:
because in my docker compose file, my container name is web.
That's it's proverbial localhost so instead of going outside the container to the URL in your browser, it needs to go to the container name itself.
Now there is another environment variable we need to set called NEXTAUTH_URL_INTERNAL. From my understanding and experiences, the NEXTAUTH_URL is responsible for handling callbacks from providers for example. It should be the URL of your website whereas NEXTAUTH_URL_INTERNAL should be the docker address of your container.
If you weren't running it in Docker, you could probably get away with just pointing it out localhost. Now that I'm writing this, is there ever a case where that would point to an actual domain? I don't think so.
Mounting environment variables via build secrets
Another thing that tripped me up was learning how to securely use your environment variables during your docker build. Because each run, copy etc., commands create layers which become cached, they can be inspected.
If your build image doesn't get pushed to docker hub then I believe you're fine, but it's probably a best practice to get used to just not copying in your environment variable file in general.
To mount your variables during build time without exposing them in anyway, we can use build secrets. This is handy for situations like private npm packages where you need to authenticate in order to download them. In my situation it was pro FontAwesome packages.
In your Dockerfile wherever you want whatever command to have access to your variables, you'll add this:
This will mount your environment variable file into the target location from which your npm run build can access them.
RUN --mount=type=secret,id=env,target=/app/.env npm run build
But that's not all.
In our docker compose file, we need to declare a secrets block like so
secrets: npmrc: file: $HOME/.npmrc env: file: .env
and then we need to associate our web service with these files.
build: context: . secrets: - npmrc - env
Now we can install our NPM packages or run scripts and have access to our environment variables without exposing them to the world. I cannot be held liable if you get hacked.
Ok, have fun.