Новинка Виртуальный VDS сервер в Финляндии от 390р
Выберите продукт

Приватный 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-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды Финляндия

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

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

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 и метрики в систему мониторинга.

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

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

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...