---
title: "Debian 12: настройка Docker-хоста для production"
date: 2026-05-22
draft: false
description: "Пошаговая настройка Debian 12 как Docker-хоста для production: установка Docker, настройка daemon, UFW + Docker интеграция, изоляция контейнеров, docker.sock, ресурсные ограничения."
summary: "Пошаговая настройка Debian 12 как Docker-хоста для production: установка Docker, настройка daemon, UFW + Docker интеграция, изоляция контейнеров, docker.sock, ресурсные ограничения."
tags: ["debian-12", "docker", "security", "hardening", "ufw", "production", "containers", "linux"]
categories: ["Безопасность"]
series: ["Linux Hardening"]
series_order: 2
---

# Debian 12: настройка Docker-хоста для production

Статья предполагает что базовая настройка уже выполнена: SSH на порту 2222, UFW включён, fail2ban настроен, sysctl применён.
Если нет - сначала:
{{< article link="/posts/debian-12-base-hardening/">}}

Здесь ставим Docker и закрываем типичные дыры: daemon без избыточных привилегий, UFW который не обходится Docker, изоляция контейнеров по сетям. Плюс разбираем docker.sock - самое опасное место на Docker-хосте.

## Исходные данные

- Debian 12 с выполненным базовым hardening
- Минимум 2GB RAM, 20GB диска

Проверь версию:

```bash
lsb_release -a
```

```
Distributor ID: Debian
Description:    Debian GNU/Linux 12 (bookworm)
Release:        12
Codename:       bookworm
```

Все команды выполняются с `sudo` если не указано иное.

## Установка Docker

### Удаление старых версий

Если был установлен Docker из репозиториев Debian - удали. Версия там устаревшая:

```bash
sudo apt remove -y docker docker-engine docker.io containerd runc docker-compose docker-doc podman-docker
```

### Установка из официального репозитория

>Команды установки актуальны на момент написания статьи. Docker меняет процедуру добавления репозитория - перед установкой сверяйся с официальной документацией: docs.docker.com/engine/install/debian

```bash
# Установить зависимости
sudo apt update
sudo apt install -y ca-certificates curl

# Создать директорию для ключей
sudo install -m 0755 -d /etc/apt/keyrings

# Добавить GPG ключ Docker
sudo curl -fsSL https://download.docker.com/linux/debian/gpg \
  -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Добавить репозиторий
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update

# Установить Docker
sudo apt install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
```

Проверь версию:

```bash
docker --version
```

```
Docker version 29.x.x, build xxxxxxx
```

Проверь что daemon запущен:

```bash
sudo systemctl status docker
```

### Добавление пользователя в группу docker

```bash
sudo usermod -aG docker $USER
# Активировать группу без перелогина
newgrp docker
```

**Важно:** членство в группе `docker` эквивалентно sudo без пароля - через docker.sock можно получить полный root на хосте. Добавляй только пользователей которым полностью доверяешь. Подробнее - в разделе про docker.sock ниже.

```bash
docker run --rm hello-world
```

Ожидаемый вывод: `Hello from Docker!`

## docker.sock - главный риск

Прежде чем настраивать daemon - разберёмся с самым опасным местом.

`/var/run/docker.sock` - Unix сокет Docker daemon. Любой процесс с доступом к нему имеет **полный root на хосте**. Не "почти root" - именно root. Через один вызов API можно запустить контейнер с bind mount `/:/host` и получить доступ ко всей файловой системе хоста:

```bash
# Пример атаки - одна команда и ты root на хосте (выполнение не требуется)
docker run --rm -v /:/host alpine chroot /host
```

Проверь кто имеет доступ к сокету:

```bash
# Права на сокет
ls -la /var/run/docker.sock
```

```
srw-rw---- 1 root docker 0 May 22 10:00 /var/run/docker.sock
```

Проверь кто в группе `docker`:

```bash
getent group docker
```

Только пользователи которым доверяешь полный root должны быть в этой группе.

## Настройка daemon.json

