ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Приватный Docker Registry на VDS: аутентификация, TLS и очистка образов

Разворачиваем собственный private registry на VDS с нуля: готовим окружение, включаем htpasswd‑auth, настраиваем TLS на Nginx, подключаем Docker‑клиентов и организуем очистку образов без простоя. В статье — команды, безопасные конфиги и автоматизация GC через systemd.
Приватный Docker Registry на VDS: аутентификация, TLS и очистка образов

Собственный private registry решает сразу несколько задач: ускоряет доставку образов внутри вашей инфраструктуры, сокращает трафик в интернет, повышает контроль доступа и даёт предсказуемость поставок. В этой статье разбираем практическую схему на VDS: контейнер registry:2, базовая аутентификация через htpasswd, TLS‑терминация на Nginx, настройка доверия клиентов и безопасная очистка образов (garbage collect), включая автоматизацию.

Архитектура и требования

Базовая схема:

  • Docker Registry (образ registry:2) с файловым хранилищем на диске.
  • Nginx в роли TLS‑прокси с Basic Auth (через файл htpasswd), проксирующий на Registry.
  • Открыт наружу только 443/tcp (и, при необходимости, 80/tcp для редиректов). Сам Registry слушает на 127.0.0.1:5000.

Минимальные вводные:

  • Свежая Linux‑система на вашем VDS, установлен Docker и (опционально) Docker Compose.
  • Системный фаерволл открыт на 443/tcp для входящих соединений.
  • Готовы файлы сертификата и ключа сервера (или временно используем самоподписанный сертификат для тестов).

Практический совет: храните данные реестра на отдельном разделе/диске, включите регулярный бэкап каталога с хранилищем и конфигами.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Подготовка каталогов и пользователей

Создадим структуру каталогов под конфиги, данные и авторизацию:

sudo mkdir -p /srv/registry/{data,auth,conf}
sudo chown -R root:root /srv/registry
sudo chmod 750 /srv/registry

Создаём файл аутентификации htpasswd

Устанавливаем утилиту и создаём первого пользователя:

sudo apt-get update
sudo apt-get install -y apache2-utils
sudo htpasswd -Bbc /srv/registry/auth/htpasswd ci-user strong_password_here
sudo chmod 640 /srv/registry/auth/htpasswd

Позже вы сможете добавлять и менять пользователей:

sudo htpasswd -B /srv/registry/auth/htpasswd another-user
sudo htpasswd -D /srv/registry/auth/htpasswd old-user

Каталоги Docker Registry и файл htpasswd на сервере

Конфиг Docker Registry

Создадим файл /srv/registry/conf/config.yml с осмысленными параметрами: включаем удаление, настраиваем прослушивание на localhost, оставляем файловое хранилище и health‑endpoint.

version: 0.1
log:
  level: info
  fields:
    service: registry
storage:
  filesystem:
    rootdirectory: /var/lib/registry
  delete:
    enabled: true
http:
  addr: 127.0.0.1:5000
  headers:
    X-Content-Type-Options: [nosniff]
    X-Frame-Options: [DENY]
    X-XSS-Protection: ["1; mode=block"]
  debug:
    addr: 127.0.0.1:5001
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

Опция storage.delete.enabled: true нужна для корректного удаления манифестов и последующего GC.

Запускаем Registry контейнер

Вариант 1: напрямую через Docker (systemd поднимет его после перезапуска хоста):

sudo docker run -d --name registry -p 127.0.0.1:5000:5000 -v /srv/registry/conf/config.yml:/etc/docker/registry/config.yml:ro -v /srv/registry/data:/var/lib/registry --restart unless-stopped registry:2

Вариант 2: через docker-compose (файл /srv/registry/docker-compose.yml):

version: "3.9"
services:
  registry:
    image: registry:2
    container_name: registry
    restart: unless-stopped
    network_mode: host
    volumes:
      - /srv/registry/conf/config.yml:/etc/docker/registry/config.yml:ro
      - /srv/registry/data:/var/lib/registry

Старт:

cd /srv/registry
sudo docker compose up -d

Nginx как TLS‑прокси и Basic Auth

Установим Nginx и настроим сервер на 443 с проксированием к 127.0.0.1:5000. В качестве источника паролей используем ранее созданный htpasswd.

sudo apt-get install -y nginx

Пример конфига Nginx (файл /etc/nginx/sites-available/registry.conf):

map $http_upgrade $connection_upgrade { default upgrade; '' close; }

upstream registry_backend {
  server 127.0.0.1:5000;
  keepalive 32;
}

