I cannot seem to get my node:alpine Docker image for a react/next.js to build.
I can get it to build fine locally, but I never see the traffic in my API logs when the Docker build reaches out to the API endpoint, which is itself running in a Docker container (nginx, headless craft cms, etc.).
The build does not seem to like localhost at all, as I’ve tried:
http://localhost:9000/api
…and this is the message I get in the logs:
#15 13.76 > Build error occurred
#15 13.76 FetchError: request to http://localhost:9000/api failed, reason: connect ECONNREFUSED 127.0.0.1:9000
I’ve heard that I can use the hostname in place of "localhost", so in my MacOS terminal I’ve typed "hostname" and I’ve swapped out the localhost for this value. This doesn’t outright error out, but the build hangs on the "Generating pages…" step of the build.
How can I get the Docker build to recognize localhost, or put another way, how can I set my API URL to an endpoint hosted by a local Docker container?
Here is my Dockerfile:
# Install dependencies only when needed
FROM node:alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn --frozen-lockfile
# Rebuild the source code only when needed
FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn static
# Production image, copy all the files and run next
FROM node:alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# You only need to copy next.config.js if you are NOT using the default configuration
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
USER nextjs
EXPOSE 3000
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry.
# ENV NEXT_TELEMETRY_DISABLED 1
CMD ["yarn", "start"]
4
Answers
Okay, I figured out the solution, or a solution rather.
After digging around, I found that I needed to connect to the "bridge network".
I found out what the bridge network IP was by first typing in "docker network ls":
This gave me the name of the network I needed to get the IP for, I did this by typing "docker network inspect craftcms_default":
This outputs quite a bit in my case, but the only thing I need is "Gateway" IP, which here is 172.19.0.1
My nginx server is mapped 9000:80, so the headless Craft CMS API is at:
http://172.19.0.1:9000/api
...however, as a quick sanity check I entered this into the browser and the result was an ever spinning/loading page. Undeterred, I entered this into env.local as the PUBLIC_NEXT_API_URL and the static site generation completed fine during the Docker build.
I'm still experimenting with an elegant way to set this env var within the Docker build, so if anyone has any suggestions I'd love to hear!
This is was an interesting challenge considering I couldn't find any answers/Google results specifically for a process running as a result of a Docker build being unable to make a fetch to a URL hosted within another locally running container. Hopefully this helps anyone else struggling with this issue!
Thanks for your post – it helped me get to a solution that should do the trick. I found that defining the bridge network gateway is a more elegant solution; it allows you to define the PUBLIC_NEXT_API_URL and never change it.
Here’s how you can do it in a docker-compose file:
Now you can set
PUBLIC_NEXT_API_URL to http://172.28.0.1:3008/api,
and be confident that this will be the endpoint every time you fire up the docker-compose.Also, note that you may need to remove an existing network and then
"docker-compose up --force-recreate"
in order to get the IP correct.I know this is an old issue but I’ll leave my solution here for anyone new. Although the given solution did work for me I looked around for something more elegant. This problem apparently occurs because the
localhost
hostname refers always to the current container’s localhost and not that of your host machine.Buried in the docker for mac docs they refer to using
host.docker.internal
to connect from a container to another service. Using the container name as recommended in many other answers did not help me, but usinghost.docker.internal
fixed the issue.docs:
https://docs.docker.com/docker-for-mac/networking/
reference:
https://forums.docker.com/t/localhost-and-docker-compose-networking-issue/23100/5
If you’re using docker-compose, normally you could reference containers using their names, which would resolve to their IP in Docker’s default network. However, it seems that this does not work during build time.
On Mac, you can connect to containers on the host through
host.docker.internal
as @MikejdeGroot said. So you could usehttp://host.docker.internal:9000/api
.On Linux, you can consider using host networking during build time, e.g.
With this config, you can use
http://localhost:9000/api
.