dockerdocker-composebuildrebuild

Docker compose is not updating source code


i have some problem with updating && rebuiling my code base

I use this command, but it's doesn't update my code base docker compose up --build -d --force-recreate

I tried before compose up use this command for clear data docker compose down -v --rmi all --remove-orphans and this works good. How can i use docker compose up without removing all data and etc everytime before building (docker-nextjs container)?

Docker version 24.0.5, build ced0996

Dockerfile

FROM node:18-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
  else echo "Lockfile not found." && exit 1; \
  fi


# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 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 during the build.
# ENV NEXT_TELEMETRY_DISABLED 1

RUN npm run build

# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app

ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

docker-compose.yml

version: '3'

services:
  nextjs-app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: docker-nextjs
    restart: always
    environment:
      NODE_ENV: production
    volumes:
      - nextjs-app-build:/app # Named volume for nextjs-app build folder
    ports:
      - '3000:3000'
    networks:
      - nginx_network
  nginx:
    image: csrinu236/medium-nginx-app # Placeholder for pushing image to Dockerhub
    build:
      context: ./nginx-docker
      dockerfile: Dockerfile
    volumes:
      - nextjs-app-build:/app # Mount the named volume to Nginx's /app/build
    depends_on:
      - nextjs-app
    restart: always
    ports:
      - '80:80'
      - '443:443'
    command: ['sh', '/etc/nginx/convert-nginx.sh']
    networks:
      - nginx_network

volumes:
  nextjs-app-build: # Define the named volume

networks:
  nginx_network:
    external: true

Solution

  • Delete all of the volumes: blocks in the file. More specifically, delete the volumes: that are mounted over the image's /app directory.

    What's happening here is that there's an old copy of the application in the nextjs-app-build named volume. You are successfully rebuilding the image, but then you're mounting the volume over its contents, and the contents of the named volume just outright replace what's in the rebuilt image.

    It looks like you're trying to take advantage of a specific behavior of specificially Docker named volumes to share files from one container to the other. When you mount a Docker named volume to a container (and only a Docker named volume, not a bind mount or a Kubernetes PersistentVolumeClaim or anything else), the first time the volume is used (and only if the volume is totally empty), contents from the image are copied into the volume. This causes the first image's content to be copied into the volume, which is then mounted into the second container. But, if you ever update the image content, the volume doesn't update (Docker doesn't know anything about its contents and assumes it has user data of some sort) and you get this behavior.

    This begs the question: how do your static assets get into the reverse proxy?

    The easiest solution is to just not bother. In most reasonably modern application frameworks you can set up a static-file server in the application itself. At that point you can just forward requests to the backend, and let that deal with its own files.

    # in your nginx config
    location / {
      proxy_pass http://nextjs-app:3000/;
      # with no try_files
    }
    

    Another option is to COPY the files into the Nginx proxy image. You could do this in the same Dockerfile, as an additional stage at the end

    FROM nginx AS proxy
    COPY --from=builder /app /app # (...or to /var/www/html?)
    COPY ./nginx-docker/convert-nginx.sh /etc/nginx
    CMD ["/etc/nginx/convert-nginx.sh"]
    

    Then in your Compose file, you'd need to specify which part of the Dockerfile to use.

    version: '3.8'
      services:
        build:
          context: .
          target: runner # <-- add
        restart: always
        ports:
          - '3000:3000'
      nginx:
        image: csrinu236/medium-nginx-app # Placeholder for pushing image to Dockerhub
        build:
          context: .     # <-- change to top-level directory (same as app)
          target: proxy  # <-- add
        depends_on:
          - nextjs-app
        restart: always
        ports:
          - '80:80'
          - '443:443'
    
    # also note no volumes:, and using the default Compose-provided networks:
    

    You can also COPY --from an image name, which would let you keep the two separate Dockerfiles (assuming you're assigning an image: name to both). The one trick with this is that Compose doesn't have a way to order image builds, so you have to manually build first the application and then the proxy.