Docker для Node.js

Практическое руководство по контейнеризации Node.js-приложения: от написания Dockerfile до запуска с docker-compose и публикации образа.

Зачем нужно

Docker позволяет упаковать Node.js-приложение вместе с нужной версией Node, переменными окружения и зависимостями в один образ. Разработчики и серверы запускают идентичную среду — исчезает проблема «у меня Node 18, на сервере Node 20». Образ легко версионируется и откатывается.

Где используется

  • Express / Fastify API: запуск в контейнере на VPS
  • Next.js: standalone-сборка внутри Docker
  • Node.js + PostgreSQL: связка через docker-compose для локальной разработки
  • Воспроизводимая среда для CI — GitHub Actions запускает тесты в Docker

Основной контент

Production Dockerfile для Node.js

# syntax=docker/dockerfile:1
FROM node:20-alpine AS base
WORKDIR /app

# Зависимости отдельным слоем — кэшируется!
FROM base AS deps
COPY package*.json() ./
RUN npm ci --omit=dev

# Сборка (для TypeScript проектов)
FROM base AS builder
COPY package*.json() ./
RUN npm ci
COPY . .
RUN npm run build          # tsc → dist/

# Финальный минимальный образ
FROM base AS runner
ENV NODE_ENV=production

RUN addgroup -S nodejs && adduser -S nodeuser -G nodejs

COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json() ./

USER nodeuser
EXPOSE 3000

CMD ["node", "dist/index.js"]

.dockerignore

node_modules
.git
.env
.env.*
dist
*.log
coverage
.DS_Store
README.md

docker-compose для разработки

# docker-compose.yml
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      NODE_ENV: development
      DATABASE_URL: postgres://user:pass@db:5432/devdb
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: devdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      retries: 5

volumes:
  pgdata:

Команды для работы

# Сборка образа
docker build -t myapp:latest .

# Запуск всего стека
docker compose up -d

# Логи
docker compose logs -f api

# Перезапуск только одного сервиса
docker compose restart api

# Пересборка после изменений в Dockerfile
docker compose up --build

# Очистка
docker compose down -v    # -v удаляет volumes

Обработка сигналов (graceful shutdown)

// server.js — важно для Docker
process.on('SIGTERM', () => {
  console.log('SIGTERM received, shutting down gracefully');
  server.close(() => {
    process.exit(0);
  });
});

Частые ошибки

  • Не указан .dockerignore: node_modules хоста копируется внутрь образа и перетирает npm ci
  • COPY . . до RUN npm ci — любое изменение кода сбрасывает кэш зависимостей
  • Приложение запускается как PID 1 без обработки сигналов — контейнер не завершается корректно (используй CMD ["node", "..."], не npm start)
  • Версия Node не закреплена: node:alpine — движущийся тег, может сломать сборку

Связанные темы

Ресурсы