Создай файл:
```bash
sudo nano /etc/docker/daemon.json
```

```json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  },
  "live-restore": true,
  "userland-proxy": false,
  "no-new-privileges": true,
  "storage-driver": "overlay2",
  "default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 65536,
      "Soft": 65536
    }
  }
}
```

Что и почему:

`log-driver: json-file` + `max-size/max-file` - ограничение логов контейнеров. Без этого `/var/lib/docker/containers/` забивает весь диск за несколько дней на активном сервисе. 50MB × 3 файла = 150MB максимум на контейнер.

`live-restore: true` - контейнеры продолжают работать пока daemon перезапускается. Без этого `systemctl restart docker` роняет все контейнеры.

`userland-proxy: false` - iptables вместо userland proxy для проброса портов. Быстрее и меньше процессов.

`no-new-privileges: true` - контейнеры не могут поднять привилегии через setuid/setgid бинарники.

`storage-driver: overlay2` - современный и быстрый драйвер. На Debian 12 используется по умолчанию, но лучше зафиксировать явно.

### Почему нет userns-remap

`userns-remap` - user namespace remapping. Root в контейнере (UID 0) маппится на непривилегированный UID на хосте (например 100000). Звучит безопасно.

На практике: ломает все bind mounts - файлы на хосте принадлежат UID 1000, а контейнер видит UID 100000 и получает `Permission denied`. Redis, PostgreSQL, nginx - у каждого свой UID, каждый нужно пересчитывать вручную через `chown -R 100000:100000`. Для production с несколькими сервисами это постоянная головная боль. Включай только если чётко понимаешь все последствия и готов управлять UID маппингом вручную.

### Почему нет icc:false

`icc: false` (inter-container communication) - глобальный запрет общения между контейнерами на уровне daemon.

Проблема: Docker Compose создаёт изолированную bridge сеть для каждого стека. Контейнеры одного стека (`app` + `postgres` + `redis`) общаются через эту сеть. С `icc: false` на уровне daemon они перестают видеть друг друга - стек перестаёт работать. Правильный подход - изоляция через сети Docker Compose. Описано ниже.

Примени конфиг:

```bash
sudo systemctl restart docker
```

Проверь что daemon запустился с нужными параметрами:

```bash
docker info | grep -E "Storage Driver|Logging Driver|Live Restore"
```

```
 Storage Driver: overlay2
 Logging Driver: json-file
 Live Restore Enabled: true
```

## Sysctl для Docker-хоста

Docker требует дополнительных параметров ядра помимо базового hardening:

```bash
sudo nano /etc/sysctl.d/99-docker.conf
```

```
# IP forwarding - обязательно для работы сети контейнеров
# Docker включает это сам при старте, фиксируем явно чтобы не зависеть от порядка сервисов
net.ipv4.ip_forward = 1

# IPv6 forwarding - только если используешь IPv6 в контейнерах
# net.ipv6.conf.all.forwarding = 1

# Лимиты inotify - для контейнеров которые следят за файлами
fs.inotify.max_user_instances = 512
fs.inotify.max_user_watches = 524288

# Для Elasticsearch и других Java приложений в контейнерах
vm.max_map_count = 262144
```

Примени:

```bash
sudo sysctl -p /etc/sysctl.d/99-docker.conf
```

Проверь forwarding:

```bash
sudo sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 1
```

## UFW + Docker

>Этот раздел - справочный. Конкретные настройки применяются при разворачивании каждого сервиса.

Docker создаёт свои iptables правила и **обходит UFW**. Порт опубликованный через `-p 80:80` становится доступен снаружи даже если UFW его не разрешал. Это не баг - это архитектурное решение Docker.

Два подхода к решению:

### Подход 1 - рекомендуемый: порты на localhost

Публикуй порты только на localhost, снаружи открывай через reverse proxy (nginx, Traefik):

```bash
# Плохо - порт открыт на всех интерфейсах, обходит UFW
docker run -p 80:80 nginx

# Хорошо - порт только на localhost
docker run -p 127.0.0.1:80:80 nginx
```

