Next.js configuration: reduce image size

Enable Standalone Mode

In order to reduce the volume after Next.js Build, you can configure the Standalone mode directly in next.config.js :

// next.config.js
/** @type {import('next').NextConfig} */
module.exports = {
  // ...
  output: 'standalone',
  // ...
}

Once configured, Next.js will automatically perform tree shaking during the build phase and copy almost all necessary files to /.next/standalone. In the next step, it can be copied directly into the Docker image and pushed to the repo. But it should be noted that:

  1. In Standalone mode, public and ./next/static folders not is copied into standalone. Next.js believes that these static files should be distributed by CDN by default, not deployed to the server. For personal use, the files should be copied manually in the Dockerfile.
  2. All environment files, including .env, serverRuntimeConfig or publicRuntimeConfig are read and stored during the build phase. That is:
    1. The environment file should be loaded in the build, otherwise there will be a bug;
    2. All environment files are immutable.

For point 2, someone has proposed a solution in the github discussion area, see: https:// github.com/vercel/next.js/discussions/34894#discussioncomment-2864423

However, since the solution at this stage is still complicated, it is not recommended to enable the Standalone mode for the time being.

cssnano reduces build file size

In addition, cssnano (https://cssnano.co/) can be configured in postcss to reduce the size of the built css file. Post my configuration:

// postcss.config.js
module.exports = {
    plugins: {
        // ...
        'cssnano': (process.env.NODE_ENV === 'production' ? {
            preset: [
                'default',
                {
                    discardComments: {
                        removeAll: true,
                    },
                }
            ]
        } : false)
    },
    // ..
}

Dockerfile configuration

# ------------- deps ----------------
FROM node:lts-alpine AS deps
RUN apk add --no-cache libc6-compat git
WORKDIR /opt/app
COPY package.json package-lock.json ./
RUN npm install --frozen-lockfile --force


# ------------- builder ----------------
# Rebuild the source code only when needed
FROM node:lts-alpine AS builder
ENV NODE_ENV=production

WORKDIR /opt/app
COPY . .
# copy all modules from build_image
COPY --from=deps /opt/app/node_modules ./node_modules
# 这里可以运行自定义命令
RUN  cat .env\ # 读取环境文件用于构建时
     && npm run build \ # 运行next build
     && npm prune --omit dev --omit optional --force # 清理devDependencies,以减少包体积

# ------------- image ----------------
# Production image
FROM node:lts-alpine AS runner
WORKDIR /opt/app

USER root
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001

# 复制运行所需文件
COPY --from=builder /opt/app/node_modules ./node_modules
COPY --from=builder /opt/app/next.config.js ./
COPY --from=builder /opt/app/next-sitemap.config.js ./
COPY --from=builder --chown=nextjs:nodejs /opt/app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /opt/app/public ./public

# 复制.env文件用于运行时
COPY .env ./

# 暴露端口
EXPOSE 80

USER nextjs

# 指定端口运行服务器
CMD /opt/app/node_modules/.bin/next start -p 80

GitHub Actions file configuration

build task

# This is a workflow to build image and push releases to repository


name: Build

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches:
      - main

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

env:
  DOMAIN: "registry.****.com" # 这里替换成服务商的Registry域名
  REGISTRY: "****" # 这里替换成仓库名称
  IMAGE_NAME: "****" # 这里替换成镜像的名称

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:

  build_and_push:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v2

      - name: create env
        uses: SpicyPizza/create-envfile@v1
        with:
          envkey_DATABASE_URL: ${{secrets.ENV_DB_STR}} # 这里可以任意添加构建镜像时需要的环境变量,只要在环境变量名前添加"envkey_"即可

      # 添加私有软件包源至npm配置文件,若没有可删除此步骤
      - name: config private package
        run: |
            echo "//npm.pkg.github.com/:_authToken=${{secrets.GH_PKG_AUTH_TOKEN}}" > ./.npmrc
            echo "@***:registry=https://npm.pkg.github.com" >> ./.npmrc

      # 登录至registry
      - name: Login to Container Registry
        uses: docker/login-action@v1
        with:
          registry: ${{env.DOMAIN}}
          username: ${{ secrets.ALIYUN_REPO_USERNAME }}
          password: ${{ secrets.ALIYUN_REPO_PASSWORD }}

      # 构建并推送镜像
      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          platforms: linux/amd64
          push: true
          tags: |
            ${{env.DOMAIN}}/${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest

server pull image

First, you need to configure the docker-compose configuration file in the corresponding folder of the server, and then use GitHub Actions to automatically pull it after the build:

name: Deploy

# Controls when the workflow will run
on:
  # Triggers the workflow when build is completed
  workflow_run:
    workflows: ["build"]
    types:
      - completed

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:


jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
      - name: Deploy to Server via SSH action
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          port: ${{ secrets.DEPLOY_PORT }}
          username: ${{ secrets.DEPLOY_USERNAME}}
          key: ${{ secrets.DEPLOY_KEY }}
          passphrase: ${{ secrets.DEPLOY_PASSPHRASE }}
          script_stop: true
          script: |
            # 替换为docker-compose.yml文件目录
            cd /****/ 

            # Pull latest build
            docker-compose pull

            # Run Container
            docker-compose up -d --build --remove-orphans

            # Clean old images
            docker container prune -f
            docker image prune -f

            echo Container updated successfully.

THIS SERVICE MAY CONTAIN TRANSLATIONS POWERED BY GOOGLE. GOOGLE DISCLAIMS ALL WARRANTIES RELATED TO THE TRANSLATIONS, EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OF ACCURACY, RELIABILITY, AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

Comments

Create Comment

Your email is not known to other users and is only used to generate unique random nicknames.