Wood Chen

Go + Nextjs Standalone Mode: GitHub Actions Build and Docker Deployment Example

0 comments138 views81 words

This post was translated from Chinese by AI. If anything reads oddly, the Chinese original is authoritative. 中文原文

The main difference is using the standalone directory generated by the frontend build and running node server.js inside it, which is more lightweight than npm run start

This is essentially a stripped-down NextJS server containing only its dynamic parts. Observations show that node_modules is only around 5% of the project's node_modules, a significant reduction in size (in our limited testing)

Backend

Build as usual

Frontend

next.config.ts

import type { NextConfig } from "next";

  

const nextConfig: NextConfig = {

  /* config options here */

  rewrites: async () => [

    {

      source: "/api/:path*",

      destination: "http://localhost:8080/api/:path*",

    },
    

  ],

  // Use standalone mode instead of static export

  output: "standalone",

  // output: "export",

  // distDir: "out",

  typescript: {

    ignoreBuildErrors: true

  },

  eslint: {

    ignoreDuringBuilds: true

  }

};

  

export default nextConfig;

Dockerfile

# Final runtime image

FROM node:22-alpine AS runner

WORKDIR /app

# Install required dependencies

RUN apk add --no-cache libc6-compat tzdata


# Set the time zone to China Standard Time

ENV TZ=Asia/Shanghai

  
# Copy the Go backend built by GitHub Actions

COPY czlconnect-backend /app/backend/


# Copy the Next.js standalone build output

COPY web/.next/standalone /app/frontend/

COPY web/.next/static /app/frontend/.next/static

COPY web/public /app/frontend/public


# Add the startup script

COPY scripts/start.sh /app/

RUN chmod +x /app/start.sh

  

# Set environment variables

ENV PORT=3000

ENV NODE_ENV=production

ENV GIN_MODE=release


# Set the user

RUN addgroup --system --gid 1001 nodejs

RUN adduser --system --uid 1001 nextjs

RUN chown -R nextjs:nodejs /app/frontend

USER nextjs

# Expose ports

EXPOSE 3000 8080

# Start the application

CMD ["/app/start.sh"]

Startup script start.sh

#!/bin/sh

  

# Ensure the time zone is set correctly

export TZ=Asia/Shanghai

  

# Start the backend application

cd /app/backend

./czlconnect-backend &

# Start the Next.js application

cd /app/frontend

# In standalone mode, npm install and npm run build are not needed

# Start server.js directly with node

node server.js

# No extra logic is needed to keep the container running, since the node process keeps running

# If the node process exits, the container also exits, and Docker can restart it

github actions example

name: 构建并推送Docker镜像

  

on:

  push:

    branches: [ main ]

  workflow_dispatch:

  

jobs:

  build:

    runs-on: ubuntu-latest

    steps:

    - name: 检出代码

      uses: actions/checkout@v4

    - name: 设置Go环境

      uses: actions/setup-go@v5

      with:

        go-version: '1.23'

    - name: 设置Node.js环境

      uses: actions/setup-node@v4

      with:

        node-version: '22'

        cache: 'npm'

        cache-dependency-path: web/package-lock.json

    - name: 构建后端

      run: |

        go mod download

        # Add CGO_ENABLED=0 to ensure a static build, and include time zone data

        CGO_ENABLED=0 go build -ldflags="-w -s" -tags timetzdata -o czlconnect-backend .

    - name: 构建前端

      run: |

        cd web

        # Install dependencies and build (using standalone mode)

        npm ci

        npm run build

        # Check that the standalone build files were generated successfully

        if [ ! -d ".next/standalone" ]; then

          echo "Next.js standalone构建失败,缺少standalone目录"

          exit 1

        fi

    - name: 设置Docker Buildx

      uses: docker/setup-buildx-action@v3

    - name: 登录到Docker Hub

      if: github.event_name != 'pull_request'

      uses: docker/login-action@v3

      with:

        username: ${{ secrets.DOCKER_HUB_USERNAME }}

        password: ${{ secrets.DOCKER_HUB_TOKEN }}

    - name: 构建并推送Docker镜像

      uses: docker/build-push-action@v6

      with:

        context: .

        push: true

        tags: ${{ secrets.DOCKER_HUB_USERNAME }}/czlconnect:latest

        # Add build arguments

        build-args: |

          BUILD_DATE=${{ github.event.repository.updated_at }}

          VCS_REF=${{ github.sha }}

Related posts

Comments 0