В docker-compose.yml:

```yaml
ports:
  - "127.0.0.1:8080:80"
```

Reverse proxy слушает на `0.0.0.0:443` и проксирует на `127.0.0.1:8080`. UFW разрешает только 443. Docker UFW не обходит - нечего обходить.

Для большинства production сетапов достаточно этого подхода - он проще и надёжнее.

### Подход 2: DOCKER-USER chain

Docker оставляет пустую цепочку `DOCKER-USER` для пользовательских правил. Правила в ней применяются до правил Docker. Нужен когда по каким-то причинам нельзя использовать reverse proxy:

```bash
sudo nano /etc/ufw/after.rules
```

Добавь в конец файла:

```
# BEGIN UFW AND DOCKER
*filter
:DOCKER-USER - [0:0]

# Разрешить трафик из локальных сетей
-A DOCKER-USER -s 10.0.0.0/8 -j RETURN
-A DOCKER-USER -s 172.16.0.0/12 -j RETURN
-A DOCKER-USER -s 192.168.0.0/16 -j RETURN

# Применить правила UFW к трафику идущему в контейнеры
-A DOCKER-USER -j ufw-user-forward

# Блокировать новые соединения снаружи к частным подсетям
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER
```

Перезагрузи UFW:

```bash
sudo ufw reload
```

## Сетевая изоляция контейнеров

>Этот раздел - справочный. Сети создаются при разворачивании каждого сервиса.

Вместо глобального `icc: false` - изоляция через сети.

По умолчанию все контейнеры запущенные без явной сети попадают в `bridge` сеть и видят друг друга. Создавай отдельную сеть для каждого приложения:

```bash
# Создать изолированную сеть для приложения
docker network create --driver bridge app_network

# Контейнеры видят только соседей по той же сети
docker run -d --name app --network app_network myapp
docker run -d --name db --network app_network postgres
```

Контейнеры из разных сетей не могут общаться - это и есть правильная изоляция без глобального `icc: false`.

В docker-compose.yml сети создаются автоматически для каждого стека. Явно задавай сети если нужна дополнительная изоляция внутри стека.

## Безопасность образов

### Сканирование уязвимостей - Trivy

```bash
# Добавить репозиторий Trivy
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
  gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null

echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] \
  https://aquasecurity.github.io/trivy-repo/deb \
  $(lsb_release -sc) main" | \
  sudo tee /etc/apt/sources.list.d/trivy.list > /dev/null

sudo apt update
sudo apt install -y trivy
```

Проверь образ:

```bash
trivy image nginx:alpine
```

Вывод:
```bash
admin@vps01:~$ trivy image nginx:alpine
2026-06-09T12:22:41+03:00       INFO    [vulndb] Need to update DB
2026-06-09T12:22:41+03:00       INFO    [vulndb] Downloading vulnerability DB...
2026-06-09T12:22:41+03:00       INFO    [vulndb] Downloading artifact...        repo="mirror.gcr.io/aquasec/trivy-db:2"
95.58 MiB / 95.58 MiB [---------------------------------------------------------------------] 100.00% 763.08 KiB p/s 2m8s
2026-06-09T12:24:52+03:00       INFO    [vulndb] Artifact successfully downloaded       repo="mirror.gcr.io/aquasec/trivy-db:2"
2026-06-09T12:24:52+03:00       INFO    [vuln] Vulnerability scanning is enabled
2026-06-09T12:24:52+03:00       INFO    [secret] Secret scanning is enabled
2026-06-09T12:24:52+03:00       INFO    [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2026-06-09T12:24:52+03:00       INFO    [secret] Please see https://trivy.dev/docs/v0.71/guide/scanner/secret#recommendation for faster secret detection
2026-06-09T12:25:11+03:00       INFO    Detected OS     family="alpine" version="3.23.4"
2026-06-09T12:25:11+03:00       INFO    [alpine] Detecting vulnerabilities...   os_version="3.23" repository="3.23" pkg_num=71
2026-06-09T12:25:11+03:00       INFO    Number of language-specific files       num=0

Report Summary

┌──────────────────────────────┬────────┬─────────────────┬─────────┐
│            Target            │  Type  │ Vulnerabilities │ Secrets │
├──────────────────────────────┼────────┼─────────────────┼─────────┤
│ nginx:alpine (alpine 3.23.4) │ alpine │        1        │    -    │
└──────────────────────────────┴────────┴─────────────────┴─────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)


nginx:alpine (alpine 3.23.4)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)

┌─────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │                      Title                      │
├─────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────┤
│ libxml2 │ CVE-2026-6732 │ HIGH     │ fixed  │ 2.13.9-r0         │ 2.13.9-r1     │ libxml2: libxml2: Denial of Service via crafted │
│         │               │          │        │                   │               │ XSD-validated document                          │
│         │               │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2026-6732       │
└─────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────┘
```

