Что бэкапит скрипт:
- PostgreSQL - Все пользователи, домены, пароли
- Конфиги - settings.py, uWSGI, Nginx, systemd
- Почта (vmail) - Все письма всех пользователей
- Статика/медиа - Аватары, загруженные файлы
- Фронтенд - Веб-интерфейс Modoboa
Особенности:
- Все временные файлы на RAID (никаких проблем с /tmp)
- Красивые прогресс-бары с реальной скоростью
- Проверка свободного места перед стартом
- Автоматическая очистка старых бэкапов
- Проверка целостности архивов
- Email-уведомления (опционально)
- Работает из cron
Установка
Шаг 1: Создаём структуру папок
- Code: Select all
sudo mkdir -p /srv/vmail/backups/modoboa/{daily,weekly,monthly,logs,temp}
Шаг 2: Создаём скрипт
- Code: Select all
sudo nano /usr/local/bin/backup_modoboa_raid.sh
- Code: Select all
#!/bin/bash
# ==================================================
# 🚀 Modoboa Backup Script with Centralized Config
# ==================================================
# ========== КОНФИГУРАЦИЯ - ВСЁ ЗДЕСЬ ==========
# Изменяй эти переменные под свою систему!
# Основные пути
RAID_BASE="/srv/vmail/backups/modoboa" # Корень бэкапов на RAID
TEMP_DIR="$RAID_BASE/temp" # Временная папка на RAID
DAILY_DIR="$RAID_BASE/daily" # Ежедневные бэкапы
WEEKLY_DIR="$RAID_BASE/weekly" # Еженедельные
MONTHLY_DIR="$RAID_BASE/monthly" # Ежемесячные
LOG_DIR="$RAID_BASE/logs" # Логи
# Пути к данным Modoboa (измени, если у тебя иначе)
MAIL_DIR="/srv/vmail" # Почтовые ящики
MODOBOA_INSTANCE="/srv/modoboa/instance" # Инстанс Modoboa
DJANGO_SETTINGS="$MODOBOA_INSTANCE/instance/settings.py" # Настройки Django
FRONTEND_DIR="$MODOBOA_INSTANCE/frontend" # Фронтенд
STATIC_DIR="$MODOBOA_INSTANCE/sitestatic" # Статика
MEDIA_DIR="$MODOBOA_INSTANCE/media" # Медиа-файлы
# Пути к конфигам системы
UWSGI_CONFIG="/etc/uwsgi/apps-available/modoboa_instance.ini" # uWSGI
SYSTEMD_SERVICE="/etc/systemd/system/modoboa-uwsgi.service" # systemd
NGINX_CONFIG="/etc/nginx/sites-available/mail.br-1.ru.conf" # Nginx
# База данных
DB_NAME="modoboa" # Имя базы данных
DB_USER="modoboa" # Пользователь БД (не меняй, postgres сам)
# Настройки бэкапа
MIN_FREE_SPACE_GB=50 # Минимум свободных ГБ
NOTIFY_EMAIL="atehdmin@gmail.com" # Email для уведомлений
# Сроки хранения (в днях)
DAILY_KEEP=7 # Хранить daily 7 дней
WEEKLY_KEEP=28 # Хранить weekly 28 дней
MONTHLY_KEEP=90 # Хранить monthly 90 дней
LOG_KEEP=30 # Хранить логи 30 дней
# ==================================================
# ДАЛЬШЕ НИЧЕГО НЕ МЕНЯЙ, ЕСЛИ НЕ УВЕРЕН!
# ==================================================
DATE=$(date +%Y%m%d_%H%M%S)
DAY_OF_WEEK=$(date +%u)
DAY_OF_MONTH=$(date +%d)
LOG_FILE="$LOG_DIR/backup_$DATE.log"
# Цвета
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# ========== ЛОВУШКА ДЛЯ ОЧИСТКИ ==========
cleanup() {
echo -e "\n${YELLOW}🧹 Очистка временных файлов...${NC}"
rm -rf "$TEMP_DIR" 2>/dev/null
echo -e "${GREEN}✓ Готово${NC}"
}
trap cleanup EXIT INT TERM
# ========== ФУНКЦИИ ==========
log() {
echo -e "$1"
echo "$(date '+%Y-%m-%d %H:%M:%S') - $2" >> "$LOG_FILE"
}
error_exit() {
log "${RED}❌ ОШИБКА: $1${NC}" "$1"
exit 1
}
check_command() {
if ! command -v $1 &> /dev/null; then
error_exit "Команда $1 не найдена. Установи: sudo apt-get install $1"
fi
}
show_spinner() {
local pid=$1
local message=$2
local spin=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
local i=0
while kill -0 $pid 2>/dev/null; do
printf "\r ${CYAN}%-20s${NC} ${YELLOW}%s${NC}" "$message" "${spin[$i]}"
i=$(( (i+1) % 10 ))
sleep 0.1
done
printf "\n"
}
check_free_space() {
local free_bytes=$(df -B1 "$RAID_BASE" | tail -1 | awk '{print $4}')
local free_gb=$((free_bytes / 1024 / 1024 / 1024))
echo -e " Свободно: ${GREEN}$free_gb GB${NC}"
if [ $free_gb -lt $MIN_FREE_SPACE_GB ]; then
log "${YELLOW}⚠️ Мало места! Нужно $MIN_FREE_SPACE_GB GB, есть $free_gb GB${NC}"
echo -e " ${YELLOW}Очищаем старые бэкапы...${NC}"
find "$DAILY_DIR" -type f -name "*.gz" -mtime +$DAILY_KEEP -delete
find "$WEEKLY_DIR" -type f -name "*.gz" -mtime +$WEEKLY_KEEP -delete
find "$MONTHLY_DIR" -type f -name "*.gz" -mtime +$MONTHLY_KEEP -delete
free_bytes=$(df -B1 "$RAID_BASE" | tail -1 | awk '{print $4}')
free_gb=$((free_bytes / 1024 / 1024 / 1024))
if [ $free_gb -lt $MIN_FREE_SPACE_GB ]; then
error_exit "Недостаточно места даже после очистки!"
fi
fi
}
check_path() {
if [ ! -e "$1" ]; then
error_exit "Путь не существует: $1"
fi
}
# ========== ПРОВЕРКИ ==========
clear
echo "=================================================="
echo -e "${BLUE}🚀 Modoboa Backup to RAID${NC}"
echo "=================================================="
# Проверяем необходимые команды
check_command "rsync"
check_command "gzip"
# Проверяем все пути из конфига
echo "🔍 Проверка путей:"
check_path "$MAIL_DIR"
check_path "$DJANGO_SETTINGS"
check_path "$FRONTEND_DIR"
check_path "$STATIC_DIR"
check_path "$MEDIA_DIR"
check_path "$UWSGI_CONFIG"
check_path "$SYSTEMD_SERVICE"
check_path "$NGINX_CONFIG"
echo -e " ${GREEN}✓ Все пути найдены${NC}"
# Создаем директории
mkdir -p "$DAILY_DIR" "$WEEKLY_DIR" "$MONTHLY_DIR" "$LOG_DIR" "$TEMP_DIR"
echo -e "📁 Временная папка: ${CYAN}$TEMP_DIR${NC}"
# Проверяем PostgreSQL
echo -n "🔍 PostgreSQL... "
if ! sudo systemctl is-active --quiet postgresql; then
error_exit "PostgreSQL не запущен!"
fi
if ! sudo -u postgres psql -lqt | cut -d \| -f 1 | grep -qw "$DB_NAME"; then
error_exit "База '$DB_NAME' не найдена!"
fi
echo -e "${GREEN}✓${NC}"
# Проверяем RAID
echo -n "🔍 RAID... "
if ! mountpoint -q "$MAIL_DIR"; then
error_exit "$MAIL_DIR не смонтирован!"
fi
check_free_space
echo -e "${GREEN}✓${NC}"
# Определяем тип бэкапа
TARGET_DIR="$DAILY_DIR"
BACKUP_TYPE="daily"
[ "$DAY_OF_WEEK" -eq 7 ] && TARGET_DIR="$WEEKLY_DIR" && BACKUP_TYPE="weekly"
[ "$DAY_OF_MONTH" -eq 1 ] && TARGET_DIR="$MONTHLY_DIR" && BACKUP_TYPE="monthly"
echo "=================================================="
echo -e "📦 Тип: ${YELLOW}$BACKUP_TYPE${NC}"
echo -e "📁 Цель: ${CYAN}$TARGET_DIR${NC}"
echo "=================================================="
# ========== 1. БАЗА ДАННЫХ ==========
echo -n "📦 PostgreSQL "
TEMP_DB="$TEMP_DIR/${DB_NAME}_db_$DATE.sql"
sudo -u postgres pg_dump "$DB_NAME" > "$TEMP_DB" &
DB_PID=$!
show_spinner $DB_PID "Бэкап базы"
if wait $DB_PID; then
gzip -c "$TEMP_DB" > "$TARGET_DIR/modoboa_db_$DATE.sql.gz"
rm -f "$TEMP_DB"
DB_SIZE=$(du -h "$TARGET_DIR/modoboa_db_$DATE.sql.gz" | cut -f1)
echo -e "\r📦 PostgreSQL ${GREEN}✓${NC} ($DB_SIZE)"
log "База данных" "успешно ($DB_SIZE)"
else
rm -f "$TEMP_DB" 2>/dev/null
error_exit "Бэкап базы данных провалился!"
fi
# ========== 2. КОНФИГИ ==========
echo -n "⚙️ Конфиги "
tar -czf "$TARGET_DIR/modoboa_configs_$DATE.tar.gz" \
"$DJANGO_SETTINGS" \
"$UWSGI_CONFIG" \
"$SYSTEMD_SERVICE" \
"$NGINX_CONFIG" 2>/dev/null &
CFG_PID=$!
show_spinner $CFG_PID "Архивация конфигов"
if wait $CFG_PID; then
CFG_SIZE=$(du -h "$TARGET_DIR/modoboa_configs_$DATE.tar.gz" | cut -f1)
echo -e "\r⚙️ Конфиги ${GREEN}✓${NC} ($CFG_SIZE)"
log "Конфиги" "успешно ($CFG_SIZE)"
else
error_exit "Бэкап конфигов провалился!"
fi
# ========== 3. ПОЧТА (VMAIL) ==========
echo "📧 Почта "
MAIL_TEMP="$TEMP_DIR/vmail_copy_$DATE"
mkdir -p "$MAIL_TEMP"
# Считаем общий размер почты
TOTAL_MAIL_SIZE=$(du -sb "$MAIL_DIR" | awk '{print $1}')
echo -e " ${CYAN}Всего писем:${NC} $(numfmt --to=iec $TOTAL_MAIL_SIZE)"
# Копируем с прогресс-баром
echo -e " ${CYAN}Копирование на RAID:${NC}"
rsync -a --delete "$MAIL_DIR/" "$MAIL_TEMP/" \
--exclude="backups/" \
--info=progress2 \
--no-inc-recursive
RSYNC_EXIT=$?
if [ $RSYNC_EXIT -ne 0 ] && [ $RSYNC_EXIT -ne 24 ]; then
error_exit "Копирование почты провалилось! (код $RSYNC_EXIT)"
else
if [ $RSYNC_EXIT -eq 24 ]; then
echo -e " ${YELLOW}⚠️ Некоторые временные файлы исчезли (нормально для Dovecot)${NC}"
fi
echo -e " ${GREEN}✓ Копирование завершено${NC}"
fi
# Архивируем
echo -e " ${CYAN}Архивация:${NC}"
tar -czf "$TARGET_DIR/modoboa_vmail_$DATE.tar.gz" -C "$MAIL_TEMP" . &
TAR_PID=$!
show_spinner $TAR_PID "Создание архива"
if wait $TAR_PID; then
MAIL_SIZE=$(du -h "$TARGET_DIR/modoboa_vmail_$DATE.tar.gz" | cut -f1)
echo -e "\r📧 Почта ${GREEN}✓${NC} ($MAIL_SIZE)"
log "Почтовые ящики" "успешно ($MAIL_SIZE)"
rm -rf "$MAIL_TEMP"
else
error_exit "Архивация почты провалилась!"
fi
# ========== 4. СТАТИКА И МЕДИА ==========
echo -n "🖼️ Статика/медиа "
tar -czf "$TARGET_DIR/modoboa_static_media_$DATE.tar.gz" \
"$STATIC_DIR" \
"$MEDIA_DIR" 2>/dev/null &
STAT_PID=$!
show_spinner $STAT_PID "Архивация статики"
if wait $STAT_PID; then
STAT_SIZE=$(du -h "$TARGET_DIR/modoboa_static_media_$DATE.tar.gz" | cut -f1)
echo -e "\r🖼️ Статика/медиа ${GREEN}✓${NC} ($STAT_SIZE)"
log "Статика и медиа" "успешно ($STAT_SIZE)"
else
error_exit "Бэкап статики провалился!"
fi
# ========== 5. ФРОНТЕНД ==========
echo -n "🎨 Фронтенд "
tar -czf "$TARGET_DIR/modoboa_frontend_$DATE.tar.gz" "$FRONTEND_DIR" 2>/dev/null &
FRONT_PID=$!
show_spinner $FRONT_PID "Архивация фронтенда"
if wait $FRONT_PID; then
FRONT_SIZE=$(du -h "$TARGET_DIR/modoboa_frontend_$DATE.tar.gz" | cut -f1)
echo -e "\r🎨 Фронтенд ${GREEN}✓${NC} ($FRONT_SIZE)"
log "Фронтенд" "успешно ($FRONT_SIZE)"
else
error_exit "Бэкап фронтенда провалился!"
fi
# ========== 6. ИНФОРМАЦИЯ ==========
echo -n "📝 Инфо-файл "
{
echo "==================================="
echo "Modoboa Backup Report"
echo "==================================="
echo "Дата: $(date '+%Y-%m-%d %H:%M:%S')"
echo "Тип: $BACKUP_TYPE"
echo "Сервер: $(hostname)"
echo "RAID: $MAIL_DIR"
echo ""
echo "Файлы бэкапа:"
ls -lh "$TARGET_DIR" | grep "$DATE"
echo ""
echo "Свободное место после бэкапа:"
df -h "$MAIL_DIR"
echo "==================================="
} > "$TARGET_DIR/backup_info_$DATE.txt"
echo -e "${GREEN}✓${NC}"
log "Информация" "создана"
# ========== 7. ОЧИСТКА ==========
echo -n "🧹 Очистка "
CLEANED=0
CLEANED=$((CLEANED + $(find "$DAILY_DIR" -type f -name "*.gz" -mtime +$DAILY_KEEP -delete -print | wc -l)))
CLEANED=$((CLEANED + $(find "$WEEKLY_DIR" -type f -name "*.gz" -mtime +$WEEKLY_KEEP -delete -print | wc -l)))
CLEANED=$((CLEANED + $(find "$MONTHLY_DIR" -type f -name "*.gz" -mtime +$MONTHLY_KEEP -delete -print | wc -l)))
find "$LOG_DIR" -type f -name "*.log" -mtime +$LOG_KEEP -delete
echo -e "${GREEN}✓${NC} (удалено $CLEANED файлов)"
log "Очистка" "удалено $CLEANED старых бэкапов"
# ========== 8. ПРОВЕРКА ==========
echo "🔍 Проверка целостности:"
ERRORS=0
for file in "$TARGET_DIR"/modoboa_*"$DATE"*.gz; do
if [ -f "$file" ]; then
echo -n " → $(basename $file)... "
if gzip -t "$file" 2>/dev/null; then
echo -e "${GREEN}✓${NC}"
else
echo -e "${RED}✗${NC}"
ERRORS=$((ERRORS + 1))
fi
fi
done
# ========== 9. ИТОГ ==========
echo "=================================================="
if [ $ERRORS -eq 0 ]; then
echo -e "${GREEN}✅ Бэкап успешно завершен!${NC}"
else
echo -e "${RED}⚠️ Бэкап завершен с $ERRORS ошибками!${NC}"
fi
echo "=================================================="
echo -e "📁 Директория: ${YELLOW}$TARGET_DIR${NC}"
echo -e "📊 Размеры файлов:"
ls -lh "$TARGET_DIR" | grep "$DATE" | awk '{print " " $5 " " $9}'
echo -e "📋 Лог: ${BLUE}$LOG_FILE${NC}"
echo -e "💾 Свободно: ${GREEN}$(df -h "$MAIL_DIR" | tail -1 | awk '{print $4}')${NC}"
echo "=================================================="
Шаг 3: Делаем скрипт исполняемым
- Code: Select all
sudo chmod +x /usr/local/bin/backup_modoboa_raid.sh
Настройка под свою систему
В начале скрипта есть блок КОНФИГУРАЦИЯ. Обязательно измените:
- Code: Select all
# ========== КОНФИГУРАЦИЯ - ВСЁ ЗДЕСЬ ==========
# Изменяй эти переменные под свою систему!
# Основные пути
RAID_BASE="/srv/vmail/backups/modoboa" # Корень бэкапов на RAID
TEMP_DIR="$RAID_BASE/temp" # Временная папка на RAID
DAILY_DIR="$RAID_BASE/daily" # Ежедневные бэкапы
WEEKLY_DIR="$RAID_BASE/weekly" # Еженедельные
MONTHLY_DIR="$RAID_BASE/monthly" # Ежемесячные
LOG_DIR="$RAID_BASE/logs" # Логи
# Пути к данным Modoboa (измени, если у тебя иначе)
MAIL_DIR="/srv/vmail" # Почтовые ящики
MODOBOA_INSTANCE="/srv/modoboa/instance" # Инстанс Modoboa
DJANGO_SETTINGS="$MODOBOA_INSTANCE/instance/settings.py" # Настройки Django
FRONTEND_DIR="$MODOBOA_INSTANCE/frontend" # Фронтенд
STATIC_DIR="$MODOBOA_INSTANCE/sitestatic" # Статика
MEDIA_DIR="$MODOBOA_INSTANCE/media" # Медиа-файлы
# Пути к конфигам системы
UWSGI_CONFIG="/etc/uwsgi/apps-available/modoboa_instance.ini" # uWSGI
SYSTEMD_SERVICE="/etc/systemd/system/modoboa-uwsgi.service" # systemd
NGINX_CONFIG="/etc/nginx/sites-available/mail.br-1.ru.conf" # Nginx
# База данных
DB_NAME="modoboa" # Имя базы данных
DB_USER="modoboa" # Пользователь БД (не меняй, postgres сам)
# Настройки бэкапа
MIN_FREE_SPACE_GB=50 # Минимум свободных ГБ
NOTIFY_EMAIL="atehdmin@gmail.com" # Email для уведомлений
# Сроки хранения (в днях)
DAILY_KEEP=7 # Хранить daily 7 дней
WEEKLY_KEEP=28 # Хранить weekly 28 дней
MONTHLY_KEEP=90 # Хранить monthly 90 дней
LOG_KEEP=30 # Хранить логи 30 дней
Запуск:
Ручной запуск:
- Code: Select all
sudo /usr/local/bin/backup_modoboa_raid.sh
Добавление в cron (автоматический ежедневный бэкап):
- Code: Select all
sudo crontab -e
Добавьте строку (бэкап каждый день в 2 часа ночи):
- Code: Select all
0 2 * * * /usr/local/bin/backup_modoboa_raid.sh
Структуры бэкапов
После работы скрипта у вас будет:
- Code: Select all
/srv/vmail/backups/modoboa/
├── daily/
│ ├── modoboa_db_20260217_001905.sql.gz
│ ├── modoboa_configs_20260217_001905.tar.gz
│ ├── modoboa_vmail_20260217_001905.tar.gz
│ ├── modoboa_static_media_20260217_001905.tar.gz
│ ├── modoboa_frontend_20260217_001905.tar.gz
│ └── backup_info_20260217_001905.txt
├── weekly/ # Воскресные бэкапы
├── monthly/ # Бэкапы за 1-е число
├── logs/ # Логи за 30 дней
└── temp/ # Временная папка (автоочистка)
Ротация (автоудаление старый логов)
- Daily - через 7 дней
- Weekly - через 28 дней
- Monthly - через 90 дней
- Логи - через 30 дней
Возможные проблемы
Ошибка "No space left on device"
Скрипт сам проверит место и предложит очистку. Если места мало даже после очистки — увеличьте
MIN_FREE_SPACE_GB или добавьте дисков.Не найден PostgreSQL
- Code: Select all
sudo systemctl status postgresql
sudo -u postgres psql -c "\l"
Пути не совпадают
Просто исправьте переменные в блоке КОНФИГУРАЦИЯ в начале скрипта.

