Скрипт бекапа Modoboa

Board index Администрирование Программное обеспечение Modoboa

Description: Почтовый сервер на базе Modoboa

#1by mexan » 17.02.2026, 08:47

Всем привет! После долгих мучений с настройкой Modoboa я написал скрипт, который делает полный бэкап всего сервера и показывает красивый прогресс.

Что бэкапит скрипт:
  • 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"

Пути не совпадают
Просто исправьте переменные в блоке КОНФИГУРАЦИЯ в начале скрипта.
Image
mexan
Администратор
Reputation: 0
Posts: 174
Topics: 133

#2by mexan » 17.02.2026, 09:12

А вообще советую не использовать данный почтовый сервер... :(. Он глюенный и косячный.
Image
mexan
Администратор
Reputation: 0
Posts: 174
Topics: 133


Return to Modoboa