Duplicacy в Docker Compose: готовый файл
Если у вас несколько серверов и все они бэкапятся в одно хранилище, restic и BorgBackup рано или поздно упрутся в проблему параллельного доступа: пока один клиент пишет снапшот, второй должен ждать или рискует повредить репозиторий при одновременном prune. Duplicacy решает это иначе — за счёт дедупликации на уровне chunks и алгоритма без эксклюзивных блокировок несколько машин могут бэкапиться в один и тот же бакет одновременно, не мешая друг другу. Ниже — рабочий docker-compose.yml, который поднимает Duplicacy в контейнере, настраивает шифрованный репозиторий в S3-совместимом хранилище и расписание с ротацией.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как Duplicacy устроен и чем он отличается от restic и BorgBackup
Duplicacy — это CLI-инструмент на Go от Acrosync (автор — экс-инженер Google Гилберт Чен). Идея та же, что у restic: файлы режутся на chunks переменного размера по содержимому (content-defined chunking), одинаковые chunks хранятся один раз, каждый снапшот — это просто список ссылок на них. Разница в деталях:
- Lock-free дедупликация. restic и Borg при одновременной записи нескольких клиентов в один репозиторий используют блокировки (lock-файлы) или требуют, чтобы
pruneиcheckзапускались строго последовательно. Duplicacy устроен так, что каждый клиент считает chunk-хэши независимо и просто кладёт файл chunk’а в хранилище под этим хэшем — коллизии исключены математически, поэтому блокировка на запись не нужна вообще. Единственное место, где нужна осторожность, — сборка мусора (prune), и Duplicacy решает это через двухфазный алгоритм с «fossils» (файлы сначала помечаются кандидатами на удаление, а не удаляются сразу), чтобы не снести chunk, который параллельно дозаписывает другой клиент. - Бэкенды. Из коробки — локальный диск, SFTP, S3 и S3-совместимые (Backblaze B2, Wasabi, MinIO, Selectel S3), Google Cloud Storage, Azure Blob, Google Drive, Dropbox, OneDrive, WebDAV. Настройка хранилища — это просто URL вида
s3://region@bucket/path, без отдельного демона. - Лицензия. CLI бесплатен для личного некоммерческого использования. Для бизнеса и для набора функций Duplicacy Web Edition с расписанием и веб-интерфейсом Acrosync продаёт лицензии — по одной на компьютер/репозиторий. Если разворачиваете это на рабочих серверах компании, а не для себя, проверьте текущие условия на сайте разработчика перед тем, как полагаться на инструмент в проде.
- Кросс-платформенность репозитория. Формат снапшотов одинаков на Linux, macOS и Windows — можно бэкапить сервер на Linux и восстанавливать файлы на Windows-машине из того же хранилища, не пересобирая репозиторий.
Официального образа на Docker Hub от самого Acrosync нет — CLI распространяется как статический бинарник под конкретную ОС. Поэтому образ мы соберём сами: это займёт один Dockerfile и даёт полный контроль над версией.
Готовим Docker-образ
Duplicacy CLI — один бинарник, релизы лежат на GitHub в проекте gilbertchen/duplicacy. Смотрим актуальную версию на странице релизов и подставляем её в сборку:
# Dockerfile
FROM alpine:3.20
ARG DUPLICACY_VERSION=3.2.3
RUN apk add --no-cache ca-certificates tzdata bash curl \
&& curl -fsSL -o /usr/local/bin/duplicacy \
"https://github.com/gilbertchen/duplicacy/releases/download/v${DUPLICACY_VERSION}/duplicacy_linux_x64_${DUPLICACY_VERSION}" \
&& chmod +x /usr/local/bin/duplicacy
WORKDIR /repo
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ARG DUPLICACY_VERSION вынесен наружу специально — при сборке проверьте на GitHub, какая версия сейчас актуальна, и передайте её через --build-arg, не полагайтесь на число из примера.
Entrypoint делает простую вещь: если репозиторий ещё не инициализирован — инициализирует, дальше уходит в цикл backup → prune по расписанию:
#!/bin/bash
# entrypoint.sh
set -euo pipefail
cd /repo
if [ ! -f .duplicacy/preferences ]; then
echo "[duplicacy] Инициализация репозитория ${SNAPSHOT_ID} -> ${STORAGE_URL}"
duplicacy init -e -storage-name default "${SNAPSHOT_ID}" "${STORAGE_URL}"
fi
while true; do
echo "[duplicacy] Запуск backup: $(date -Iseconds)"
duplicacy backup -storage default -stats || echo "[duplicacy] backup завершился с ошибкой"
if [ "${PRUNE_ENABLED:-true}" = "true" ]; then
duplicacy prune -storage default \
-keep 0:${KEEP_ALL_DAYS:-7} \
-keep 7:${KEEP_WEEKLY_DAYS:-30} \
-keep 30:${KEEP_MONTHLY_DAYS:-180} || echo "[duplicacy] prune завершился с ошибкой"
fi
echo "[duplicacy] Сплю ${INTERVAL_SECONDS:-86400} секунд"
sleep "${INTERVAL_SECONDS:-86400}"
done
Такой цикл проще, чем тащить в контейнер cron или supercronic, и достаточен, если бэкап нужен раз в сутки. Для более гибкого расписания (например, разное время у разных снапшотов) можно заменить sleep на supercronic с crontab-файлом — но для одного репозитория цикл со sleep надёжнее и меньше двигающихся частей.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверdocker-compose.yml
Полный файл для бэкапа каталога /opt/app/data в S3-совместимое хранилище (подойдут Backblaze B2, Wasabi, Selectel, self-hosted MinIO — синтаксис URL общий для S3 API):
services:
duplicacy:
build: .
image: duplicacy-agent:3.2.3
container_name: duplicacy-agent
restart: unless-stopped
environment:
SNAPSHOT_ID: app-prod-01
STORAGE_URL: s3://eu-central-1@my-backup-bucket/app-prod
AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY}
AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY}
DUPLICACY_PASSWORD: ${REPO_PASSWORD}
INTERVAL_SECONDS: 86400
KEEP_ALL_DAYS: 7
KEEP_WEEKLY_DAYS: 30
KEEP_MONTHLY_DAYS: 180
volumes:
- /opt/app/data:/repo/data:ro
- duplicacy_state:/repo/.duplicacy
- duplicacy_cache:/repo/.duplicacy/cache
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
duplicacy_state:
duplicacy_cache:
Важные детали:
- Данные монтируются
:ro— контейнеру, который только читает и шлёт chunks в облако, не нужен доступ на запись к рабочим файлам приложения. duplicacy_stateхранит.duplicacy/preferences— файл с зашифрованным ключом и URL хранилища. Потерять этот том — не катастрофа (репозиторий переинициализируется по паролю), но удобнее держать его на персистентном volume, чтобы не пере-инициализировать при каждом пересоздании контейнера.duplicacy_cache— локальный кэш метаданных chunks. Ускоряет повторные backup/check, но не критичен: можно удалить, Duplicacy пересоберёт его при следующем запуске.- Переменные
S3_ACCESS_KEY,S3_SECRET_KEY,REPO_PASSWORDдержите в.envрядом с compose-файлом и добавьте.envв.gitignore— это ключи от бакета и пароль шифрования репозитория, при утечке которого ваши бэкапы читает кто угодно, у кого есть доступ к хранилищу.
Собираем и поднимаем:
docker compose build --build-arg DUPLICACY_VERSION=3.2.3
docker compose up -d
docker compose logs -f duplicacy
Инициализация и первый бэкап вручную
Автоматическая инициализация в entrypoint удобна для нового сервера, но для проверки лучше зайти в контейнер и прогнать команды руками — так виднее, что происходит:
docker compose exec duplicacy bash
# Проверить, что репозиторий инициализирован
duplicacy -log info
# Прогнать backup с подробным выводом
duplicacy -log backup -storage default -stats -threads 4
# Посмотреть список снапшотов в хранилище
duplicacy -log list
# Проверить целостность без скачивания всех данных (быстрая проверка)
duplicacy -log check -storage default
Флаг -threads 4 управляет параллельной загрузкой chunks — на слабом VPS с 1-2 vCPU разумно не задирать больше 2-4 потоков, иначе backup начнёт конкурировать за CPU с самим приложением.
Если бэкапите несколько серверов в один бакет (например, три реплики одного сервиса или несколько независимых VPS одной компании), у каждого должен быть свой уникальный SNAPSHOT_ID — это не имя бакета, а идентификатор клиента внутри общего хранилища. Именно уникальность SNAPSHOT_ID плюс lock-free дедупликация и позволяют писать в один бакет параллельно без риска гонки.
Мультисерверная схема: общий бакет для нескольких хостов
Типичный сценарий, где Duplicacy выигрывает у restic/Borg: несколько VPS в разных локациях (например, RU и US) бэкапятся в одно объектное хранилище, чтобы одинаковые файлы — общие библиотеки, Docker-образы, статические ассеты — дедуплицировались между серверами, а не хранились по три копии. Настройка — тот же compose-файл, запущенный на каждом сервере со своим SNAPSHOT_ID (server-ru-01, server-us-01) и одинаковым STORAGE_URL, указывающим на общий бакет:
environment:
SNAPSHOT_ID: server-ru-01 # на втором сервере — server-us-01
STORAGE_URL: s3://eu-central-1@shared-backup-bucket/prod
Оба хоста пишут в один STORAGE_URL параллельно — благодаря content-defined chunking одинаковые блоки данных (например, одна и та же версия системной библиотеки) лягут в хранилище один раз. Экономия зависит от того, насколько похожи данные: для двух копий одного приложения с общими Docker-слоями выигрыш заметный, для двух разных проектов — почти нулевой.
Восстановление файлов
Восстановление — тоже команда duplicacy, запускается из директории репозитория (то есть внутри того же контейнера или volume):
# Список ревизий конкретного снапшота
docker compose exec duplicacy duplicacy list -id app-prod-01
# Восстановить конкретную ревизию поверх текущей директории
docker compose exec duplicacy duplicacy restore -r 42 -stats
# Восстановить один файл, не трогая остальное
docker compose exec duplicacy duplicacy restore -r 42 -stats config/production.yml
# Сравнить содержимое двух ревизий (что изменилось)
docker compose exec duplicacy duplicacy diff -r1 40 -r2 42
Для полного восстановления на новый сервер (например, после потери исходного VPS) достаточно: развернуть тот же образ, положить пустую директорию /repo/data, инициализировать репозиторий с тем же SNAPSHOT_ID, STORAGE_URL и паролем — и выполнить duplicacy restore -r <последняя_ревизия>. Duplicacy скачает нужные chunks из хранилища и соберёт файлы заново, даже если восстанавливаете на машине другой архитектуры или ОС.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Duplicacy бесплатен для использования на сервере компании?
CLI бесплатен для личного некоммерческого применения; для коммерческого использования и для Web Edition с расписанием через интерфейс Acrosync требует лицензию на компьютер. Перед продакшен-развёртыванием на рабочих серверах компании проверьте актуальные условия на сайте разработчика.
Чем это лучше restic, если у restic тоже есть дедупликация?
Дедупликация в обоих схожая по идее. Разница — в параллельном доступе: restic требует последовательного выполнения prune/check при нескольких клиентах на одном репозитории (иначе риск повреждения индекса), Duplicacy спроектирован так, что несколько клиентов пишут в одно хранилище одновременно без внешней координации. Если у вас один сервер и одно хранилище — разница некритична, тогда стоит смотреть на restic в Docker Compose или сравнение restic и BorgBackup.
Нужен ли отдельный сервер под хранилище бэкапов?
Нет, подойдёт любой S3-совместимый бакет — от Backblaze B2 до self-hosted MinIO в Docker Compose на отдельном VPS, если хотите держать бэкапы физически отдельно от продакшена, но под своим контролем.
Что будет, если контейнер упадёт посреди backup?
Chunks, которые успели загрузиться, останутся в хранилище (они иммутабельны и адресуются по хэшу, повторная загрузка при следующем запуске их просто пропустит). Незавершённый снапшот-манифест просто не будет создан, поэтому предыдущая ревизия остаётся рабочей и доступной для restore.
Как проверить, что бэкап реально восстановится?
Регулярно гоняйте duplicacy check -storage default -files (проверка с валидацией chunks, а не только их наличия) и хотя бы раз в квартал делайте тестовое восстановление на отдельный сервер, а не только смотрите логи backup.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →