Ми використовуємо файли cookie для покращення вашого досвіду. Продовжуючи відвідувати цей сайт, ви погоджуєтесь з використанням cookie.Політика конфіденційності

EvilMailEvilMail
МожливостіУсе, що вміє EvilMailТимчасова пошта для…Тимчасова пошта для Discord, ChatGPT та іншихМережеві інструментиІнструменти DNS, WHOIS, портів і мережіПорівнянняEvilMail проти інших сервісів тимчасової пошти
APIДовідник REST API та посібникиTemp Mail APIГотовий код для копіювання будь-якою мовою
ДодаткиЦіниБлог
Про насХто ми та що створюємоДовіра та безпекаЯк ми захищаємо ваші даніПитання й відповідіВідповіді на поширені запитанняКонтактиЗв’яжіться з нашою командою
УвійтиРеєстрація
EvilMailEvilMail
ДодаткиЦіниБлог
УвійтиРеєстрація
ГоловнаБлогСтратегія резервного копіювання пошти: консистентні бекапи Maildir, бази користувачів і відновлення окремої скриньки
Посібник

Стратегія резервного копіювання пошти: консистентні бекапи Maildir, бази користувачів і відновлення окремої скриньки

Бекап пошти ламається не тоді, коли ви його робите, а коли намагаєтесь відновити. Розбираємо, чому наївний rsync живого Maildir дає дублікати й зламаний UIDVALIDITY, як зняти консистентний зріз трьох незалежних станів і як повернути ОДНУ скриньку, не зачепивши решту 2000 акаунтів.

EvilMail Team
18 липня 2026 р.
12 хв читання
Стратегія резервного копіювання пошти: консистентні бекапи Maildir, бази користувачів і відновлення окремої скриньки
Зміст
Чому «бекап є, а відновити не можна» — три стани, які треба зловити разомAnatomy Maildir: чому цей формат прощає копіювання (і де все одно кусає)dsync проти rsync: правильний інструмент для реплікації скриньокКонсистентний знімок без зупинки сервісуВідновлення однієї скриньки, не зачепивши інші 2000Ротація, off-site і перевірка, що бекап живийЧек-лист: перед тим, як назвати бекап пошти «готовим»

Чому «бекап є, а відновити не можна» — три стани, які треба зловити разом

Класична сцена. О третій ночі спрацював cron, rsync -a /var/mail/vhosts backup:/ відпрацював без помилок, лог зелений, ви спите спокійно. Через тиждень користувач зносить теку «Клієнти», ви розгортаєте вчорашню копію — і IMAP-клієнт замість повернутих листів показує 4000 дублікатів, а Thunderbird починає перекачувати всю скриньку заново. Бекап був. Відновлення — ні.

Причина проста і болюча: rsync копіює файли, а поштова скринька — це не папка з файлами. Це три незалежні стани, які мають бути взаємно консистентними на один момент часу:

  • Файли Maildir — самі листи в new/ і cur/.
  • Метадані Dovecot — dovecot-uidlist, dovecot.index*, subscriptions, dovecot-keywords, maildirfolder. Саме тут живе відповідність «файл ↔ IMAP UID».
  • Реляційна база `mailserver` — таблиці virtual_domains, virtual_users, virtual_aliases: хеші паролів, квоти, мапінг адрес на каталоги.

rsync живого Maildir ловить лист у процесі переїзду з tmp/ у new/: файл у копії вже є, а рядка в dovecot-uidlist ще немає (або навпаки — uidlist оновився, файл не долетів). При restore Dovecot бачить неузгоджений uidlist, вирішує, що це чужа скринька, і присвоює нові UID під новим UIDVALIDITY. Для IMAP-клієнта це сигнал «скринька перестворена» — стягуй усе наново. Звідси дублікати й трафік.

Далі — як зняти всі три стани узгоджено і, головне, як відновити один акаунт, не зачепивши інші.