>Status `fixed` - исправление уже есть, обнови образ
>Status `affected` - исправления нет, принимай решение осознанно

Правило для production: образы с CRITICAL уязвимостями не идут в production. HIGH - анализировать и принимать решение.

Сканировать перед каждым деплоем:

```bash
# Выйти с ненулевым кодом если есть CRITICAL/HIGH - удобно для CI
trivy image --exit-code 1 --severity CRITICAL,HIGH nginx:alpine
```

### Аудит событий Docker

Auditd фиксирует все обращения к Docker на уровне ядра: запуск контейнеров, изменения конфигурации, доступ к docker.sock. Когда что-то пойдёт не так - это первое место где искать что произошло и когда.

```bash
sudo apt install -y auditd
```

Создай правила:

```bash
sudo nano /etc/audit/rules.d/docker.rules
```

```
-w /usr/bin/docker -p rwxa -k docker
-w /var/lib/docker -p rwxa -k docker
-w /etc/docker -p rwxa -k docker
-w /var/run/docker.sock -p rwxa -k docker
```

```bash
sudo systemctl enable auditd
sudo systemctl restart auditd
```

Просматривай события:

```bash
sudo ausearch -k docker | tail -20
```

### Правила выбора образов

>Этот раздел - справочный. Применяй при выборе базового образа для своих Dockerfile.

Alpine-based или distroless - меньше attack surface:

```dockerfile
# 200MB лишних пакетов и уязвимостей
FROM ubuntu:24.04

# 5MB, минимум уязвимостей
FROM alpine:3.20

# Ещё лучше - distroless (нет shell, нет пакетного менеджера)
FROM gcr.io/distroless/static-debian12
```

Всегда указывай конкретный тег - никогда `latest`:

```bash
# Плохо - непредсказуемо, сломается при следующем релизе образа
docker pull nginx:latest

# Хорошо - предсказуемо
docker pull nginx:1.27-alpine
```

## Изоляция контейнеров

>Этот раздел - справочный.

### Capabilities

Контейнер по умолчанию имеет набор Linux capabilities которые ему не нужны. Убирай всё лишнее:

```bash
# Убрать все capabilities, добавить только нужные
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx
```

Минимальный набор для большинства веб-сервисов: `NET_BIND_SERVICE` (порты < 1024). Если сервис не биндит привилегированные порты - вообще ничего добавлять не нужно, только `--cap-drop=ALL`.

### Read-only filesystem

```bash
# Контейнер не может писать в FS кроме явно указанных точек
docker run --read-only --tmpfs /tmp --tmpfs /var/run nginx
```

В docker-compose.yml:

```yaml
read_only: true
tmpfs:
  - /tmp
  - /var/run
```

Не все образы поддерживают read-only без дополнительной настройки - смотри документацию образа.

### Seccomp

Docker применяет seccomp профиль по умолчанию - блокирует ~44 опасных syscall. Проверь что профиль применяется:

```bash
docker info | grep seccomp
# Security Options: ... seccomp
```

### Не запускай контейнеры с --privileged

```bash
# НИКОГДА в production
docker run --privileged nginx
```

