Статичный сайт с Vite + Caddy + Docker: пошаговое руководство
В данной статье будет подробно рассмотрен полный цикл деплоя сайта, используюшего Vite в качестве инструмента сборки. Мы напишем многоступенчатый Dockerfile, чтобы собрать образ с готовой статикой, и поднимем контейнер с сервером Caddy, который автоматически выдаст и будет обновлять сертификаты Let's Encrypt.
Нам понадобится:
- Арендованный или домашний сервер на Ubuntu с публичным IP
- Домен, привязанный к IP
- Docker
- Готовый проект с vite
- Терминал
Структура проекта:
Подразумевается, что у вас уже есть проект использующий сборщик 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 и кэширования
# Привязанный домен, берется из .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
# Сборка сайта с использованием 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
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 автоматически подтянет их при запуске.
DOMAIN=ваш домен
EMAIL=ваша почтаПеренос кода на сервер
Легче всего доставить готовый проект можно с использованием системы контроля версий(GitHub, GitLab, Bitbucket)
На удаленной машине установите git(если отсутствует)
sudo apt update && sudo apt install git -y # Для Ubuntu/DebianПроверить можно следующей командой
git --version # git version 2.50.1Склонируйте проект и перейдите в корневую папку
git clone http://github.com/user/site.git && \
cd siteЗапуск
- Подключаемся к серверу по ssh
- В корне проекта выполняем команду
docker compose up --build -dПроверить работоспособность можно со своего компьютера через curl
curl domain.comИтог
Это решение представляет собой полноценную конфигурацию на базе Docker для развертывания React-приложений (собранных с помощью Vite). Оно включает многоэтапную сборку с использованием Node и caddy, автоматическую настройку TLS и правильные заголовки кэширования. Надеюсь данная статья оказалась полезной для вас.