Консистентний зріз @ t₀ = три узгоджені потоки не одна папка 1. Файли Maildir tmp/ → new/ → cur/ 1720099200.M12..:2,S знімає: dsync 2. Метадані Dovecot dovecot-uidlist index / keywords знімає: dsync (разом) 3. SQL mailserver virtual_users domains / aliases mysqldump --single-transaction Backup snapshot @ t₀ ✗ Неправильний шлях: rsync тягне лише файли → index і SQL лишаються розсинхронізованими → при restore Dovecot присвоює нові UID → дублікати в клієнта

Anatomy Maildir: чому цей формат прощає копіювання (і де все одно кусає)

Maildir спроєктований під atomic rename, і це його головна перевага для бекапу. Кожен лист — окремий файл, який ніколи не редагується in-place. MTA спершу пише в tmp/ під тимчасовим ім'ям, а потім робить rename() у new/. rename() у межах одного тому — атомарна операція, тож у new/ файл або є цілим, або його немає. Пів-листа не буває. Коли клієнт вперше бачить повідомлення, Dovecot переносить його з new/ у cur/ і дописує суфікс прапорців.

Ім'я файлу несе стан: 1720099200.M12345P6789.mx1:2,S. Після :2, — прапорці одним рядком: P (passed/переслано), R (replied/відповіли), S (seen/прочитано), T (trashed/у кошику), D (draft/чернетка), F (flagged/позначено). Позначити лист прочитаним — це не запис усередину файлу, а rename з додаванням S до суфікса. Тому копія, зроблена посеред такого rename, може мати файл зі старим ім'ям, а dovecot.index — уже з новим станом.

Де саме кусає:

  • `dovecot-uidlist` — плоский файл відповідності «UID ↔ ім'я файлу». rsync може зловити його напівоновленим.
  • `UIDVALIDITY` — 32-бітне число на теку. IMAP-клієнт кешує пару (UIDVALIDITY, UID). Якщо UIDVALIDITY змінилося, клієнт зобов'язаний за протоколом викинути весь локальний кеш і перезавантажити теку. Збереження цього числа — і є різниця між «тихим» restore і бурею дублікатів.
  • Прапорці в суфіксах легко розходяться з dovecot-keywords та index, якщо копіювати файли й метадані окремими проходами.

Висновок для бекапу: окремий лист завжди цілий, а от набір метаданих — це живий стан, який не можна фотографувати покадрово різними інструментами.

dsync проти rsync: правильний інструмент для реплікації скриньок

doveadm backup і doveadm sync (двигун dsync) розуміють семантику Maildir + index. Вони синхронізують на рівні листів і прапорців, а не байтів файлів: зберігають UID, UIDVALIDITY, keywords, не плодять дублікатів навіть на скриньці, куди прямо зараз падає пошта. dsync читає стан через ту саму бібліотеку, що й сам Dovecot, тож бачить консистентну картину, а не файлову «фотографію на ходу».

bash
# Бекап однієї скриньки в Maildir на бекап-хості
doveadm backup -u [email protected] \
  maildir:/backup/vhosts/evilmail.pro/user

# Бекап усіх скриньок разом: %d = домен, %n = локальна частина
doveadm backup -A maildir:/backup/%d/%n

# Інкрементальна дельта (тільки зміни з минулого разу)
doveadm sync -u [email protected] \
  maildir:/backup/vhosts/evilmail.pro/user

Різниця backup і sync: backup — односторонній, робить приймач точною копією джерела (зайве на приймачі видаляє). sync — двонаправлений merge. Для нічного бекапу беремо backup, для вливання в живу скриньку — sync (про це нижче).

Критерійrsync -adoveadm (dsync)
UID / UIDVALIDITYгубить, генерує новізберігає
Дублікати при restoreтак, масовоні
Робота на живій скриньцінебезпечноштатно
Швидкість інкрементусканує все деревобачить лише дельту index
Keywords / прапорцірозходятьсясинхронізує

Чесно: rsync не «поганий» узагалі — він валідний для холодного повного знімка, коли Dovecot зупинений, або поверх атомарного снапшота ФС. Саме цей нюанс і веде до наступного розділу.

