Когда на небольшом VDS крутится несколько сервисов, вопрос логов встаёт быстро: grep по файлам неудобен, ротация хаотична, а поиск по ошибкам — боль. Полноценные ELK/Opensearch-стэки слишком прожорливы. Нужен лёгкий центр логирования, который выдержит «боевую» нагрузку, не съест всё место на диске и не потребует постоянного шаманства. Разумный компромисс — Loki в одном процессе плюс Promtail на источниках. В этой статье собраны проверенные практики, готовые конфиги и «страховка» от классических проблем с ретеншном и кардинальностью меток.
Зачем Loki на малом VDS и что получится
Loki принципиально дешевле по ресурсам: он индексирует не сам текст строк, а метки (labels), а содержимое хранит в сжатых чанках. Это даёт ощутимую экономию CPU и диска. В едином процессе Loki спокойно работает на 1–2 vCPU и 1–2 ГБ RAM при сотнях событий в секунду и ретеншне 7–30 дней, если не раздувать метки.
Цель этого гайда:
- Развернуть Loki в однопроцессном режиме с хранением на локальном диске и включённым ретеншном.
- Запустить Promtail на том же VDS (и, при необходимости, на удалённых хостах) с аккуратными метками и позициями чтения.
- Настроить безопасную публикацию приёма логов (варианты с привязкой к localhost и через обратный прокси).
- Дать минимальные, но достаточные лимиты, чтобы «не уронить» машину при всплеске логов.
Архитектура без излишеств
Минимальная схема:
- Один Loki в режиме «single process». Хранение на локальном диске: индексы через
boltdb-shipper, чанки в файловой системе. Включёнcompactorдля ретеншна. - Promtail на каждом источнике. Для самого узла с Loki — локальный Promtail, который читает
/var/logи контейнерные логи (если нужны). - При необходимости приёма логов с внешних машин — публикация порта через обратный прокси с базовой аутентификацией и ограничениями, плюс межсетевой экран.
Почему не отправлять напрямую в Loki без прокси? Можно, но прокси помогает ограничивать размер запросов и скорость, давать базовую аутентификацию и скрывать внутренние детали, а также упростить ротацию сертификатов и иметь единую точку входа.

Оценка ресурсов: диски, CPU и память
Грубая прикидка диска под логи: возьмите средний объём логов в сутки (в несжатом виде) и умножьте на коэффициент 0.3–0.5 — это ориентир с учётом сжатия Loki. Например, при 1 ГБ/сутки и ретеншне 14 дней требуется 1 ГБ × 0.4 × 14 ≈ 5.6 ГБ под чанки плюс 10–20% на индексы и служебные файлы. Заложите запас ×1.5 на всплески и неравномерность.
CPU: 1 vCPU хватает для десятков-сотен событий/сек. RAM: 1–2 ГБ достаточно при умеренной кардинальности меток и разумных лимитах. Для выбора железа и ОС на малых серверах см. обзор производительности на ARM в материале ARM VDS: производительность PHP/Node.
Контроль меток: как не устроить взрыв кардинальности
В Loki дорого индексируются не строки логов, а метки. Чем больше уникальных наборов меток (streams), тем больше индексов и RAM. Не превращайте каждую строку в отдельный поток.
Рекомендации:
- Оставьте короткий и стабильный набор меток:
job,host,app,env. Этого обычно достаточно. - Не включайте в метки динамические значения:
request_id,user_id,path,pod_uid,container_hashи т. п. Если они нужны в поиске — оставляйте в тексте строки. - Если собираете контейнерные логи, жёстко отфильтруйте лишние Docker/Kubernetes-метки через relabeling.
- Ставьте лимиты:
max_streams_per_user,ingestion_rate_mb,ingestion_burst_size_mb,max_label_names_per_series,max_line_size.
Файловое хранение и ретеншн «без боли»
Для одного узла идеальный вариант — схема boltdb-shipper для индексов и filesystem для чанков. Ретеншн включается в секции compactor и limits_config. compactor периодически сжимает и удаляет устаревшие чанки согласно срокам хранения. Это работает надёжно и не требует внешнего объектного хранилища.
Обратите внимание на границы времени: retention_period — верхний предел. При пиковых нагрузках и активной компрессии фактическое удаление старых чанков может происходить с небольшой задержкой (в пределах интервала компактинга).

