Skip to content

Статичный сайт с Vite + Caddy + Docker: пошаговое руководство

В данной статье будет подробно рассмотрен полный цикл деплоя сайта, используюшего Vite в качестве инструмента сборки. Мы напишем многоступенчатый Dockerfile, чтобы собрать образ с готовой статикой, и поднимем контейнер с сервером Caddy, который автоматически выдаст и будет обновлять сертификаты Let's Encrypt.

Нам понадобится:

  1. Арендованный или домашний сервер на Ubuntu с публичным IP
  2. Домен, привязанный к IP
  3. Docker
  4. Готовый проект с vite
  5. Терминал

Структура проекта:

Подразумевается, что у вас уже есть проект использующий сборщик Vite, в качестве примера будет показан простой сайт с документацией на VitePress, конфигурация для docker и caddy находится в корневой папке проекта

root
├── Caddyfile
├── Dockerfile
├── docker-compose.yaml
├── docs
│        ├── index.md
│        └── .vitepress
├── package-lock.json
└── package.json

Сборка проекта

При выполнении команды npm run build создается папка /dist, содержащая файл index.html и плоскую структуру каталога /assets с файлами .js и .css. Сервер Node.js здесь не задействован - на выходе получаются исключительно статические файлы.

Это важно для Docker в продуктовой среде Node.js не требуется. Нужен только сервер для раздачи статических файлов. В качестве сервера в данном примере выбран Caddy — современный веб-сервер, написанный на Go, который распространяется как единый статически скомпилированный бинарник без внешних зависимостей.

Его ключевые преимущества:

  • Автоматический HTTPS - Caddy получает и обновляет сетрификаты Let's Encrypt по протоколу ACME прямо из коробки, без необходимости настраивать Certbot или монтировать volume с сертификатами, Для продуктовой среды это означает, что достаточно указать домен в Caddyfile, и сервер сам позабоитстя о шифровании.
  • Простая конфигурация - синтаксис Caddyfile интуитивно понятен и лаконичен. Для раздачи статики из /dist нужна всего одна строча: root * /usr/share/caddy + file_server. В отличии от Nginx, где для базового кейса требуется 10-15 строк с location, alias, try_files и типами mime.

Из минусов можно отметить больший оверхед по ресурсам (из-за рантайма Go), но в контексте раздачи статических файлов разница в 5-10% производительности незаметна.

Конфигурация Caddy

Ниже приведен пример конфигурации для сервера, с настройками домена, TLS и кэширования

Caddyfile
# Привязанный домен, берется из .env файла конфигурации

{$DOMAIN} {
    # Почта для получения сертификатов Let's Encrypt
    tls {$EMAIL}

    # Указывает, где находятся файлы сайта
    # * означает, что правило применяется ко всем запросам
    # Путь /usr/share/caddy — стандартная папка для статики в Caddy
    root * /usr/share/caddy

    # Включает сервер для отдачи файлов из корневой директории
    # Автоматически определяет MIME-типы
    file_server

    # Включает GZIP-сжатие для ответов сервера
    encode gzip

    # Устанавливает заголовки кеширования для статических ресурсов:
    header /assets/* Cache-Control: "public, max-age32536000, immutable"
    header /*.css Cache-Control: "public, max-age=31536000, immutable"
    header /*.js Cache-Control: "public, max-age=31536000, immutable"

    # Попытка найти файл → если нет, то папку → если ничего нет, отдать index.html
    try_files {path} {path}/ /index.html

    # Заголовки безопасности

    header {
      # Требует HTTPS в течение 2 лет (63,072,000 сек)
      Strict-Transport-Security "max-age=63072000"
      # Запрещает браузеру определять MIME-тип (защита от MIME-атак)
      X-Content-Type-Options "nosniff"
      # Включает защиту от XSS-атак
      X-XSS-Protection "1; mode=block"
      # Запрещает встраивание сайта во frame/iframe (защита от clickjacking)
      X-Frame-Options "DENY"
    }
}

Сборка проекта, Dockerfile

Dockerfile
# Сборка сайта с использованием Node.js

FROM node:24-slim AS builder

WORKDIR /app

# Установка зависимостей (кешируется, если не менялся package.json)

COPY package*.json ./
RUN npm ci

# Копирование исходников и сборка

COPY docs ./docs
RUN npm run docs:build # Результат: /app/docs/.vitepress/dist

# Финальный образ Alpine + Caddy

FROM alpine:3.23.5

# Установка веб-сервера Caddy

RUN apk add --no-cache caddy

# Копируем собранный сайт из первого этапа в стандартную папку Caddy

COPY --from=builder /app/docs/.vitepress/dist /usr/share/caddy

# Копируем конфиг Caddy

COPY Caddyfile /etc/caddy/Caddyfile

# Открываем порты HTTP/HTTPS

EXPOSE 80 443

# Запуск Caddy

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

Конфигурируем docker-compose.yml

yaml
version: "3.8"

services:

# Название сервиса (может быть любым)

caddy:
  build: # Dockerfile находится в текущей директории (.)
    context: . # Используем файл с именем Dockerfile для сборки образа
    dockerfile: Dockerfile

  container_name: vitepress-site

  # Политика перезапуска:
  # - контейнер будет автоматически перезапускаться, если упадёт
  # - не будет перезапускаться, только если остановлен вручную
  restart: unless-stopped

  ports:
    # Открываем порт 80 (HTTP)
    - "80:80"
    # Открываем порт 443 (HTTPS)
    - "443:443"

  volumes:
    # Том для хранения данных Caddy (сертификаты, база данных)
    - caddy_data:/data
    # Том для конфигурации Caddy
    - caddy_config:/config

  environment:
    # Передаём переменную окружения DOMAIN из .env файла
    - DOMAIN=${DOMAIN}
    # Передаём переменную окружения EMAIL из .env файла
    - EMAIL=${EMAIL}

  networks:
    - web

volumes:
  caddy_data:
  caddy_config:

networks:
  web: # Создаём сеть с драйвером bridge (стандартная сеть Docker)
  driver: bridge

Файл с переменными .env

Перед запуском docker compose в корне проекта нужно создать .env файл с переменными, docker автоматически подтянет их при запуске.

properties
DOMAIN=ваш домен
EMAIL=ваша почта

Перенос кода на сервер

Легче всего доставить готовый проект можно с использованием системы контроля версий(GitHub, GitLab, Bitbucket)

На удаленной машине установите git(если отсутствует)

bash
sudo apt update && sudo apt install git -y # Для Ubuntu/Debian

Проверить можно следующей командой

bash
git --version # git version 2.50.1

Склонируйте проект и перейдите в корневую папку

bash
git clone http://github.com/user/site.git && \
  cd site

Запуск

  • Подключаемся к серверу по ssh
  • В корне проекта выполняем команду
bash
docker compose up --build -d

Проверить работоспособность можно со своего компьютера через curl

bash
curl domain.com

Итог

Это решение представляет собой полноценную конфигурацию на базе Docker для развертывания React-приложений (собранных с помощью Vite). Оно включает многоэтапную сборку с использованием Node и caddy, автоматическую настройку TLS и правильные заголовки кэширования. Надеюсь данная статья оказалась полезной для вас.