Що бачить IMAP-клієнт після restore rsync UIDVAL=A restore UIDVAL=B (нове!) ×100 дублікатів клієнт трактує теку як нову → повне повторне завантаження dsync UIDVAL=A restore UIDVAL=A (та сама) 0 дублікатів UID 1..100 ті самі, прапорці S/T на місці → клієнт не помічає різниці Консистентність вимірюється не наявністю файлів, а поведінкою клієнта після restore

Консистентний знімок без зупинки сервісу

Гасити Postfix і Dovecot щоночі ніхто не буде. Рішення — зняти два узгоджені зрізи майже в один момент: дамп бази без блокувань і атомарний снапшот тому з поштою.

База mailserver на InnoDB дампиться консистентно без зупинки прийому пошти:

bash
mysqldump --single-transaction --routines --triggers \
  -u mailuser -p mailserver > /backup/mailserver_$(date +%F).sql

--single-transaction відкриває одну транзакцію з consistent snapshot — усі таблиці бачаться на один момент, без LOCK TABLES, доставка не завмирає. Але це працює лише для InnoDB. Якщо хоч одна таблиця на MyISAM — --single-transaction її не захистить, потрібен --lock-tables, а це вже блокування. Перевірте рушій через SHOW TABLE STATUS перед тим, як довіритися прапорцю.

Файли й метадані Dovecot фіксуємо снапшотом ФС у ту саму секунду:

bash
# ZFS
zfs snapshot rpool/mail@backup-$(date +%F_%H%M)

# LVM
lvcreate -L5G -s -n mailsnap /dev/vg0/mail

Снапшот атомарний — він фотографує /var/mail/vhosts разом з dovecot-uidlist та index на один момент. Далі з примонтованого снапшота ганяємо dsync або навіть rsync (тут файли вже «холодні», не рухаються) на off-site.

Порядок важливий, але дрейф прощенний. Робіть дамп бази, потім снапшот ФС. Якщо між ними хтось створить акаунт, ви отримаєте каталог без запису в SQL — некритично: скринька ще порожня, наступний бекап її підхопить. Зворотна ситуація — запис у SQL без каталогу — теж нестрашна, Dovecot створить Maildir при першій доставці. Небезпечний лише зворотний порядок при видаленні, тому видалення акаунтів робіть окремою процедурою, а не покладайтесь на дрейф.

Відновлення однієї скриньки, не зачепивши інші 2000

Заради цього все й будувалося. Сценарій: [email protected] зніс теку «Клієнти», треба повернути стан на вчора. Решта 2000 акаунтів працюють — їх не можна навіть торкнутися.

Спокуса зробити cp -a /backup/.../user /var/mail/vhosts/evilmail.pro/user — і це рівно та помилка, що породжує тікети. cp -a поверх живого Maildir змішує вчорашній index із сьогоднішніми файлами, Dovecot ловить розсинхрон, форсує reindex, скидає прапорці seen і ламає UIDVALIDITY. Клієнт качає все наново.

Правильно — merge через dsync прямо в цільову скриньку:

bash
# Дістаємо ТІЛЬКИ цю скриньку з бекапа (снапшот/окремий шлях)
# і мерджимо в живий Maildir через ту саму бібліотеку Dovecot.
doveadm sync -u [email protected] \
  maildir:/backup/vhosts/evilmail.pro/user

# Права ОБОВ'ЯЗКОВО: інакше Dovecot отримає Permission denied
chown -R 5000:5000 /var/mail/vhosts/evilmail.pro/user

doveadm sync зіллє листи з бекапа з поточним станом: повернені повідомлення приїдуть зі збереженими UID, а нові листи, що прийшли після бекапа, залишаться на місці. Жодного затирання. Забутий chown 5000:5000 — друга за популярністю причина «скринька порожня після restore»: файли є, але Dovecot під uid 5000 їх не читає.

