Надёжнее всего бэкапить Linux VPS так: каждый день отправлять зашифрованные снапшоты restic в хранилище за пределами сервера, задать понятную политику хранения и регулярно проверять восстановление. В этой инструкции всё это настраивается на Debian 12/13, Ubuntu 22.04/24.04 и Rocky Linux / AlmaLinux: установка restic, выбор внешнего репозитория (S3-совместимое хранилище, SFTP или rclone), дампы баз данных, запуск по таймеру systemd и проверка того, что данные действительно можно вернуть.
Почему бэкапы должны храниться вне VPS
Копия на том же сервере — не бэкап: всё, что удаляет сервер, удаляет и её. На облачном VPS JUSTG данные безвозвратно удаляются и при вполне обычных действиях:
- Смена локации (Change location): устанавливается новый VPS с новым IP, ничего не копируется. Как только он готов, старый VPS и все его данные удаляются.
- Просроченное продление: неоплаченная услуга приостанавливается в день окончания срока; если счёт не оплачен и через 7 дней после этой даты, услуга отменяется, а данные удаляются.
- Переустановка ОС стирает диск, а отмена в режиме Immediate удаляет услугу и данные сразу после обработки запроса.
Прибавьте сюда опечатку в rm -rf, неудачное обновление или взлом — и правило становится очевидным: полагаться можно только на бэкап, который вы сделали сами и храните в другом месте. Перед сменой локации, переустановкой или отменой запустите бэкап вручную и убедитесь, что новый снапшот появился.
Шаг 1: Установите restic
В Debian 12/13, Ubuntu 24.04 и EPEL версия достаточно свежая:
# Debian 12/13, Ubuntu 24.04
apt update && apt install -y restic
restic version
# Rocky Linux / AlmaLinux 8, 9 (from EPEL)
dnf install -y epel-release && dnf install -y restic
В Ubuntu 22.04 стоит restic 0.12: без сжатия и без поддержки актуального формата репозитория. Там установите официальный бинарный файл и проверьте контрольную сумму:
# Official static binary (Ubuntu 22.04 or any distro with an old restic)
VER=0.18.1 # check https://github.com/restic/restic/releases for the latest version
cd /tmp
curl -fLO https://github.com/restic/restic/releases/download/v${VER}/restic_${VER}_linux_amd64.bz2
curl -fLO https://github.com/restic/restic/releases/download/v${VER}/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS # must print: OK
bunzip2 restic_${VER}_linux_amd64.bz2
install -m 0755 restic_${VER}_linux_amd64 /usr/local/bin/restic
restic version
Шаг 2: Выберите внешний репозиторий
Репозиторий — это место, где хранятся зашифрованные снапшоты. Выберите один вариант и сохраните его в /etc/restic/env (простые строки вида NAME=value).
Вариант A: S3-совместимое объектное хранилище
Подойдёт любой провайдер с S3 API. Создайте бакет и ключ доступа только к нему, а в URL укажите endpoint провайдера:
# /etc/restic/env (S3-compatible object storage)
RESTIC_REPOSITORY=s3:https://s3.example.com/example-backups/vps1
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
AWS_ACCESS_KEY_ID=REPLACE_WITH_ACCESS_KEY
AWS_SECRET_ACCESS_KEY=REPLACE_WITH_SECRET_KEY
# Optional: monitoring ping URL (Step 11)
HC_PING_URL=
Вариант B: SFTP на второй сервер
Используйте другой свой сервер — лучше в другой локации или у другого провайдера — с отдельным пользователем backup, которому принадлежит /srv/restic:
# /etc/restic/env (SFTP to a second server)
RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/vps1
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
HC_PING_URL=
# One-time SSH key login for root on the VPS
ssh-keygen -t ed25519 -N "" -f /root/.ssh/id_ed25519 # skip if the key exists
ssh-copy-id [email protected]
ssh [email protected] true # accept the host key once
Вариант C: бэкенд rclone
rclone подключает restic к десяткам сервисов (Backblaze B2, Google Drive, OneDrive, WebDAV и другим):
# Install and configure rclone (interactive wizard, run as root)
apt install -y rclone # Rocky/Alma: dnf install -y rclone (EPEL)
rclone config # create a remote, for example named "offsite"
# /etc/restic/env (any rclone backend)
RESTIC_REPOSITORY=rclone:offsite:example-backups/vps1
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
RCLONE_CONFIG=/root/.config/rclone/rclone.conf
HC_PING_URL=
/etc/restic/env обычно можно и удалять бэкапы, так что злоумышленник, захвативший VPS, способен стереть репозиторий. Для лучшей защиты включите версионирование или object lock для бакета либо используйте хранилище только на дозапись, а пароль от аккаунта хранилища на сервере не держите.Шаг 3: Создайте файл пароля и репозиторий
install -d -m 0700 /etc/restic
openssl rand -base64 32 > /etc/restic/password
chmod 600 /etc/restic/password
nano /etc/restic/env # paste one of the examples above
chmod 600 /etc/restic/env
# Load the settings into the current shell and create the repository
set -a; . /etc/restic/env; set +a
restic init
/etc/restic/password и /etc/restic/env в менеджер паролей или другое надёжное место вне сервера: если VPS пропадёт, для восстановления понадобятся только эти два файла.Шаг 4: Решите, что включать в бэкап, а что исключать
Бэкапьте конфигурацию и данные, а не всю операционную систему: переустановить ОС быстрее, чем восстанавливать системные файлы, а снапшоты остаются компактными.
# /etc/restic/include - one path per line; delete lines that do not exist
/etc
/root
/home
/srv
/var/www
/opt
/usr/local
/var/spool/cron
/var/backups/db
# /etc/restic/excludes
/root/.cache
/home/*/.cache
**/node_modules
**/*.tmp
/var/www/*/cache
- Включайте:
/etc, домашние каталоги, корни сайтов, данные приложений, crontab и папку с дампами баз. - Пропускайте:
/proc,/sys,/dev,/run,/tmp, swap-файлы и кэши. Их нет в списке, поэтому restic их даже не читает. - Файлы баз данных (
/var/lib/mysql,/var/lib/postgresql), скопированные на работающей базе, могут оказаться несогласованными. Бэкапьте дампы (шаг 5). - Docker: не бэкапьте
/var/lib/dockerцеликом; сохраняйте compose-файлы и тома или папки с данными.
Шаг 5: Делайте дамп баз данных перед каждым бэкапом
Скрипт ниже пишет сжатые дампы в /var/backups/db — эта папка уже есть в списке. Выставьте MYSQL и POSTGRES под свой сервер:
#!/bin/bash
# /usr/local/sbin/db-dump.sh - consistent database dumps before each backup
set -euo pipefail
umask 077
MYSQL=yes # yes / no
POSTGRES=no # yes / no
OUT=/var/backups/db
mkdir -p "$OUT"
if [ "$MYSQL" = yes ]; then
DUMP=$(command -v mariadb-dump || command -v mysqldump)
"$DUMP" --all-databases --single-transaction --quick \
--routines --events --triggers | gzip > "$OUT/mysql-all.sql.gz.tmp"
mv "$OUT/mysql-all.sql.gz.tmp" "$OUT/mysql-all.sql.gz"
fi
if [ "$POSTGRES" = yes ]; then
cd /tmp
runuser -u postgres -- pg_dumpall | gzip > "$OUT/postgres-all.sql.gz.tmp"
mv "$OUT/postgres-all.sql.gz.tmp" "$OUT/postgres-all.sql.gz"
fi
--single-transaction даёт согласованный дамп таблиц InnoDB без блокировки сайта. В Debian и Ubuntu root подключается к MariaDB/MySQL через локальный сокет без пароля; если у вас пароль нужен, укажите его в /root/.my.cnf (права 600), а не в командной строке.
Шаг 6: Создайте скрипт бэкапа
Один скрипт делает дампы, бэкап, чистку по политике хранения и еженедельную проверку, а также отчитывается на необязательный URL мониторинга (шаг 10). Любая ошибка останавливает его, так что сбои не проходят незамеченными:
#!/bin/bash
# /usr/local/sbin/restic-backup.sh - dump databases, back up, apply retention
set -euo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
set -a; . /etc/restic/env; set +a
fail() {
[ -n "${HC_PING_URL:-}" ] && curl -fsS -m 10 --retry 3 "$HC_PING_URL/fail" >/dev/null || true
}
trap fail ERR
/usr/local/sbin/db-dump.sh
restic backup \
--files-from /etc/restic/include \
--exclude-file /etc/restic/excludes \
--exclude-caches \
--tag daily
restic forget --prune \
--keep-daily 7 --keep-weekly 4 --keep-monthly 6
# Sundays: verify the repository and read back 5% of the data
if [ "$(date +%u)" = 7 ]; then
restic check --read-data-subset=5%
fi
if [ -n "${HC_PING_URL:-}" ]; then
curl -fsS -m 10 --retry 3 "$HC_PING_URL" >/dev/null
fi
Сделайте скрипты исполняемыми и запустите первый бэкап вручную. Первый запуск выгружает всё, следующие — только изменения.
chmod 700 /usr/local/sbin/db-dump.sh /usr/local/sbin/restic-backup.sh
/usr/local/sbin/restic-backup.sh # first run by hand, watch for errors
restic snapshots
Шаг 7: Запуск по таймеру systemd (или через cron)
Таймер systemd — предпочтительный вариант: он пишет в журнал, догоняет пропущенный запуск (Persistent=true) и понижает приоритет задачи по CPU и диску, чтобы сайт не тормозил.
# /etc/systemd/system/restic-backup.service
[Unit]
Description=restic backup to off-site repository
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-backup.sh
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Daily restic backup
[Timer]
OnCalendar=*-*-* 03:30:00
RandomizedDelaySec=30min
Persistent=true
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer
# Test the service once and read its log
systemctl start restic-backup.service
journalctl -u restic-backup.service -n 50 --no-pager
Если вам удобнее cron, используйте вместо таймера (не одновременно):
# /etc/cron.d/restic-backup (only if you do not use the systemd timer)
30 3 * * * root /usr/local/sbin/restic-backup.sh >> /var/log/restic-backup.log 2>&1
Шаг 8: Политика хранения: restic forget --prune
restic forget решает, какие снапшоты оставить, а --prune удаляет данные, которые больше не нужны ни одному снапшоту. Политика в скрипте хранит:
| Параметр | Что сохраняется |
|---|---|
--keep-daily 7 | последний снапшот каждого из последних 7 дней |
--keep-weekly 4 | по одному снапшоту в неделю за 4 недели |
--keep-monthly 6 | по одному снапшоту в месяц за 6 месяцев |
Прежде чем полагаться на новую политику, проверьте её с --dry-run:
set -a; . /etc/restic/env; set +a
restic snapshots
# Preview what a policy would remove, without deleting anything
restic forget --dry-run --keep-daily 7 --keep-weekly 4 --keep-monthly 6
restic stats --mode raw-data
Каждое воскресенье скрипт также запускает restic check. Он проверяет структуру репозитория, а --read-data-subset=5% каждый раз скачивает и сверяет другие 5% данных, так что повреждение обнаружится раньше, чем бэкап понадобится. Для дополнительной уверенности restic check можно запустить вручную в любой момент.
Шаг 9: Проверьте восстановление
Бэкап, из которого вы ни разу не восстанавливались, — это лишь надежда. Раз в месяц восстанавливайте что-нибудь реальное во временную папку и сравнивайте:
set -a; . /etc/restic/env; set +a
restic snapshots
restic ls latest /etc/nginx
# Restore one directory to a scratch location, then compare
restic restore latest --target /tmp/restore-test --include /etc/nginx
diff -r /etc/nginx /tmp/restore-test/etc/nginx && echo "restore OK"
# Check that the database dump is complete (last line: "-- Dump completed on ...")
restic dump latest /var/backups/db/mysql-all.sql.gz | zcat | tail -n 1
rm -rf /tmp/restore-test
Один раз отрепетируйте и худший сценарий, например на запасном VPS: новый сервер, у вас есть только два сохранённых файла.
# On the NEW server: install restic, then restore the two saved files
install -d -m 0700 /etc/restic
nano /etc/restic/password && nano /etc/restic/env
set -a; . /etc/restic/env; set +a
restic restore latest --target /srv/restore
rsync -a /srv/restore/var/www/ /var/www/ # copy back what you need
zcat /srv/restore/var/backups/db/mysql-all.sql.gz | mariadb # or: mysql
restic restore latest --target / на работающей системе: команда перезапишет живые файлы, включая системные настройки, которые могут не подойти новому серверу. Восстанавливайте в отдельную папку и копируйте обратно только нужное.Шаг 10: Получайте оповещения о сбоях бэкапа
Бэкапы обычно ломаются тихо: истёк ключ, переполнен бакет, сменился пароль. Помогает сервис по принципу «dead man's switch»: после каждого успешного запуска скрипт обращается к URL, а если обращение не пришло вовремя, вы получаете оповещение. Разверните такой сервис сами (например, open-source Healthchecks) или возьмите любой аналог и добавьте URL проверки в /etc/restic/env:
HC_PING_URL=https://hc.example.com/ping/REPLACE-WITH-YOUR-CHECK-ID
При успехе скрипт обращается к этому URL, при ошибке — к $HC_PING_URL/fail. Задайте период проверки в один день с запасом в несколько часов. Если такого сервиса нет, хотя бы раз в неделю смотрите restic snapshots и journalctl -u restic-backup.service.
FAQ
Сколько места нужно в хранилище?
Для первого снапшота — примерно объём ваших данных (часто меньше благодаря сжатию), дальше немного в день на изменения. Через пару недель запустите restic stats --mode raw-data и посмотрите реальную цифру.
restic пишет, что репозиторий заблокирован. Что делать?
Предыдущий запуск прервался и оставил устаревшую блокировку. Убедитесь, что restic сейчас не работает (pgrep -a restic), затем выполните restic unlock и снова запустите бэкап.
На тарифе с 512 МБ restic расходует слишком много памяти. Можно ли это уменьшить?
Добавьте GOGC=20 в /etc/restic/env, чтобы restic чаще освобождал память, держите список путей компактным и проверьте, что на VPS есть swap. Если памяти всё равно не хватает, повысьте тариф на странице услуги. Нужна помощь — создайте тикет: у JUSTG работает AI-поддержка 24/7 по тикетам, а инженеры подключаются, когда нужен человек.