`--privileged` даёт контейнеру доступ ко всем устройствам хоста и отключает все механизмы изоляции. Если видишь `--privileged` в чужом docker-compose - это красный флаг.

## Ресурсные ограничения

>Этот раздел - справочный.

Без ограничений один контейнер может съесть всю память или CPU хоста и положить остальные сервисы.

>OOM (Out Of Memory) killer - механизм ядра Linux: когда процесс превышает лимит памяти, ядро принудительно его завершает. Код завершения контейнера 137 (128 + сигнал 9) означает что сработал OOM killer, а не штатное завершение.

```bash
# Максимум 512MB RAM - если превысит, OOM (Out Of Memory) killer завершит контейнер
docker run -m 512m --memory-reservation 256m nginx

# Максимум 0.5 ядра
docker run --cpus="0.5" nginx

# Защита от fork bomb - контейнер не создает бесконечное количество процессов
docker run --pids-limit 200 nginx
```

Если контейнер неожиданно останавливается - первым делом проверяй OOM (Out Of Memory):

```bash
dmesg | grep -i "oom\|killed process"
docker inspect CONTAINER --format='{{.State.OOMKilled}}'
```

## Docker Compose с настройками безопасности

>Этот раздел - справочный.

Шаблон `docker-compose.yml` для production: сети, лимиты, изоляция:

```yaml
services:
  web:
    image: nginx:1.27-alpine
    container_name: web
    restart: unless-stopped

    # Порт только на localhost - reverse proxy снаружи
    ports:
      - "127.0.0.1:8080:80"

    # Ресурсные ограничения
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
        reservations:
          memory: 128M

    # Изоляция
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp
      - /var/cache/nginx
      - /var/run

    security_opt:
      - no-new-privileges:true

    pids_limit: 100

    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "3"

    networks:
      - frontend

  db:
    image: postgres:16-alpine
    container_name: db
    restart: unless-stopped

    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password

    volumes:
      - db_data:/var/lib/postgresql/data

    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M

    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
      - DAC_OVERRIDE
      - CHOWN
    security_opt:
      - no-new-privileges:true

    pids_limit: 200

    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "3"

    # Только backend сеть - не доступен снаружи
    networks:
      - backend

  app:
    image: myapp:1.2.3
    container_name: app
    restart: unless-stopped

    # Подключена к обеим сетям - мост между web и db
    networks:
      - frontend
      - backend

    depends_on:
      - db

    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true

    pids_limit: 100

    logging:
      driver: "json-file"
      options:
        max-size: "50m"
        max-file: "3"

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # backend сеть без доступа к интернету

volumes:
  db_data:

secrets:
  db_password:
    file: ./secrets/db_password.txt
```

`internal: true` для backend сети - контейнеры в ней не имеют доступа к внешним сетям. База данных не должна ходить в интернет.

## Регулярные задачи

>Этот раздел - справочный.

### Проверка контейнеров с docker.sock

Проверь запущенные контейнеры у которых docker.sock смонтирован:

```bash
# Контейнеры с docker.sock = контейнеры с root-доступом к хосту
docker ps --format '{{.Names}}' | xargs -I {} docker inspect {} \
  --format '{{.Name}}: {{range .Mounts}}{{if eq .Source "/var/run/docker.sock"}}DOCKER_SOCK_MOUNTED{{end}}{{end}}' \
  2>/dev/null | grep DOCKER_SOCK_MOUNTED
```
Запускай после добавления каждого нового сервиса. Пустой вывод - норма.

Никогда не монтируй docker.sock в контейнер без явной необходимости. Если сервис требует docker.sock (Portainer, Watchtower, Traefik с автообнаружением) - осознавай что это сервис с root-доступом к хосту.

### Очистка

Docker накапливает мусор: остановленные контейнеры, неиспользуемые образы, dangling volumes:

