Ежедневное резервное копирование баз MySQL в Debian, Ubuntu и Linux
Небольшого скрипта и задания cron достаточно, чтобы каждую ночь сохранять все базы MySQL и MariaDB в сжатом виде. Вместе с теми местами, где Debian и Ubuntu расходятся и резервное копирование срывается незаметно.
У вас Debian, Ubuntu или другой дистрибутив Linux на VPS, root-сервере или выделенном сервере, и вы хотите автоматически создавать резервные копии всех баз данных MySQL и MariaDB каждый день? Тогда вы попали по адресу. В этом руководстве вы настроите скрипт резервного копирования, который каждую ночь выгружает все базы данных в сжатый SQL-файл и сам удаляет устаревшие копии.
Порядок действий одинаков во всех дистрибутивах: он работает и в Debian с Ubuntu, и в AlmaLinux, Rocky Linux и RHEL. Отличия кроются не в самом скрипте, а в том, какой сервер баз данных у вас запущен и как к нему подключиться. Этому посвящён отдельный раздел ниже: различия между Debian и Ubuntu.
Что понадобится
Вам нужен доступ по SSH с правами root и установленный сервер MySQL или MariaDB. Рекомендуем актуальные версии дистрибутивов: Debian 13 «Trixie» и Debian 12 «Bookworm», Ubuntu 24.04 LTS «Noble Numbat» и Ubuntu 22.04 LTS «Jammy Jellyfish», а также AlmaLinux и Rocky Linux версий 9 и 10. Более старые системы в продуктивной работе использовать уже не стоит: у Debian 10 поддержка закончилась в июне 2024 года, у Ubuntu 20.04 LTS в мае 2025 года, а CentOS 7 тоже больше не получает регулярных обновлений безопасности.
Сначала обновите систему и установите текстовый редактор Nano, если его ещё нет.
Для Debian и Ubuntu:
apt update && apt upgrade -y
apt install nano -y
Для AlmaLinux, Rocky Linux и RHEL:
dnf update -y
dnf install nano -y
В актуальных системах семейства RHEL dnf пришёл на смену yum. Старая команда там обычно ещё работает как ссылка, но использовать следует именно dnf.
Заложите также достаточно места на накопителе. Недельный запас сжатых копий в зависимости от размера баз данных быстро занимает несколько гигабайт. Свободное место проверяется командой df -h.
Безопасное хранение учётных данных
Паролю не место прямо в вызове mysqldump: командные строки видны через ps всем пользователям системы. Вместо этого создайте файл с учётными данными, читать который сможет только root:
nano /root/.my.cnf
Содержимое файла:
[client]
user=root
password=ВАШ_ПАРОЛЬ_БАЗЫ_ДАННЫХ
Затем выставьте права так, чтобы доступ был только у root:
chmod 600 /root/.my.cnf
Если пароля root вообще нет
В Debian и Ubuntu учётная запись базы данных root обычно защищена не паролем, а личностью системного пользователя. В MariaDB этот механизм называется unix_socket, в MySQL: auth_socket. Пароля тогда просто не существует, и запись в файле с учётными данными ни на что не влияет. Так ли это у вас, покажет такой запрос:
mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"
Если там стоит unix_socket или auth_socket, у вас два пути: либо запускать скрипт от системного пользователя root и полностью отказаться от файла с учётными данными, либо завести отдельного пользователя для резервного копирования. Второй вариант лучше, потому что обходится без полных прав учётной записи root:
CREATE USER 'kh_backup'@'localhost' IDENTIFIED BY 'ВАШ_ПАРОЛЬ_РЕЗЕРВНОГО_КОПИРОВАНИЯ';
GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES, RELOAD, PROCESS ON *.* TO 'kh_backup'@'localhost';
FLUSH PRIVILEGES;
В файле с учётными данными тогда укажите user=kh_backup. Права намеренно урезаны: чтение, представления, события, триггеры и, как запасной вариант, блокировка таблиц, которые не используют InnoDB. Привилегии RELOAD и PROCESS выдаются только глобально, отсюда и ON *.*. Право PROCESS нужно MySQL 8, чтобы прочитать сведения о табличных пространствах. Если выдавать его не хочется, добавьте в скрипт --no-tablespaces. Если войти не получается вообще никак, поможет статья сброс пароля root в MySQL и MariaDB.
Создание скрипта резервного копирования
Теперь создадим bash-скрипт, который выполняет выгрузку и удаляет устаревшие копии. В нашем примере он называется mysql_export_all.sh и лежит в каталоге /opt/mysqlbackups:
mkdir -p /opt/mysqlbackups
nano /opt/mysqlbackups/mysql_export_all.sh
В этот скрипт впишите следующее содержимое:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/mysqlbackups"
KEEP_DAYS=7
DATE=$(date +%Y-%m-%d-%H-%M)
mkdir -p "$BACKUP_DIR"
mysqldump --defaults-extra-file=/root/.my.cnf --all-databases --single-transaction --routines --events | gzip > "$BACKUP_DIR/alldbs_$DATE.sql.gz"
find "$BACKUP_DIR" -type f -name "alldbs_*.sql.gz" -mtime +$KEEP_DAYS -delete
Самые важные опции коротко:
--defaults-extra-fileчитает пользователя и пароль из только что созданного файла. Эта опция должна стоять первой.--single-transactionдаёт для таблиц InnoDB согласованный на один момент времени снимок, не блокируя базу данных.--routinesи--eventsдобавляют в резервную копию хранимые процедуры и запланированные события. Триггерыmysqldumpсохраняет и без того сам.gzipсжимает выгрузку и заметно экономит место.- Команда
findудаляет только файлы резервных копий старше семи дней. ЧерезKEEP_DAYSвы подстраиваете срок хранения.
Строка set -euo pipefail важнее, чем кажется. Без pipefail оболочка оценивает только код возврата gzip, а он равен нулю и в том случае, если mysqldump перед этим аварийно завершился. В итоге у вас окажется технически исправный архив с неполным содержимым, и никто этого не заметит.
Какой сервер баз данных у вас работает, зависит от дистрибутива. В Debian нет пакета mysql-server, там везде используется MariaDB, а Ubuntu предлагает и то, и другое. Скрипт во всех случаях работает без изменений:
| Система | mysql-server | mariadb-server |
|---|---|---|
| Debian 13 | отсутствует | 11.8 |
| Debian 12 | отсутствует | 10.11 |
| Ubuntu 24.04 LTS | 8.0 | 10.11 |
| Ubuntu 22.04 LTS | 8.0 | 10.6 |
Права на запуск и первая проверка
Сделайте скрипт исполняемым:
chmod +x /opt/mysqlbackups/mysql_export_all.sh
Запустите его один раз вручную и проверьте результат, прежде чем ставить на автомат:
/opt/mysqlbackups/mysql_export_all.sh
ls -lh /opt/mysqlbackups/
Созданный файл должен быть заметно больше нуля байт. Но начало файла говорит меньше, чем его конец. Поэтому проверьте, цел ли архив и действительно ли выгрузка дошла до конца:
gzip -t /opt/mysqlbackups/alldbs_*.sql.gz
zcat /opt/mysqlbackups/alldbs_*.sql.gz | tail -n 1
Последняя строка полной выгрузки начинается с -- Dump completed on. Если её нет, выгрузка была прервана, и файл как резервная копия ничего не стоит, даже если он весит несколько сотен мегабайт. Эта единственная строка — самая быстрая надёжная проверка из всех, что у вас есть.
Настройка cron для ежедневной резервной копии
Откройте редактор заданий cron:
export VISUAL=nano; crontab -e
Для ежедневной резервной копии в 5:00 утра добавьте такую строку:
0 5 * * * /opt/mysqlbackups/mysql_export_all.sh >> /var/log/mysql-backup.log 2>&1
Вывод при этом попадает в лог-файл, так что ошибки можно будет разобрать позже. Правильно ли записано задание, проверяется командой crontab -l. Находиться оно должно в crontab пользователя root. Во многих образах Ubuntu вход под root отключён, там команда выглядит как sudo crontab -e. Иначе обычный пользователь заведёт собственный crontab, и скрипт позже споткнётся о файл с учётными данными в каталоге /root.
В минимальных установках семейства RHEL службы cron иногда нет. Устанавливается и включается она так:
dnf install cronie -y
systemctl enable --now crond
С этого момента все базы данных выгружаются каждую ночь в 5:00, а копии старше семи дней исчезают сами. Подробнее о расписаниях и типичных подводных камнях: настройка cron в Linux.
Восстановление из резервной копии
Резервная копия чего-то стоит только тогда, когда вы можете её развернуть. Поэтому хотя бы раз осознанно проверьте восстановление, лучше всего на тестовой системе:
zcat /opt/mysqlbackups/alldbs_2026-07-26-05-00.sql.gz | mysql --defaults-extra-file=/root/.my.cnf
Если нужно вернуть только одну базу данных, сначала распакуйте копию и вырежьте из неё нужный фрагмент либо дополнительно сохраняйте отдельные базы командой mysqldump --databases mydb. Удобнее сразу писать по отдельному файлу на каждую базу. Для этого замените в скрипте строку с mysqldump на такой цикл:
for DB in $(mysql --defaults-extra-file=/root/.my.cnf -N -B -e "SHOW DATABASES;" | grep -Ev '^(information_schema|performance_schema|sys)$'); do
mysqldump --defaults-extra-file=/root/.my.cnf --single-transaction --routines --events "$DB" | gzip > "$BACKUP_DIR/${DB}_$DATE.sql.gz"
done
Три исключённые базы данных — это представления внутреннего состояния сервера, восстанавливать их бессмысленно. Не забудьте поправить и шаблон поиска в команде find, иначе новые имена файлов перестанут удаляться. Полный переезд на другой сервер разобран на практическом примере в статье перенос WordPress на новый сервер.
Хранение копий за пределами сервера
Копии, которые лежат только на том же сервере, при потере всей системы уже не помогут. Поэтому передавайте файлы дополнительно во второе место, например по rsync или scp на другой сервер или в хранилище резервных копий:
rsync -avz /opt/mysqlbackups/ user@backup-host:/path/to/backup/
Просто допишите эту команду в конец своего скрипта, и передача будет выполняться автоматически. Чтобы в задании cron она проходила без запросов, пользователю root нужен SSH-ключ без парольной фразы, публичная часть которого прописана на целевой системе. Как его создать, описано в статье подключение к серверу по SSH.
Различия между Debian и Ubuntu
Обе системы используют apt, обе хранят конфигурацию в /etc/mysql/, и приведённый выше скрипт работает в обеих без изменений. Тем не менее есть шесть пунктов, в которых они расходятся, и каждый из них способен незаметно сорвать резервное копирование:
| Тема | Debian | Ubuntu |
|---|---|---|
| Сервер баз данных | только MariaDB | MySQL 8.0 или MariaDB |
| Клиентский пакет | mariadb-client | mysql-client-8.0 или mariadb-client |
| Утилита выгрузки | mariadb-dump, mysqldump как ссылка | при MySQL только mysqldump |
| Вход под root | unix_socket | auth_socket при MySQL из пакета |
| Сервисная учётная запись | начиная с MariaDB 10.4 не создаётся | debian-sys-maint в /etc/mysql/debian.cnf |
| Лог ошибок | журнал, journalctl -u mariadb | при MySQL /var/log/mysql/error.log |
Названия пакетов и инструменты
Если вы снимаете копию базы данных с другой машины, там нужен только клиентский пакет. В Debian он называется mariadb-client, в Ubuntu с MySQL 8: mysql-client-8.0. Для скрипта, которому нужно работать в обеих системах, есть метапакет default-mysql-client: в Debian он указывает на клиент MariaDB, в Ubuntu на клиент MySQL.
Начиная с MariaDB 10.5 у инструментов есть дополнительное имя с префиксом mariadb-, а начиная с MariaDB 11 именно оно является основным, тогда как mysqldump остаётся лишь ссылкой на него. Там работают оба вызова. А в системе Ubuntu с MySQL 8 существует только mysqldump, и скрипт с mariadb-dump завершится там сообщением command not found. Поэтому для скриптов, которым нужно работать в обоих мирах, правильный вызов: mysqldump.
Сервисная учётная запись и аутентификация
В Ubuntu пакет mysql-server по-прежнему создаёт сервисную учётную запись debian-sys-maint и записывает её данные в /etc/mysql/debian.cnf. Этот файл доступен для чтения только пользователю root и годится для резервного копирования напрямую, без всякой подготовки:
mysqldump --defaults-file=/etc/mysql/debian.cnf --all-databases --single-transaction --routines --events | gzip > /opt/mysqlbackups/alldbs.sql.gz
В Debian с MariaDB версии 10.4 и выше эта учётная запись заново уже не создаётся. Файл там обычно ещё есть, но, как правило, указывает всё на того же root через сокет. Поэтому не рассчитывайте, что скрипт, работающий в Ubuntu через этот файл, сделает в Debian то же самое. Проверить это можно за один шаг:
mysql --defaults-file=/etc/mysql/debian.cnf -e "SELECT current_user();"
Новые учётные записи MySQL 8 создаёт с механизмом caching_sha2_password, MariaDB: с mysql_native_password. Для выгрузки через локальный сокет это роли не играет, зато играет, если вы снимаете копию по сети со старого клиента. Если он сообщает «The server requested authentication method unknown to the client», клиент слишком стар для MySQL 8 и его пора обновить.
AppArmor в Ubuntu
В Ubuntu AppArmor включён по умолчанию и ограничивает сервер баз данных, то есть процесс mysqld или mariadbd. Инструмент mysqldump он не ограничивает, и именно это различие решает, попадёте вы в ловушку или нет.
Выгрузка из этого руководства пишет файл средствами оболочки (| gzip > ...), то есть от имени пользователя, который запускает скрипт. Этого пути AppArmor не касается, и он работает в любом каталоге, куда пользователю разрешена запись. Но как только пишет сам сервер, действуют другие правила. Так происходит при mysqldump --tab=/path и при любом SELECT ... INTO OUTFILE, ведь файл в этом случае создаёт серверный процесс, а не ваша оболочка. Тогда срабатывают две независимые друг от друга блокировки: серверная переменная secure_file_priv и профиль AppArmor для сервера. В Ubuntu переменная по умолчанию имеет значение /var/lib/mysql-files/, и ровно этот каталог разрешён в профиле AppArmor. Поэтому целевой путь вроде /opt/mysqlbackups упирается сразу в два запрета.
Коварнее всего здесь диагностика. Первая блокировка заявляет о себе внятно: ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement. Вторая же проявляется как скупое Errcode: 13 "Permission denied", хотя владелец и права целевого каталога на первый взгляд полностью в порядке. Тот, кто начинает раздавать права на файлы, ищет не там. В этом случае проверьте и то, и другое:
mysql -e "SHOW VARIABLES LIKE 'secure_file_priv';"
aa-status | grep -Ei 'mysqld|mariadbd'
journalctl -k | grep -i 'apparmor.*DENIED' | tail -n 20
Если в выводе последней команды появляется строка с apparmor="DENIED" и вашим целевым путём, причина найдена. Проще всего до этого вообще не доводить и остаться при выгрузке через конвейер. Если запись со стороны сервера действительно нужна, пишите в /var/lib/mysql-files/, а затем переносите файл. И только если оба варианта отпадают, дополните профиль нужным путём. Для этого предусмотрен файл /etc/apparmor.d/local/usr.sbin.mysqld, а в случае MariaDB соответственно usr.sbin.mariadbd, после чего выполняется systemctl reload apparmor. Отключение AppArmor решением не является: это снятие защитного слоя, который прикрывает как раз этот сервер.
Частые ошибки и их решения
Резервная копия размером 0 байт: чаще всего не совпадают учётные данные в /root/.my.cnf. Проверьте вход командой mysql --defaults-extra-file=/root/.my.cnf -e "SHOW DATABASES;". Нередко за этим стоит описанный выше случай, когда учётная запись работает через сокет и записанный пароль поэтому вообще не подходит.
«Access denied» в cron, но не в консоли: задание cron выполняется не от того пользователя, от которого вы ожидаете. Впишите задание в crontab пользователя root и используйте в скрипте только абсолютные пути. Другие причины собраны в статье как устранить MySQL Access denied for user.
Unknown table 'COLUMN_STATISTICS' in information_schema: вы снимаете копию базы MariaDB инструментом mysqldump из MySQL 8, например с машины на Ubuntu. При выгрузке MySQL 8 обращается к таблице, которой в MariaDB нет. Добавьте к вызову --column-statistics=0. Эта опция известна только клиенту MySQL, на чистой системе с MariaDB ставить её нельзя.
Access denied; you need (at least one of) the PROCESS privilege(s) for this operation: пользователю резервного копирования в MySQL 8 не разрешено читать сведения о табличных пространствах. Выдайте PROCESS, как описано выше, или добавьте --no-tablespaces.
mariadb-dump: command not found: скрипт пришёл с системы Debian с MariaDB 11, а работает теперь в Ubuntu с MySQL 8. Такого имени там нет. Пишите mysqldump, эта команда работает в обеих системах.
Can't connect to local MySQL server through socket: сервер не запущен либо сокет лежит не по тому пути, который ожидается. Возможные причины разобраны в статье как исправить ошибку сокета MySQL.
SELinux блокирует скрипт (AlmaLinux, Rocky Linux, RHEL): проверьте командой ausearch -m avc -ts recent, зафиксирован ли отказ в доступе, и при необходимости поправьте контекст каталога с резервными копиями.
Накопитель заполняется: уменьшите KEEP_DAYS или переносите старые копии во внешнее хранилище. Что ещё съедает место, показывает статья как освободить место, когда диск в Linux заполнен.
Сообщение об ошибке из-за --single-transaction на таблицах MyISAM: эта опция действует только с InnoDB. Для таблиц MyISAM вместо неё можно использовать --lock-tables, тогда таблицы блокируются на время выгрузки. Учтите также, что системные таблицы в MariaDB лежат в движке Aria и --single-transaction их не охватывает.
Раз уж вы всё равно занимаетесь базой данных, доведите дело до конца: удалите анонимных пользователей, сотрите тестовую базу и отключите удалённый доступ для root. Как это сделать, описано в статье защита MariaDB и MySQL.
Частые вопросы
Почему пароль от базы данных не должен стоять в команде mysqldump?
Чем Debian и Ubuntu отличаются при резервном копировании баз MySQL?
В Ubuntu AppArmor не даёт записать файл резервной копии. Что делать?
Подходит ли это руководство для любого дистрибутива Linux?
Что делать, если у учётной записи root в базе данных вообще нет пароля?
Как долго хранятся резервные копии?
Достаточно ли хранить резервные копии на том же сервере?
Как восстановить данные из резервной копии?
Задание cron не выполняется или сообщает Access denied. Что можно проверить?
Резервная копия весит 0 байт. В чём причина?
2024-2026 KernelHost GmbH. Все права защищены. Эта инструкция охраняется авторским правом. Публикация на других сайтах, в том числе частично или в изменённом виде, без нашего письменного согласия не разрешается. Цитирование с указанием источника и активной ссылкой мы приветствуем.

