Статичный сайт с Vite + Caddy + Docker: пошаговое руководство
- вторник, 4 августа 2026 г. в 00:00:05
В данной статье будет подробно рассмотрен полный цикл деплоя сайта, используюшего 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% производительности незаметна.
Ниже приведен пример конфигурации для сервера, с настройками домена, 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" } }
# Сборка сайта с использованием 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"]
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
Перед запуском 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 domain.com
Это решение представляет собой полноценную конфигурацию на базе Docker для развертывания React-приложений (собранных с помощью Vite). Оно включает многоэтапную сборку с использованием Node и caddy, автоматическую настройку TLS и правильные заголовки кэширования. Надеюсь данная статья оказалась полезной для вас.