Якщо зник сам акаунт (рядок у virtual_users), не робіть restore всієї таблиці — це затре всі акаунти, створені після бекапа. Точковий INSERT:

sql
INSERT INTO virtual_users (domain_id, email, password, quota)
VALUES (53, '[email protected]', '{PLAIN}ВІДНОВЛЕНИЙ_ПАРОЛЬ', 2147483648);

domain_id 53 — це evilmail.pro, 54 — evilmail.cloud. Схема пароля — PLAIN, як налаштовано в Dovecot.

Фінальна валідація — переконатися, що UIDVALIDITY не змінився і кількість листів збіглася:

bash
doveadm mailbox status -u [email protected] \
  'messages uidvalidity' '*'

Ротація, off-site і перевірка, що бекап живий

GFS-схема інкрементів dsync: 7 щоденних / 4 тижневих / 6 місячних. Off-site копія — обов'язково шифрованим каналом; бекап поштової бази з хешами паролів на незашифрованому чужому диску — це інцидент, що чекає нагоди.

Але головне не ротація, а restore-drills. Бекап без регулярного тестового відновлення — це кіт Шредінгера: він одночасно робочий і зламаний, поки ви не відкрили коробку в момент аварії. Ритуал: раз на місяць піднімайте випадкову скриньку з бекапа в ізольований Dovecot-інстанс і звіряйте лічильник через doveadm search або doveadm mailbox status. Збіглося — бекап живий.

Що моніторити автоматично:

  • Вік останнього успішного dsync — alert, якщо > 26 годин (добовий цикл плюс запас).
  • Розмір інкременту — аномально малий інкремент означає, що щось не синкнулось, а лог усе одно зелений.
  • Дельта SQL ↔ ФС — SELECT COUNT(*) FROM virtual_users проти кількості каталогів у /var/mail/vhosts/*/. Розбіжність більша за кілька свіжостворених акаунтів — сигнал, що бекап або створення акаунтів десь розходяться.

Чек-лист: перед тим, як назвати бекап пошти «готовим»

  • dsync, не rsync, для гарячих скриньок; rsync — лише поверх снапшота або зупиненого Dovecot.
  • `UIDVALIDITY` зберігається — перевірено через doveadm mailbox status.
  • `mysqldump --single-transaction`, рушій таблиць підтверджено як InnoDB.
  • Снапшот ФС і дамп БД зняті узгоджено за часом, порядок задокументований.
  • Off-site копія шифрована.
  • Restore-drill пройдено цього місяця в ізольованому інстансі.
  • Права `5000:5000` прописані в процедурі restore.
  • Вік останнього успішного бекапа моніториться (alert > 26 год).
  • Є документована процедура відновлення ОДНІЄЇ скриньки через doveadm sync, а не cp -a

Бекап, який проходить усі десять пунктів, — це не «папка на іншому диску», а відтворюваний стан пошти, який ви вже вміли повертати до того, як він знадобився насправді.

Поділитися
𝕏 TwitterLinkedInReddit
ПопередняВисока доступність пошти без спільного сховища: два MX і Dovecot replicationНаступнаМіграція пошти без простою: масове перенесення скриньок через imapsync і безшовне перемикання MX

Схожі статті

Посібник

Розгортання Roundcube: тонкий вебклієнт над Dovecot, а не другий поштовий сервер

11 хв читання
Посібник

Міграція пошти без простою: масове перенесення скриньок через imapsync і безшовне перемикання MX

12 хв читання
Посібник

Тонке налаштування greylisting: зупиняємо ботів, не гальмуючи легітимну пошту

11 хв читання
EvilMailEvilMail

EvilMail — free temp mail and disposable email service. No registration required.

Продукт

МожливостіЦіниAPITools

Ресурси

БлогFAQGlossaryРеєстраціяПро нас

Правова інформація

Політика конфіденційностіУмови використанняДовіра та безпекаКонтакти
© 2026 EvilMail. Усі права захищені.
.
  • Дельта SQL ↔ каталоги vhosts під контролем.