Архив каталога /var/lib/mysql во время работающей базы — не надёжная резервная копия. Копия только SQL без wp-content/uploads — тоже неполный комплект. Для WordPress в Docker нужны как минимум два согласованных компонента: логический дамп MariaDB и файлы сайта.
Ниже — процедура для Compose-стека, где сервисы называются db, wordpress и cli, а данные лежат в именованных volumes. Команды не требуют публикации порта базы.
Сначала определите, что восстанавливать
Минимальный комплект WordPress:
- SQL-дамп базы;
wp-content/uploads;
- собственные плагины и темы, если они не собираются из репозитория;
wp-config.php, если его настройки не формируются окружением;
- secrets и инструкция по их созданию — но не открытые пароли в общем архиве;
- версия
compose.yaml и список образов.
Если весь /var/www/html находится в volume, проще сохранить его целиком. Архив получится больше, зато восстановление не зависит от того, какие плагины когда-то ставили через админку.
1. Создайте каталог копии
backup_dir="$PWD/backups/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$backup_dir"
Путь абсолютный: это важно для последующего bind mount во временный контейнер.
Зафиксируйте конфигурацию и фактические образы:
docker compose config > "$backup_dir/compose.resolved.yaml"
docker compose images > "$backup_dir/images.txt"
В compose.resolved.yaml могут оказаться обычные environment-переменные. Перед переносом архива в общее хранилище проверьте файл и не оставляйте в нём секреты. Compose secrets, подключённые через файлы, в содержимое YAML не разворачиваются.
2. Остановите запись на время снимка
Для небольшого сайта понятнее короткое окно обслуживания, чем попытка угадать, какие плагины продолжают писать в базу и uploads.
docker compose run --rm cli maintenance-mode activate
docker compose stop wordpress
MariaDB остаётся запущенной, но веб-приложение больше не меняет данные. Если команда прервётся, не забудьте позже выполнить docker compose start wordpress и отключить режим обслуживания.
3. Выгрузите MariaDB штатным инструментом
docker compose exec -T db sh -lc \
'mariadb-dump --user=root --password="$(cat /run/secrets/db_root_password)" \
--single-transaction --quick --routines --events --triggers --hex-blob wordpress' \
| gzip -9 > "$backup_dir/wordpress.sql.gz"
--single-transaction даёт согласованный снимок таблиц InnoDB без долгой блокировки. Ключ не превращает MyISAM в транзакционное хранилище: если остались нетранзакционные таблицы, учитывайте их отдельно или сохраняйте базу в остановленном режиме.
Параметр -T у docker compose exec отключает псевдотерминал. Без него перенаправление бинарного или сжатого вывода в файл иногда получает лишние управляющие данные.
4. Сохраните файлы из volume
Получите ID остановленного контейнера WordPress и подключите его volumes к одноразовому контейнеру только для чтения:
wordpress_container="$(docker compose ps -aq wordpress)"
docker run --rm \
--volumes-from "$wordpress_container:ro" \
--mount "type=bind,src=$backup_dir,dst=/backup" \
alpine:3.22 \
tar --exclude='./.maintenance' -C /var/www/html -czf /backup/wordpress-files.tar.gz .
Файл .maintenance исключён: после восстановления сайт не должен навсегда остаться на служебной странице. Если ваша конфигурация хранит пользовательские данные в других volumes, добавьте их в комплект отдельно.
5. Верните сайт и проверьте архивы
docker compose start wordpress
docker compose run --rm cli maintenance-mode deactivate
gzip -t "$backup_dir/wordpress.sql.gz"
tar -tzf "$backup_dir/wordpress-files.tar.gz" | head
sha256sum \
"$backup_dir/wordpress.sql.gz" \
"$backup_dir/wordpress-files.tar.gz" \
> "$backup_dir/SHA256SUMS"
gzip -t и просмотр списка tar находят обрыв файла, но не доказывают, что сайт восстановится. Контрольные суммы пригодятся при переносе в другое хранилище.
Скопируйте комплект за пределы того же сервера. Если диск или VPS пропадёт вместе с каталогом backups, локальная копия не поможет. Для важных данных разумно иметь несколько копий на разных носителях, причём хотя бы одну — недоступную обычной учётной записи приложения.
6. Проведите пробное восстановление
Используйте отдельное имя Compose-проекта, отдельные volumes и другой локальный порт. Проще всего скопировать compose.yaml в временный каталог, изменить публикацию WordPress с 8080 на 18080 и выполнить:
restore_project="wordpress-restore-$(date +%s)"
docker compose -p "$restore_project" up -d db
Дождитесь healthy-состояния и импортируйте дамп в новую базу:
gzip -dc "$backup_dir/wordpress.sql.gz" \
| docker compose -p "$restore_project" exec -T db sh -lc \
'mariadb --user=root --password="$(cat /run/secrets/db_root_password)" wordpress'
Создайте, но пока не запускайте контейнер WordPress, затем разверните файлы в его новый volume:
docker compose -p "$restore_project" create wordpress
restore_container="$(docker compose -p "$restore_project" ps -aq wordpress)"
docker run --rm \
--volumes-from "$restore_container" \
--mount "type=bind,src=$backup_dir,dst=/backup,readonly" \
alpine:3.22 \
sh -lc 'find /var/www/html -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + && tar -C /var/www/html -xzf /backup/wordpress-files.tar.gz'
Запустите восстановленный сайт:
docker compose -p "$restore_project" up -d wordpress
docker compose -p "$restore_project" ps
Откройте тестовый адрес и проверьте вход, несколько записей, изображения, поиск и административную часть. Почту на стенде направьте в Mailpit или полностью изолируйте, чтобы восстановленный сайт не отправил реальные уведомления.
После проверки удалите именно временный проект. Сначала выведите его имя и контейнеры, чтобы не попасть в рабочий стек:
docker compose -p "$restore_project" ps
docker compose -p "$restore_project" down -v
Для тестового проекта -v нужен: он удалит только созданные для проверки volumes. Для production такой ключ без осознанного сброса использовать нельзя.
Что считать успешным бэкапом
Копия готова не тогда, когда команда завершилась без ошибки, а когда выполнены четыре условия:
- архивы проходят проверку целостности;
- они находятся не только на исходном сервере;
- восстановление повторяется по записанной инструкции;
- тестовый сайт открывается и содержит ожидаемые данные.
Запишите дату последней проверки и реальное время восстановления. Это сразу показывает, укладывается ли процедура в допустимый простой.
Официальные источники