Chuyển đến nội dung chính

Lesson 21: Deployment & Hosting

Nuxt deployment targets (Node, Static, Edge). Vercel, Netlify, Cloudflare Pages, AWS. Docker deployment, Dockerfile. GitHub Actions CI/CD. Environment variables management.

💻 Programming — Lesson 21 Lesson 21: Deployment & Hosting

Vue.js & Nuxt: From Basics to Advanced

Part 6: Testing, Deployment & Production

xdev.asia

1. Deployment Targets

PresetsOutputUse cases
node-serverNode.js serverVPS, Docker, PM2
static. staticStatic HTMLNetlify, GitHub Pages
cloudflare-pagesEdge WorkersCloudflare
vercelServerlessVercel
netlifyServerlessNetflix
// nuxt.config.ts
export default defineNuxtConfig({
  // Auto-detect hoặc set thủ công
  nitro: {
    preset: 'node-server', // 'cloudflare-pages' | 'vercel' | ...
  },
})

2. Docker Deployment

# Dockerfile
FROM node:20-alpine AS base
RUN corepack enable

FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

FROM base AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build

FROM base AS runtime
WORKDIR /app
COPY --from=build /app/.output ./.output

ENV NODE_ENV=production
ENV NUXT_HOST=0.0.0.0
ENV NUXT_PORT=3000
EXPOSE 3000

USER node
CMD ["node", ".output/server/index.mjs"]
# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    env_file: .env.production
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 5s
      retries: 3

3. Deploy to Vercel

# Install Vercel CLI
npm i -g vercel

# Deploy
vercel --prod
// vercel.json
{
  "buildCommand": "nuxt build",
  "outputDirectory": ".output",
  "framework": "nuxtjs"
}

4. Cloudflare Pages

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare-pages',
  },
})
# Deploy via Wrangler
npx wrangler pages deploy .output/public

5. Environment Variables

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // Server-only (NUXT_ prefix)
    databaseUrl: process.env.NUXT_DATABASE_URL,
    jwtSecret: process.env.NUXT_JWT_SECRET,
    // Public (NUXT_PUBLIC_ prefix)
    public: {
      apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api',
      appName: 'My App',
    },
  },
})
# .env.production
NUXT_DATABASE_URL=postgresql://user:pass@host:5432/db
NUXT_JWT_SECRET=super-secret-key
NUXT_PUBLIC_API_BASE=https://api.example.com

6. GitHub Actions CI/CD

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - run: pnpm install --frozen-lockfile

      - run: pnpm test
        env:
          NUXT_PUBLIC_API_BASE: http://localhost:3000

      - run: pnpm build

      - name: Deploy to server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            cd /app
            docker compose pull
            docker compose up -d --build

7. PM2 Production

// ecosystem.config.cjs
module.exports = {
  apps: [{
    name: 'nuxt-app',
    script: '.output/server/index.mjs',
    instances: 'max',
    exec_mode: 'cluster',
    env_production: {
      NODE_ENV: 'production',
      NUXT_PORT: 3000,
    },
  }],
}
pm2 start ecosystem.config.cjs --env production
pm2 save
pm2 startup

Next article: Production Monitoring & Best Practices — Sentry, logging, security.