Ежедневное резервное копирование баз MySQL в Debian, Ubuntu и Linux

Опубликовано Обновлено 12 мин. чтения

Небольшого скрипта и задания 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-servermariadb-server
Debian 13отсутствует11.8
Debian 12отсутствует10.11
Ubuntu 24.04 LTS8.010.11
Ubuntu 22.04 LTS8.010.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/, и приведённый выше скрипт работает в обеих без изменений. Тем не менее есть шесть пунктов, в которых они расходятся, и каждый из них способен незаметно сорвать резервное копирование:

ТемаDebianUbuntu
Сервер баз данныхтолько MariaDBMySQL 8.0 или MariaDB
Клиентский пакетmariadb-clientmysql-client-8.0 или mariadb-client
Утилита выгрузкиmariadb-dump, mysqldump как ссылкапри MySQL только mysqldump
Вход под rootunix_socketauth_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?
Командные строки видны через команду ps всем пользователям системы. Поэтому запишите пользователя и пароль в файл /root/.my.cnf и выставьте права командой chmod 600, чтобы читать его мог только root. В скрипте файл подключается ключом --defaults-extra-file, и этот ключ обязательно должен стоять первым.
Чем Debian и Ubuntu отличаются при резервном копировании баз MySQL?
Прежде всего сервером баз данных. В Debian нет пакета mysql-server, там везде используется MariaDB (в Debian 13 версии 11.8, в Debian 12 версии 10.11), а Ubuntu поставляет и MySQL 8.0, и MariaDB. Отсюда четыре практических различия: клиентский пакет называется mariadb-client, а не mysql-client-8.0; начиная с MariaDB 11 утилита называется mariadb-dump (mysqldump остаётся как ссылка, а в MySQL 8 существует только это имя); сервисную учётную запись debian-sys-maint в /etc/mysql/debian.cnf создаёт теперь только Ubuntu; лог ошибок в MariaDB попадает в журнал, а не в /var/log/mysql/error.log. Сам скрипт резервного копирования работает в обеих системах без изменений.
В Ubuntu AppArmor не даёт записать файл резервной копии. Что делать?
Сначала выясните, кто на самом деле пишет файл. AppArmor ограничивает сервер баз данных, а не утилиту mysqldump. Выгрузку через конвейер в gzip пишет оболочка, поэтому её это не касается. Если же файл создаёт сервер, например при mysqldump --tab или SELECT ... INTO OUTFILE, срабатывают две блокировки: переменная secure_file_priv (в Ubuntu по умолчанию /var/lib/mysql-files/) и профиль AppArmor для сервера. Вторая проявляется лишь как Errcode 13 Permission denied, хотя права на файлы в порядке. Увидеть её можно командой journalctl -k и поиском по apparmor=DENIED. Решение: либо остаться при выгрузке через конвейер, либо писать в /var/lib/mysql-files/ и затем переносить файл. Отключение AppArmor решением не является.
Подходит ли это руководство для любого дистрибутива Linux?
Да. Сам скрипт резервного копирования от дистрибутива не зависит. Отличается только управление пакетами: Debian и Ubuntu используют apt, а AlmaLinux, Rocky Linux и RHEL используют dnf. В актуальных системах семейства RHEL dnf пришёл на смену yum. Старая команда там обычно ещё работает как ссылка, но использовать следует именно dnf.
Что делать, если у учётной записи root в базе данных вообще нет пароля?
В Debian и Ubuntu root по умолчанию защищён идентичностью системного пользователя: в MariaDB через unix_socket, в MySQL через auth_socket. Пароля тогда не существует, и запись в файле с учётными данными ни на что не влияет. Либо запускайте скрипт от системного пользователя root и откажитесь от файла с учётными данными, либо заведите отдельного пользователя для резервного копирования с правами SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES, RELOAD и PROCESS. Второй путь чище, потому что обходится без полных прав учётной записи root.
Как долго хранятся резервные копии?
В примере скрипта семь дней. Более старые файлы команда find удаляет автоматически. Через переменную KEEP_DAYS в скрипте вы подстраиваете срок хранения под свободное место.
Достаточно ли хранить резервные копии на том же сервере?
Нет. При отказе накопителя, атаке шифровальщика или случайно удалённом каталоге копии пострадают ровно так же, как и сама база данных. Копия, которая разделяет судьбу оригинала, копией не является. Поэтому передавайте файлы дополнительно по rsync или scp во второе место, например в Storage Box или на второй сервер в другой локации.
Как восстановить данные из резервной копии?
Командой zcat с передачей вывода клиенту базы данных, то есть zcat /opt/mysqlbackups/alldbs_2026-07-26-05-00.sql.gz и конвейер на mysql. Восстановление лучше хотя бы раз осознанно проверить на тестовой системе. Если вы хотите восстанавливать отдельные базы по отдельности, пишите в скрипте по одному файлу на каждую базу данных.
Задание cron не выполняется или сообщает Access denied. Что можно проверить?
Сначала проверьте командой crontab -l, действительно ли запись сохранена и находится ли она в crontab пользователя root. Во многих образах Ubuntu вход под root отключён, там команда выглядит как sudo crontab -e. Используйте в скрипте только абсолютные пути. В минимальных установках семейства RHEL службы cron часто нет: установите пакет cronie и включите службу crond.
Резервная копия весит 0 байт. В чём причина?
Чаще всего не совпадают учётные данные в /root/.my.cnf. Проверьте вход командой mysql --defaults-extra-file=/root/.my.cnf с простым запросом вроде SHOW DATABASES. Проверьте также, действительно ли доработала до конца выгрузка, которая выглядит полной: последняя строка законченного экспорта начинается с -- Dump completed on. Если её нет, выгрузка была прервана, и файл как резервная копия ничего не стоит.

Резервное копирование MySQL Резервное копирование MariaDB mysqldump Резервное копирование Cron MySQL Linux Debian Ubuntu