server {
  listen 443 ssl http2;
  server_name registry.example.com;

  ssl_certificate /etc/ssl/certs/registry.crt;
  ssl_certificate_key /etc/ssl/private/registry.key;

  client_max_body_size 0;
  chunked_transfer_encoding on;

  # Basic Auth
  auth_basic "Registry";
  auth_basic_user_file /srv/registry/auth/htpasswd;

  # Docker Registry v2 API
  location /v2/ {
    proxy_pass http://registry_backend;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_read_timeout 900s;
    proxy_send_timeout 900s;
  }
}

server {
  listen 80;
  server_name registry.example.com;
  return 301 https://$host$request_uri;
}

Активируем сайт и проверяем конфиг:

sudo ln -s /etc/nginx/sites-available/registry.conf /etc/nginx/sites-enabled/registry.conf
sudo nginx -t
sudo systemctl reload nginx

Схема: Nginx как TLS‑прокси перед Docker Registry

Важно: закройте наружу порт 5000 (Registry) в фаерволле и слушайте его только на 127.0.0.1. Открытым остаётся 443/tcp Nginx.

Сертификаты: самоподписанный или доверенный

Для тестов можно сгенерировать самоподписанный сертификат:

sudo openssl req -x509 -newkey rsa:4096 -nodes -days 365 -keyout /etc/ssl/private/registry.key -out /etc/ssl/certs/registry.crt -subj "/CN=registry.example.com"
sudo systemctl reload nginx

В продакшене используйте доверенный сертификат. Если применяете собственный CA, добавьте его корневой сертификат на клиентские хосты. Получить доверенный TLS проще всего через SSL-сертификаты.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Настройка доверия Docker‑клиентов

Если сертификат не из публично доверенного УЦ, Docker‑клиенты должны доверять вашему CA или конкретному серверному сертификату. На каждом клиенте создайте директорию и положите сертификат:

sudo mkdir -p /etc/docker/certs.d/registry.example.com
sudo cp /path/to/ca.crt /etc/docker/certs.d/registry.example.com/ca.crt
sudo systemctl restart docker

Для узла с нестандартным портом каталог должен называться /etc/docker/certs.d/registry.example.com:443. Не используйте «insecure‑registries», если можете установить доверие к CA — это безопаснее.

Проверка: login, push, pull

# Входим
sudo docker login registry.example.com
# Тегируем локальный образ
sudo docker tag alpine:3.20 registry.example.com/tools/alpine:3.20
# Публикуем
sudo docker push registry.example.com/tools/alpine:3.20
# Проверяем скачивание
sudo docker pull registry.example.com/tools/alpine:3.20

Если видите x509: certificate signed by unknown authority — клиент не доверяет вашему сертификату/CA. Проверьте, что файл ca.crt корректно установлен и перезапущен Docker.

Политика прав и учётные записи

Basic Auth — простой и надёжный вариант для единого периметра. Рекомендации:

  • Используйте htpasswd -B с достаточной стоимостью (-C 12 и выше).
  • Разделяйте учётки по командам/CI‑системам и периодически ротируйте пароли.
  • Ограничивайте доступ к реестру на уровне сети (firewall, VPN, allow‑list по IP).

Очистка образов: delete + garbage collect

Registry хранит слои, связанные с манифестами. Чтобы освободить место, сначала удаляем манифесты, затем запускаем сборщик мусора (GC). Мы уже включили storage.delete.enabled: true, значит API позволит удаление.

Удаление манифеста по digest

Алгоритм:

  1. Получить digest манифеста по тэгу.
  2. Удалить манифест по digest.

Пример с curl (понадобится Basic Auth):

# 1) Узнаём digest по тэгу (важен Accept для манифестов v2)
curl -I -u ci-user:strong_password_here -H "Accept: application/vnd.docker.distribution.manifest.v2+json" https://registry.example.com/v2/tools/alpine/manifests/3.20

# В ответе ищем заголовок Docker-Content-Digest: sha256:...

# 2) Удаляем манифест по digest
curl -X DELETE -u ci-user:strong_password_here https://registry.example.com/v2/tools/alpine/manifests/sha256:...

Удаление всех ненужных тэгов переводит «висячие» слои в кандидаты на сборку мусора.

Запуск garbage collect

Классическая рекомендация — останавливать трафик на время GC. Самый простой способ: временно закрыть доступ в Nginx или остановить контейнер Registry, выполнить GC в одноразовом контейнере и снова запустить сервис.

# Останавливаем Registry
sudo docker stop registry

# Запускаем GC в одноразовом контейнере, монтируя те же тома
sudo docker run --rm -v /srv/registry/conf/config.yml:/etc/docker/registry/config.yml:ro -v /srv/registry/data:/var/lib/registry registry:2 registry garbage-collect /etc/docker/registry/config.yml

# Запускаем Registry обратно
sudo docker start registry