Шаги внедрения (вариант с контейнерами)
Ниже — последовательность действий в стиле «минимум магии», которую удобно восстанавливать и переносить между серверами. Пример используется как шаблон: версии образов и пути подстраивайте под себя.
1) Каталоги и права
Создайте рабочие директории под конфиги и данные. Важно заранее выделить отдельный путь для данных Loki, чтобы удобнее контролировать диск и бэкапы.
mkdir -p /opt/logs-stack/loki
mkdir -p /opt/logs-stack/promtail
mkdir -p /opt/logs-stack/nginx
mkdir -p /var/lib/loki
2) Конфиг Loki с ретеншном
Конфигурация для одиночного процесса с хранением в файловой системе и включённым compactor. В примере задан ретеншн 14 дней, адекватный для небольшого VDS.
# /opt/logs-stack/loki/loki.yml
server:
http_listen_port: 3100
http_listen_address: 127.0.0.1
grpc_listen_port: 0
log_level: info
auth_enabled: false
common:
path: /var/lib/loki
replication_factor: 1
ring:
instance_addr: 127.0.0.1
storage_config:
boltdb_shipper:
active_index_directory: /var/lib/loki/index
cache_location: /var/lib/loki/index-cache
shared_store: filesystem
filesystem:
directory: /var/lib/loki/chunks
schema_config:
configs:
- from: 2024-01-01
store: boltdb-shipper
object_store: filesystem
schema: v12
index:
prefix: index_
period: 24h
limits_config:
retention_period: 336h # 14 дней
ingestion_rate_mb: 8
ingestion_burst_size_mb: 16
max_streams_per_user: 2000
max_entries_limit_per_query: 5000
max_line_size: 256kb
max_label_names_per_series: 20
reject_old_samples: true
reject_old_samples_max_age: 168h
chunk_store_config:
max_look_back_period: 336h
ingester:
chunk_idle_period: 10m
chunk_target_size: 1048576 # ~1 МБ
max_transfer_retries: 0
compactor:
working_directory: /var/lib/loki/compactor
shared_store: filesystem
compaction_interval: 5m
retention_enabled: true
3) Конфиг Promtail для локальных логов
Promtail будет читать системные логи и, при необходимости, контейнерные. Важно: минимум меток и фильтрация динамических значений.
# /opt/logs-stack/promtail/promtail.yml
server:
http_listen_port: 9080
http_listen_address: 127.0.0.1
positions:
filename: /var/lib/promtail-positions.yaml
clients:
- url: http://127.0.0.1:3100/loki/api/v1/push
batchwait: 1s
backoff_config:
min_period: 500ms
max_period: 5s
max_retries: 10
scrape_configs:
- job_name: syslog
static_configs:
- targets: [localhost]
labels:
job: syslog
host: ${HOSTNAME}
env: prod
__path__: /var/log/*.log
pipeline_stages:
- drop_multiline: false
- job_name: journal
journal:
path: /var/log/journal
max_age: 12h
relabel_configs:
- source_labels: ['__journal__systemd_unit']
target_label: 'app'
static_configs:
- targets: [localhost]
labels:
host: ${HOSTNAME}
env: prod
- job_name: docker
static_configs:
- targets: [localhost]
labels:
job: docker
host: ${HOSTNAME}
env: prod
__path__: /var/lib/docker/containers/*/*.log
pipeline_stages:
- json:
expressions:
stream: stream
attrs: attrs
- labels:
stream:
- drop:
source: attrs
expression: '.*'
4) Обратный прокси (опционально)
Если надо принимать логи с других машин, публикуйте Loki через прокси. Простой вариант — Nginx с ограничениями размера запроса и базовой авторизацией. Для локального узла оставьте bind на 127.0.0.1, а внешний доступ давайте только через прокси и файрвол.
# /opt/logs-stack/nginx/loki.conf
server {
listen 80;
server_name _;
client_max_body_size 10m;
keepalive_timeout 15;
# auth_basic "Restricted"; # включите при необходимости
# auth_basic_user_file /etc/nginx/loki.htpasswd;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_pass http://127.0.0.1:3100;
}
}
5) Docker Compose для запуска
Один файл — и весь стек поднимется разом. Порт Loki «наружу» не отдаём, только локально; наружу — только Nginx (если нужен).
# /opt/logs-stack/docker-compose.yml
version: "3.8"
services:
loki:
image: grafana/loki:2.9.0
command: -config.file=/etc/loki/loki.yml
volumes:
- /var/lib/loki:/var/lib/loki
- ./loki/loki.yml:/etc/loki/loki.yml:ro
network_mode: host
restart: unless-stopped
promtail:
image: grafana/promtail:2.9.0
command: -config.file=/etc/promtail/promtail.yml
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail/promtail.yml:/etc/promtail/promtail.yml:ro
- /var/lib/promtail-positions.yaml:/var/lib/promtail-positions.yaml
network_mode: host
restart: unless-stopped
# Включайте только если нужен приём с внешних хостов
nginx:
image: nginx:stable
volumes:
- ./nginx/loki.conf:/etc/nginx/conf.d/default.conf:ro
ports:
- "80:80"
depends_on:
- loki
restart: unless-stopped
6) Запуск и проверка
Запустите стек, убедитесь, что Loki принимает записи, а Promtail отдаёт.
cd /opt/logs-stack
docker compose up -d
# Проверка локи-эндпоинтов (должны отвечать 200)
curl -sS localhost:3100/ready
curl -sS localhost:3100/metrics | head -n 5
# Проверка promtail
curl -sS localhost:9080/metrics | head -n 5
Ретеншн: время хранения и «санитария» диска
Секцию limits_config.retention_period подбирайте от задач: часто хватает 7–14 дней. Для инцидент-менеджмента можно держать 30 дней, но проверяйте влияние на диск. compactor.retention_enabled обязан быть включён. Интервал компактинга 5 минут — разумный баланс для маленького узла.
Чтобы не «съесть» диск при всплеске логов:
- Ставьте лимиты приёма:
ingestion_rate_mbиingestion_burst_size_mb. - Делайте мониторинг свободного места и метрик Loki: рост количества чанков, промахи по лимитам, ошибки 429/5xx.
- Не храните неструктурированный поток «всё подряд» — фильтруйте шум на стороне Promtail (например, health-check строки).
Безопасность: минимально необходимые меры
Базовые шаги:
- Не слушайте 0.0.0.0 у Loki, оставьте
127.0.0.1. Внешний доступ — только через прокси с аутентификацией. - В файрволе открывайте только порт прокси. Ограничивайте доступ по адресам источников логов.
- В прокси ограничьте
client_max_body_sizeи таймауты, включите keepalive в разумных пределах. - Для внешнего трафика используйте TLS: оформить и автоматизировать помогут SSL-сертификаты. Для доверенных агентов можно настроить mTLS на прокси.
- В Promtail не храните чувствительные данные в метках. При необходимости редактируйте строки через
pipeline_stages(маскирование секретов, удаление PII).
Тонкая настройка производительности
Важные «крутилки» Loki:
chunk_target_size: около 1 МБ — хороший старт для баланса компрессии/поиска.chunk_idle_period: через сколько «закрывать» чанк при низком трафике; 10 минут — компромисс.max_look_back_period: ограничивает глубину поиска и экономит ресурсы.max_entries_limit_per_query: ограничивает объём выдачи, защищая узел от тяжёлых запросов.
В Promtail контролируйте пакетирование (batchwait), ретраи (backoff_config) и берегите диск: файл позиций храните на отдельном томе, не на перегруженном корневом разделе.
Типовые ошибки и быстрые фиксы
- Пустой индекс при больших объёмах и коротких чанках. Увеличьте
chunk_target_sizeиchunk_idle_period, уменьшите количество потоков (меток). - Большой расход RAM на индексы: проверьте кардинальность — вероятно, в метках «протекли» динамические значения. Урезайте через
relabel_configs. - HTTP 429 (rate limit). Источник «льёт» слишком быстро. Увеличивайте лимиты аккуратно либо зажимайте шум на стороне Promtail. Иногда помогает увеличить
batchwait. - Диск заполняется не по плану. Проверьте, включён ли
compactorи верно ли стоитretention_period. Убедитесь, что пути хранения чанков и индексов те, что вы планировали. - Проблемы с правами/SELinux/AppArmor. Promtail должен иметь доступ на чтение. Проверьте монтирование, контексты и группы.
Наблюдаемость и бэкапы
Loki и Promtail публикуют полезные метрики: статус готовности, объём принятых записей, количество активных потоков, результаты ретраев, состояние компактора. Снимайте их и стройте алерты: переполнение диска, рост ошибок 5xx, всплески 429, падение количества записей.
Бэкап локального хранилища: индексы (index, index-cache) и чанки. На малом VDS практично делать регулярные снепшоты тома или файловый бэкап вне пиков. Согласуйте с ретеншном: нет смысла тянуть в бэкап то, что завтра удалит compactor. При ресторе соблюдайте целостность каталогов.
Как масштабировать дальше
Если нагрузка растёт, а место и retention хочется увеличивать, есть два пути:
- Вертикально: побольше CPU/RAM/диска, более быстрый SSD, пересмотр лимитов и фильтров.
- Горизонтально: переход к объектному хранилищу для чанков и распределённой топологии Loki. Но это уже другой класс сложности и выходит за рамки «малого VDS».
Чек-лист внедрения
- Определите целевой ретеншн (7–14 дней) и дневной объём логов.
- Подготовьте диск и каталоги данных для Loki.
- Настройте Loki с
boltdb-shipper,filesystem, включитеcompactorи лимиты. - Настройте Promtail с аккуратными метками и файлами позиций.
- При внешнем приёме — прокси, файрвол, аутентификация, лимиты тела и таймаутов.
- Проверьте готовность эндпоинтов, метрики, первые записи в индексе.
- Поставьте мониторинг свободного места и ключевых ошибок.
Итоги
Одиночный Loki с Promtail — практичное решение для централизованных логов на малом VDS. Оно экономно по ресурсам, даёт быстрый поиск, прозрачное хранение на файловой системе и предсказуемый ретеншн через compactor. Основные «секреты» успеха — дисциплина в метках, умеренные лимиты приёма и регулярный контроль диска. Запустив такой стек однажды, вы перестанете «grep-ить» ротации и сосредоточитесь на сервисах.


