Go + Nextjs Standalone Mode: GitHub Actions Build and Docker Deployment Example
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.jsinside it, which is more lightweight thannpm 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 }}
Comments 0