Если вам важно свести простой к минимуму, можно гасить трафик на уровне Nginx (например, временным deny all на /v2/) и выполнять GC с минимальным окном.

Автоматизация GC через systemd timer

Создадим сервис /etc/systemd/system/registry-gc.service:

[Unit]
Description=Docker Registry garbage-collect
Wants=docker.service
After=docker.service

[Service]
Type=oneshot
ExecStart=/usr/bin/docker stop registry
ExecStart=/usr/bin/docker run --rm -v /srv/registry/conf/config.yml:/etc/docker/registry/config.yml:ro -v /srv/registry/data:/var/lib/registry registry:2 registry garbage-collect /etc/docker/registry/config.yml
ExecStart=/usr/bin/docker start registry

[Install]
WantedBy=multi-user.target

Таймер /etc/systemd/system/registry-gc.timer (например, еженедельно ночью):

[Unit]
Description=Weekly Docker Registry GC

[Timer]  *-*-* 03:30:00
Persistent=true

[Install]
WantedBy=timers.target

Активируем таймер:

sudo systemctl daemon-reload
sudo systemctl enable --now registry-gc.timer

Мониторинг и наблюдаемость

  • Проверка каталога: GET /v2/_catalog (за Basic Auth и TLS).
  • Health‑endpoint: http://127.0.0.1:5001/debug/health внутри сервера.
  • Логи Nginx и Registry: анализируйте коды 401/403/5xx, пиковые времена и размеры загрузок.

Безопасность

  • Держите Registry на 127.0.0.1, наружу — только Nginx на 443.
  • Минимизируйте набор пользователей, включайте ротацию паролей и ограничение по IP на уровне фаерволла.
  • В текстах и CI не храните пароли в открытом виде, используйте секреты среды выполнения.
  • Следите за параметром client_max_body_size в Nginx: 0 отключает лимит, но это уместно лишь в доверенной сети.

Если реестр крутится на общем хосте с другими рабочими нагрузками, подумайте об изоляции. Полезно начать с обзора про изоляцию контейнеров (gVisor/Firecracker).

Производительность и стабильность

  • Включите keepalive к backend и http2 на фронте — это улучшит конвейеризацию и скорость слоёв.
  • Увеличьте proxy_read_timeout и proxy_send_timeout при больших образах и медленных дисках.
  • Дисковая подсистема важнее всего: используйте быстрые SSD, следите за inode и свободным местом.

Бэкапы и восстановление

Для файлового хранилища достаточно копии каталога /srv/registry/data и конфигов. При восстановлении убедитесь, что совпадают доменное имя и пути — клиенты ожидают прежний CN в сертификате. Перед восстановлением остановите Registry, а после — выполните проверочный docker pull нескольких критичных образов.

Типичные ошибки и их решение

  • 401 Unauthorized: проверьте логин/пароль, права на файл htpasswd, конфиг Nginx с auth_basic.
  • 413 Request Entity Too Large: увеличьте client_max_body_size на 443‑сервере.
  • x509 unknown authority: установите правильный ca.crt для реестра на клиентах и перезапустите Docker.
  • timeout при push/pull: увеличьте таймауты прокси и проверьте I/O диска на VDS.

Итоги

Мы подняли приватный Docker Registry на VDS с базовой аутентификацией, TLS на Nginx и настроили безопасную уборку пространства: удаление манифестов по API и периодический garbage collect. При такой схеме вы контролируете доступ, экономите трафик и снижаете риски сбоев внешних реестров. Дальше можно добавить IP‑allowlist, отдельные учётки для CI/CD и метрики в систему мониторинга.

Поделиться статьей

Вам будет интересно

Kubernetes CronJob: concurrencyPolicy, startingDeadlineSeconds, timeZone и работа с Job OpenAI Статья написана AI (GPT 5)

Kubernetes CronJob: concurrencyPolicy, startingDeadlineSeconds, timeZone и работа с Job

CronJob в Kubernetes часто «ломается» не из‑за приложения, а из‑за нюансов планирования: параллельные запуски, пропущенные окна, т ...
Kernel panic на VDS: сбор диагностики через kdump и анализ vmcore OpenAI Статья написана AI (GPT 5)

Kernel panic на VDS: сбор диагностики через kdump и анализ vmcore

Kernel panic на VDS случается редко, но приводит к внезапному ребуту и потере контекста. В статье — пошаговый план: быстрый triage ...
SPF, DKIM и DMARC: как читать заголовки письма и SMTP-логи, чтобы повысить доставляемость OpenAI Статья написана AI (GPT 5)

SPF, DKIM и DMARC: как читать заголовки письма и SMTP-логи, чтобы повысить доставляемость

Практическое руководство по диагностике доставляемости: как по заголовкам письма (Authentication-Results, Received-SPF, ARC) прове ...