Локальные бэкапы Vaultwarden » История » Версия 1
Redmine Admin, 25.03.2026 10:32
| 1 | 1 | Redmine Admin | <div class="backup-guide"> |
|---|---|---|---|
| 2 | <h2>💾 Вариант 1: Локальные бэкапы с ротацией (базовый)</h2> |
||
| 3 | <p>Автоматизированное резервное копирование данных Vaultwarden с хранением архивов и очисткой устаревших файлов.</p> |
||
| 4 | |||
| 5 | <div class="info-box"> |
||
| 6 | <strong>📋 Что делает скрипт:</strong> |
||
| 7 | <ul> |
||
| 8 | <li>Останавливает контейнер для консистентности БД</li> |
||
| 9 | <li>Создаёт сжатый архив с данными</li> |
||
| 10 | <li>Запускает контейнер обратно</li> |
||
| 11 | <li>Удаляет бэкапы старше заданного периода</li> |
||
| 12 | <li>Ведёт логирование всех операций</li> |
||
| 13 | </ul> |
||
| 14 | </div> |
||
| 15 | |||
| 16 | <h3>1️⃣ Создайте скрипт бэкапа</h3> |
||
| 17 | <pre class="bash"><code>sudo nano /usr/local/bin/vaultwarden-backup.sh</code></pre> |
||
| 18 | |||
| 19 | <p>Вставьте содержимое:</p> |
||
| 20 | <pre class="bash"><code>#!/bin/bash |
||
| 21 | # Vaultwarden Backup Script |
||
| 22 | # Резервное копирование данных Vaultwarden |
||
| 23 | |||
| 24 | set -e # Выход при ошибке |
||
| 25 | |||
| 26 | # === НАСТРОЙКИ === |
||
| 27 | SOURCE_DIR="/root/vaultwarden/data" # Источник данных |
||
| 28 | BACKUP_DIR="/root/vaultwarden/backups" # Куда сохранять бэкапы |
||
| 29 | RETENTION_DAYS=30 # Сколько дней хранить бэкапы |
||
| 30 | TIMESTAMP=$(date +%Y%m%d_%H%M%S) |
||
| 31 | BACKUP_FILE="vaultwarden_backup_${TIMESTAMP}.tar.gz" |
||
| 32 | LOG_FILE="/var/log/vaultwarden-backup.log" |
||
| 33 | |||
| 34 | # Создаем папку для бэкапов если нет |
||
| 35 | mkdir -p "$BACKUP_DIR" |
||
| 36 | |||
| 37 | # Логирование |
||
| 38 | log() { |
||
| 39 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" |
||
| 40 | } |
||
| 41 | |||
| 42 | log "=== Начало бэкапа ===" |
||
| 43 | |||
| 44 | # Останавливаем контейнер для консистентности БД (опционально, но рекомендуется) |
||
| 45 | log "Остановка контейнера vaultwarden..." |
||
| 46 | docker stop vaultwarden 2>/dev/null || true |
||
| 47 | |||
| 48 | # Создаем архив |
||
| 49 | log "Создание архива: $BACKUP_FILE" |
||
| 50 | tar -czf "${BACKUP_DIR}/${BACKUP_FILE}" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" |
||
| 51 | |||
| 52 | # Запускаем контейнер обратно |
||
| 53 | log "Запуск контейнера vaultwarden..." |
||
| 54 | docker start vaultwarden 2>/dev/null || true |
||
| 55 | |||
| 56 | # Удаляем старые бэкапы |
||
| 57 | log "Удаление бэкапов старше $RETENTION_DAYS дней..." |
||
| 58 | find "$BACKUP_DIR" -name "vaultwarden_backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS -delete -print | tee -a "$LOG_FILE" |
||
| 59 | |||
| 60 | # Показываем размер и список бэкапов |
||
| 61 | log "Размер нового бэкапа: $(du -h "${BACKUP_DIR}/${BACKUP_FILE}" | cut -f1)" |
||
| 62 | log "Бэкапы в хранилище:" |
||
| 63 | ls -lh "$BACKUP_DIR"/*.tar.gz 2>/dev/null | tee -a "$LOG_FILE" |
||
| 64 | |||
| 65 | log "=== Бэкап завершен успешно ==="</code></pre> |
||
| 66 | |||
| 67 | <h3>2️⃣ Сделайте скрипт исполняемым</h3> |
||
| 68 | <pre class="bash"><code>sudo chmod +x /usr/local/bin/vaultwarden-backup.sh</code></pre> |
||
| 69 | |||
| 70 | <h3>3️⃣ Протестируйте вручную</h3> |
||
| 71 | <pre class="bash"><code>sudo /usr/local/bin/vaultwarden-backup.sh</code></pre> |
||
| 72 | |||
| 73 | <p>Проверьте, что файл появился:</p> |
||
| 74 | <pre class="bash"><code>ls -lh /root/vaultwarden/backups/</code></pre> |
||
| 75 | |||
| 76 | <div class="success-box"> |
||
| 77 | <strong>✅ Ожидаемый результат:</strong> |
||
| 78 | <pre><code>total 1.2M |
||
| 79 | -rw-r--r-- 1 root root 1.2M Jun 22 03:00 vaultwarden_backup_20260622_030001.tar.gz</code></pre> |
||
| 80 | </div> |
||
| 81 | |||
| 82 | <h3>4️⃣ Настройте автоматизацию через Cron</h3> |
||
| 83 | <p>Откройте crontab root-пользователя:</p> |
||
| 84 | <pre class="bash"><code>sudo crontab -e</code></pre> |
||
| 85 | |||
| 86 | <p>Добавьте строку для ежедневного бэкапа в 3:00 ночи:</p> |
||
| 87 | <pre class="cron"><code>0 3 * * * /usr/local/bin/vaultwarden-backup.sh</code></pre> |
||
| 88 | |||
| 89 | <div class="tip-box"> |
||
| 90 | <strong>💡 Примеры других расписаний:</strong> |
||
| 91 | <ul> |
||
| 92 | <li><code>0 2 * * 0</code> — каждое воскресенье в 2:00</li> |
||
| 93 | <li><code>0 */6 * * *</code> — каждые 6 часов</li> |
||
| 94 | <li><code>30 4 1 * *</code> — 1-го числа каждого месяца в 4:30</li> |
||
| 95 | </ul> |
||
| 96 | </div> |
||
| 97 | |||
| 98 | <h3>❓ Почему с остановкой контейнера?</h3> |
||
| 99 | <div class="warning-box"> |
||
| 100 | <strong>⚠️ Важно:</strong> Vaultwarden использует SQLite. Хотя он устойчив к бэкапам "на лету", |
||
| 101 | остановка контейнера гарантирует <strong>100% консистентность базы данных</strong> и исключает |
||
| 102 | риск повреждения архива при одновременной записи. |
||
| 103 | </div> |
||
| 104 | |||
| 105 | <h3>🔧 Полезные команды для управления бэкапами</h3> |
||
| 106 | <pre class="bash"><code># Просмотр логов бэкапа |
||
| 107 | tail -f /var/log/vaultwarden-backup.log |
||
| 108 | |||
| 109 | # Ручной запуск бэкапа с выводом в реальном времени |
||
| 110 | sudo /usr/local/bin/vaultwarden-backup.sh |
||
| 111 | |||
| 112 | # Проверка статуса cron-задачи |
||
| 113 | sudo systemctl status cron |
||
| 114 | |||
| 115 | # Тестовый прогон cron (сухой запуск) |
||
| 116 | sudo run-parts --test /etc/cron.daily |
||
| 117 | |||
| 118 | # Восстановление из бэкапа (пример) |
||
| 119 | tar -xzf /root/vaultwarden/backups/vaultwarden_backup_20260622_030001.tar.gz -C /root/vaultwarden/</code></pre> |
||
| 120 | |||
| 121 | <h3>🗂️ Структура после настройки</h3> |
||
| 122 | <pre><code>/root/vaultwarden/ |
||
| 123 | ├── data/ # Рабочие данные Vaultwarden |
||
| 124 | ├── backups/ # Папка для архивов |
||
| 125 | │ ├── vaultwarden_backup_20260620_030001.tar.gz |
||
| 126 | │ ├── vaultwarden_backup_20260621_030001.tar.gz |
||
| 127 | │ └── vaultwarden_backup_20260622_030001.tar.gz ← последний |
||
| 128 | ├── docker-compose.yml |
||
| 129 | └── ... |
||
| 130 | |||
| 131 | /var/log/ |
||
| 132 | └── vaultwarden-backup.log # Лог-файл скрипта</code></pre> |
||
| 133 | |||
| 134 | <h3>🔄 Альтернатива: бэкап без остановки контейнера</h3> |
||
| 135 | <p>Если простой сервиса недопустим, можно использовать <code>docker exec</code> для копирования "на лету":</p> |
||
| 136 | <pre class="bash"><code># Заменить секцию остановки/запуска контейнера на: |
||
| 137 | log "Копирование данных без остановки..." |
||
| 138 | docker exec vaultwarden tar -czf - /data > "${BACKUP_DIR}/${BACKUP_FILE}"</code></pre> |
||
| 139 | <p class="small">⚠️ Используйте этот метод только если понимаете риски: при активной записи в БД архив может быть неполным.</p> |
||
| 140 | </div> |
||
| 141 | |||
| 142 | <style> |
||
| 143 | .backup-guide { |
||
| 144 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; |
||
| 145 | line-height: 1.6; |
||
| 146 | color: #2c3e50; |
||
| 147 | max-width: 100%; |
||
| 148 | font-size: 14px; |
||
| 149 | } |
||
| 150 | .backup-guide h2 { |
||
| 151 | color: #1a5276; |
||
| 152 | border-bottom: 3px solid #3498db; |
||
| 153 | padding-bottom: 0.6em; |
||
| 154 | margin: 1.5em 0 1em; |
||
| 155 | font-size: 1.4em; |
||
| 156 | } |
||
| 157 | .backup-guide h3 { |
||
| 158 | color: #2874a6; |
||
| 159 | margin: 1.3em 0 0.7em; |
||
| 160 | background: #ebf5fb; |
||
| 161 | padding: 0.6em 0.9em; |
||
| 162 | border-left: 4px solid #3498db; |
||
| 163 | border-radius: 0 4px 4px 0; |
||
| 164 | font-size: 1.15em; |
||
| 165 | } |
||
| 166 | .backup-guide p { margin: 0.6em 0; } |
||
| 167 | .backup-guide pre { |
||
| 168 | background: #1e1e1e; |
||
| 169 | color: #d4d4d4; |
||
| 170 | padding: 1em; |
||
| 171 | border-radius: 5px; |
||
| 172 | overflow-x: auto; |
||
| 173 | font-size: 0.85em; |
||
| 174 | margin: 0.7em 0; |
||
| 175 | border: 1px solid #3a3a3a; |
||
| 176 | } |
||
| 177 | .backup-guide code { |
||
| 178 | font-family: "Consolas", "Monaco", "Courier New", monospace; |
||
| 179 | color: #ce9178; |
||
| 180 | } |
||
| 181 | .backup-guide pre code { color: #d4d4d4; } |
||
| 182 | .backup-guide pre.bash::before { |
||
| 183 | content: "🐧 Bash"; |
||
| 184 | display: block; |
||
| 185 | background: #4a5568; |
||
| 186 | color: #fff; |
||
| 187 | padding: 0.25em 0.6em; |
||
| 188 | margin: -1em -1em 0.8em -1em; |
||
| 189 | font-size: 0.75em; |
||
| 190 | border-radius: 4px 4px 0 0; |
||
| 191 | font-weight: 600; |
||
| 192 | } |
||
| 193 | .backup-guide pre.cron::before { |
||
| 194 | content: "⏰ Crontab"; |
||
| 195 | display: block; |
||
| 196 | background: #7b68a3; |
||
| 197 | color: #fff; |
||
| 198 | padding: 0.25em 0.6em; |
||
| 199 | margin: -1em -1em 0.8em -1em; |
||
| 200 | font-size: 0.75em; |
||
| 201 | border-radius: 4px 4px 0 0; |
||
| 202 | font-weight: 600; |
||
| 203 | } |
||
| 204 | .backup-guide ul { |
||
| 205 | padding-left: 1.6em; |
||
| 206 | margin: 0.5em 0; |
||
| 207 | } |
||
| 208 | .backup-guide li { margin: 0.35em 0; } |
||
| 209 | .backup-guide .info-box { |
||
| 210 | background: #e8f4fd; |
||
| 211 | border: 1px solid #b6d4fe; |
||
| 212 | border-left: 4px solid #3498db; |
||
| 213 | padding: 1em 1.2em; |
||
| 214 | margin: 1em 0; |
||
| 215 | border-radius: 0 5px 5px 0; |
||
| 216 | } |
||
| 217 | .backup-guide .success-box { |
||
| 218 | background: #e6f7ee; |
||
| 219 | border: 1px solid #b7e4c7; |
||
| 220 | border-left: 4px solid #27ae60; |
||
| 221 | padding: 1em 1.2em; |
||
| 222 | margin: 1em 0; |
||
| 223 | border-radius: 0 5px 5px 0; |
||
| 224 | } |
||
| 225 | .backup-guide .warning-box { |
||
| 226 | background: #fff3cd; |
||
| 227 | border: 1px solid #ffeaa7; |
||
| 228 | border-left: 4px solid #f39c12; |
||
| 229 | padding: 1em 1.2em; |
||
| 230 | margin: 1em 0; |
||
| 231 | border-radius: 0 5px 5px 0; |
||
| 232 | } |
||
| 233 | .backup-guide .tip-box { |
||
| 234 | background: #f0f7ff; |
||
| 235 | border: 1px solid #cfe2ff; |
||
| 236 | border-left: 4px solid #17a2b8; |
||
| 237 | padding: 1em 1.2em; |
||
| 238 | margin: 1em 0; |
||
| 239 | border-radius: 0 5px 5px 0; |
||
| 240 | } |
||
| 241 | .backup-guide .small { |
||
| 242 | font-size: 0.9em; |
||
| 243 | color: #666; |
||
| 244 | font-style: italic; |
||
| 245 | } |
||
| 246 | .backup-guide a { |
||
| 247 | color: #2980b9; |
||
| 248 | text-decoration: none; |
||
| 249 | } |
||
| 250 | .backup-guide a:hover { text-decoration: underline; } |
||
| 251 | @media (max-width: 768px) { |
||
| 252 | .backup-guide { font-size: 13px; } |
||
| 253 | .backup-guide pre { font-size: 0.8em; } |
||
| 254 | } |
||
| 255 | </style> |