```bash
# Показать что занимает место
docker system df

# Удалить всё неиспользуемое (остановленные контейнеры, dangling образы, неиспользуемые сети)
docker system prune -f

# Удалить всё включая неиспользуемые образы
docker system prune -a -f

# Удалить неиспользуемые volumes (ОСТОРОЖНО - данные удалятся безвозвратно)
docker volume prune -f
```

Автоматизируй через cron:

```bash
sudo crontab -e
```

```
# Еженедельная очистка - каждое воскресенье в 4:00
0 4 * * 0 docker system prune -f >> /var/log/docker-prune.log 2>&1
```

### Мониторинг ресурсов

```bash
# Статистика всех запущенных контейнеров в реальном времени
docker stats

# Один снимок без потока
docker stats --no-stream

# Конкретный контейнер
docker stats web --no-stream
```

## Troubleshooting

### Контейнер не видит DNS

**Симптом:** `curl: Could not resolve host: example.com` внутри контейнера.

**Причина:** DNS не передан контейнеру.

**Решение:** Добавь DNS в daemon.json:

```json
{
  "dns": ["8.8.8.8", "8.8.4.4"]
}
```

```bash
sudo systemctl restart docker
```

---

### Docker обходит UFW

**Симптом:** Порт опубликованный через `-p 80:80` доступен снаружи несмотря на UFW deny.

**Причина:** Docker пишет напрямую в iptables, обходя UFW.

**Решение:** Использовать `127.0.0.1:80:80` вместо `80:80` - подход 1 из раздела UFW + Docker выше.

---

### Permission denied на bind mount

**Симптом:** Контейнер падает с `Permission denied` при обращении к примонтированной директории.

**Причина:** UID процесса в контейнере не совпадает с владельцем файлов на хосте.

**Решение:** Проверь под каким UID работает процесс в контейнере:

```bash
docker run --rm IMAGE id
```

Установи правильного владельца на хосте:

```bash
sudo chown -R UID:GID /path/to/mount
```

---

### OOM killer убивает контейнер

**Симптом:** Контейнер неожиданно останавливается, `docker ps` показывает `Exited (137)`.

**Причина:** Превышен memory limit, ядро завершило процесс.

**Диагностика:**

```bash
# Проверить OOM события
dmesg | grep -i "oom\|killed process"

# Статус контейнера
docker inspect CONTAINER --format='{{.State.OOMKilled}}'
```

**Решение:** Увеличить `memory` limit или оптимизировать приложение.

---

### Диск забит логами контейнеров

**Симптом:** `df -h` показывает 100% на `/var/lib/docker`.

**Диагностика:**

```bash
# Найти самые большие лог файлы
sudo find /var/lib/docker/containers/ -name "*.log" -exec du -sh {} \; | sort -rh | head -10
```

**Экстренное решение:**

```bash
# Обрезать лог конкретного контейнера (не удаляет файл)
sudo truncate -s 0 /var/lib/docker/containers/CONTAINER_ID/CONTAINER_ID-json.log
```

**Постоянное решение:** `max-size` и `max-file` в daemon.json - уже настроено выше.

## Финальная проверка
```bash
# Статус daemon
sudo systemctl status docker

# Конфигурация
docker info | grep -E "Storage Driver|Logging Driver|Live Restore|no-new-privileges"

# Открытые порты
ss -tulnp | grep docker

# Размер данных Docker
docker system df

# Контейнеры с docker.sock
docker ps -q | xargs docker inspect --format '{{.Name}}: {{range .Mounts}}{{if eq .Source "/var/run/docker.sock"}}SOCK{{end}}{{end}}' 2>/dev/null | grep SOCK
```

## Что дальше

Docker-хост готов к production нагрузке. Следующий шаг - разворачиваем сервисы поверх этой базы.

- Peertube на VPS - следующая статья серии: собственный видеохостинг на этом Docker-хосте
- Reverse proxy - Traefik или nginx перед контейнерами
- Мониторинг - Prometheus + cAdvisor + Grafana
- Централизованные логи - Loki + Promtail

**Стек этой статьи:** Debian 12 (Bookworm) · Docker CE · UFW · auditd · Trivy · docker-compose
