Сообщество / Docker

Обсуждение — Docker Volumes и бэкап данных контейнеров

Источник:
Docker Volumes и бэкап данных на VPS

Контейнер удаляется — данные в нём исчезают. Чтобы база и файлы пережили пересоздание, нужны тома. Разберём, как их правильно использовать и как делать бэкапы, которые реально восстанавливаются. Данные внутри контейнера эфемерны: docker rm — и всё стёрто. Docker предлагает три способа сохранить данные снаружи:

Подробнее →
tema_py
Junior
Сообщения: 25
Репутация: 11
Дата регистрации:
18.08.2026
АВТОР ТЕМЫ02 сент. 2026 г., 22:10 (GMT+3)
скинул как в статье на своего бота — не бьёт.

сетап простой: python-telegram-bot, вебхук, sqlite с историей, всё в одном compose. том прописал, контейнер снёс и поднял заново. бот живой, вебхук проходит, а история чатов как с нуля. сначала грешил на бэкап тома.

полчаса тыкался, потом полез внутрь. volume bot_data на месте, а база лежала в /app/bot.db, я монтировал /app/data. том красивый и пустой, бэкап снимал пустоту и её же восстанавливал. я молодец, полтора часа конфиг крутил))


services:
bot:
image: bot:local
volumes:
- bot_data:/app/data
environment:
BOT_TOKEN: ${BOT_TOKEN}
DB_PATH: /app/bot.db
volumes:
bot_data:


DB_PATH я так и не поправил под том. сейчас в env /app/data/bot.db, в томе хоть что-то лежит. у кого бот с sqlite в докере — named volume держите или bind-mount на хост, чтоб файл глазами видеть?
0
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
02 сент. 2026 г., 23:01 (GMT+3)

tema_py писал:
том красивый и пустой


Короче, путь мимо тома — классика. Для sqlite я bind на хост: файл виден, бэкап не пустота.
0
tema_py
Junior
Сообщения: 25
Репутация: 11
Дата регистрации:
18.08.2026
02 сент. 2026 г., 23:18 (GMT+3)
masha_spb bind после этой пустоты я тоже воткнул, ещё раз бэкапить красивый том без файла желания ноль))

masha_spb писал:
Для sqlite я bind на хост: файл виден, бэкап не пустота.


тут не поспоришь. named volume сам по себе нормальный, если путь совпадает. у меня не совпал: том на /app/data, база жила в /app/bot.db, DB_PATH в env «потом поправлю» и не поправил. контейнер живой, вебхук проходит, история как с нуля. бэкапил том исправно, восстанавливал исправно, на выходе снова пустота. я молодец, полтора часа конфиг крутил и ещё полчаса искал сломанный бэкап, хотя ломал я сам.

сейчас каталог ./data на хосте, в контейнере /app/data, в env уже /app/data/bot.db, не /app/bot.db.


services:
bot:
image: bot:local
restart: unless-stopped
volumes:
- ./data:/app/data
environment:
BOT_TOKEN: ${BOT_TOKEN}
DB_PATH: /app/data/bot.db
OLLAMA_URL: http://127.0.0.1:11434
ALLOW_IDS: ${ALLOW_IDS}

ls ./data/bot.db — и сразу видно, есть там что-то или снова пусто. это и спасло нервы после первого круга. ещё плюс bind: sqlite3 на хосте видит файл, не надо docker exec. с named volume я бы лез в контейнер каждый раз, а sqlite3 в образе бота я даже не ставил, там только python.

вторая грабля за вечер: контейнер пишет не от root, папку ./data я создал с хоста своим юзером, sqlite сразу permission denied. chown на ./data под uid из контейнера, отпустило. без этого bind тоже выглядит как «том на месте, бот живой, база не пишется», только ошибка уже в логах, не в пустой папке.

бэкап теперь не docker volume, а файл. тупо cp ./data/bot.db пока бот пишет — я так сначала и сделал. у sqlite есть wal и shm. скопировал один bot.db, поднял копию на тесте — история обрезанная, последних сообщений нет. копировать три файла руками тоже пробовал, один раз wal не доехал, тестовая база сказала malformed.

живой бот стопать не хочу, вебхук тогда молчит и телега орёт в логах. снимаю слепок через сам sqlite:


#!/bin/bash
set -euo pipefail
SRC=/home/artem/bots/tg/data/bot.db
DST=/home/artem/bots/tg/backups
mkdir -p "$DST"
STAMP=$(date +%Y%m%d_%H%M)
sqlite3 "$SRC" ".backup '$DST/bot_$STAMP.db'"
find "$DST" -name 'bot_*.db' -mtime +14 -delete

.backup внутри sqlite консистентный, даже если бот в этот момент пишет. литстрим не трогал, для истории чатов и так хватает. копии лежат рядом с проектом, не внутри ./data. том сгорел — бэкапы живые. бэкапы сгорели — рабочий файл ещё крутится. хранить слепок там же, откуда снимаешь, я уже проверял, проверять больше не буду.

крон на хосте, не в контейнере. в контейнере cron я год назад поднимал, потом забыл что он там есть, и удивлялся откуда нагрузка.


15 3 * * * /home/artem/bots/tg/backup.sh >> /home/artem/bots/tg/backup.log 2>&1

проверку восстановления делал вечером: контейнер снёс, ./data оставил, compose up — история на месте, вебхук не перевыпускал. потом отдельно взял вчерашний слепок, подменил bot.db на тесте — чаты тоже на месте. это уже не «том красивый и пустой».

из гайда путь тома как пример ок, бьёт уже мой env, который я не довёл до этого пути. кто бэкапит named volume и не лезет ls внутрь контейнера — гляньте глазами, что файл реально в этом каталоге. docker volume ls всегда зелёный, это ничего не значит.

bind vs named: для одной sqlite, которую надо тыкать и копировать, bind. если завтра два сервиса и общая папка — named верну, но с тем же sqlite3 .backup, не rsync по живому файлу и не «надеюсь путь совпал».

у кого бот с sqlite в докере — как снимаете, пока контейнер живой? .backup, литстрим, или стопаете на минуту? я стопать не хочу, вебхук тогда 404 ловит.
1
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
03 сент. 2026 г., 00:02 (GMT+3)

tema_py писал:
named volume сам по себе нормальный, если путь совпадает


tema_py путь — да, без этого том красивый и пустой, как у тебя. Для sqlite я всё равно bind: wal и shm рядом с bot.db, бэкап три файла с хоста, sqlite3 без docker exec. Если совсем просто — named volume снова прячет базу, и полчаса ищешь, куда она делась.
0
kirill_nsk
Junior
Сообщения: 54
Репутация: 15
Дата регистрации:
26.08.2026
03 сент. 2026 г., 07:01 (GMT+3)
masha_spb bind это просто папка на сервере?
я том и bind путаю пока.
а можно попроще?)
0
daniyar_ala
Junior
Сообщения: 21
Репутация: 4
Дата регистрации:
24.08.2026
03 сент. 2026 г., 08:17 (GMT+3)

kirill_nsk писал:
bind это просто папка на сервере?


да, по сути так. в compose слева папка на сервере, справа путь внутри контейнера — ./data:/app/data. файлы лежат у тебя в ./data, ls сразу показывает bot.db. sqlite3 на хосте открывает без docker exec, бэкап — просто скопировал каталог.

named volume docker хранит сам. имя видишь в docker volume ls, а файлы на хосте глазами почти не трогаешь. том может быть «на месте», контейнер живой, а база лежит мимо. у tema_py как раз это: монтировал /app/data, а DB_PATH смотрел в /app/bot.db. бэкапил красивую пустоту.

для sqlite я с masha_spb согласен: bind спокойнее. wal и shm рядом с базой, три файла с хоста, ничего не прячется. named volume нормальный, если путь в env и маунт совпадают. иначе история чатов как с нуля.

я сам сначала named брал, пока на тестовом боте не поймал пустой том и полчаса винил бэкап. сейчас bind и в контейнер каждый раз не лезу )

kirill_nsk если сетап пока простой — bind и не думай про тома. у тебя sqlite в одном контейнере, или уже postgres рядом?
0
andryukha
Junior
Сообщения: 40
Репутация: 15
Дата регистрации:
17.08.2026
03 сент. 2026 г., 09:21 (GMT+3)
daniyar_ala postgres «рядом» — это уже не bind и не том, это смета. sqlite на боте копейки, база рядом сразу про память и другую сумму. окей, а сколько в месяц за эту рядом, если не махать рукой? я утром сел прикинуть и сам себе позавидовал, что sqlite. калькулятор у меня уже рефлекс))
0
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
03 сент. 2026 г., 19:43 (GMT+3)

andryukha писал:
сколько в месяц за эту рядом, если не махать рукой?


не в месяц. в другую задачу. tema_py, у тебя в compose бот, sqlite и ссылка на ollama — это не «postgres рядом». рядом начинается, когда sqlite уже не справляется: несколько писателей сразу, роли, отчёты, не файл с историей чатов.

вечером после этой ветки полезла в свой compose, не в бота, в open webui. сервис живой, том на месте, а я вдруг не вспомнила, куда он вообще пишет диалоги. полезла руками: чаты лежат своей sqlite внутри данных вебui, модели — вообще в другом томе. я их когда-то сунула в один архив «на всякий случай». архив вышел нелепый, восстанавливать не хотелось даже смотреть. с тех пор историю и веса не мешаю. модели проще скачать заново, чем таскать в каждый ночной бэкап. диалоги — наоборот, их заново не нагуглишь.

andryukha, калькулятор у тебя честный, просто столбец не тот. sqlite на боте — это не экономия на тарифе, это отсутствие второй службы. postgres в том же compose сразу просит свой том под PGDATA, своего пользователя и свой dump. и вот здесь уже не bind против named. я один раз сняла «бэкап тома» у postgres, пока он писал. файлы на месте, красиво лежат, postgres потом отказывается считать их своими. dump — да. каталог данных на горячую, как bot.db с хоста — лотерея. это другой ритуал, не апгрейд папки.

если совсем просто: пока ls ./data/bot.db показывает живой файл и бот не орёт на блокировки — живите так. «взрослая база» из гайдов подождёт момента, когда файл реально упрётся. я чуть не поставила postgres под чаты вебui, потому что в одном тексте production-ready начиналось с него. чатов было сорок штук. стыдно, но полезно.)

короче, раз ветка про тома и бэкап, а не про вкус баз.

sqlite у бота — копируете ./data, лучше когда контейнер секунду не пишет. я перед копированием останавливаю, копирую, поднимаю. на телеграм-боте за эти секунды никто не умрёт. даже на горячую чаще отъедет последний чат, не вся история, но этот фокус я на себе больше не проверяю.

если когда-нибудь таки postgres — не надейтесь, что docker volume backup равен дампу. том можно снимать, но сначала pg_dump, и уже dump кладёте туда, где его видно глазами. иначе через полгода восстанавливаете каталог, который база не узнаёт. звучит скучно, да. после пустого тома скучное как раз и надо.

ollama в этот же архив не мешайте. отдельный том или каталог. история чатов и веса модели в одном tar — это я уже проходила, второй раз не хочу.

про «сколько в месяц» вы платите за память под модель, не под sqlite. если на эту же машину поставить postgres «на вырост», он начнёт жевать то, что вы отдавали ollama. на восьми гигах это уже не копейки рядом, это выбор: или 14b дышит нормально, или база сидит в запасе, а модель через раз. для бота с историей я бы этот выбор даже не открывала.

tema_py, твой ./data:/app/data и DB_PATH внутрь — этого достаточно. не чини то, что после пустого тома наконец видно глазами. а «база рядом» пусть подождёт живого упора sqlite, не скучающего калькулятора.

у кого-то бот уже переезжал со sqlite на postgres не из принципа, а потому что файл деградировал? интересно живое, не «так правильнее в двадцать шестом».
0
tema_py
Junior
Сообщения: 25
Репутация: 11
Дата регистрации:
18.08.2026
03 сент. 2026 г., 20:03 (GMT+3)
masha_spb ну да, postgres я туда не собирался тащить)) sqlite на чаты, олама на хосте.

после твоего архива полез в свой tar. модели туда же влезли, файл распух, открывать не стал. теперь только ./data бэкаплю, веса пусть качаются. у кого ещё модели в общий архив уезжали?
1
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
03 сент. 2026 г., 20:12 (GMT+3)

tema_py писал:
модели туда же влезли, файл распух, открывать не стал


вечером глянула du -h на свой tar в /backup — до сих пор стыдно. ollama на хосте как раз спасает: веса сидят в ~/.ollama, в ./data их нет. короче, модели я из ночного архива выкинула, проще pull, чем таскать гигабайты.
1
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
03 сент. 2026 г., 21:24 (GMT+3)
daniyar_ala, ты bind kirill_nsk нормально разложил, тут без спора.

tema_py писал:
теперь только ./data бэкаплю, веса пусть качаются


короче, индекс для поиска я из тома вечером вычистила. du на open webui показал, что он раздулся почти как модели. пересобрать дешевле, чем таскать в tar. в архиве пусть сидят диалоги, не кэш.
0
tema_py
Junior
Сообщения: 25
Репутация: 11
Дата регистрации:
18.08.2026
03 сент. 2026 г., 21:28 (GMT+3)
daniyar_ala ну это прям я, да)) ты нормально разложил, я уже не отпираюсь.

daniyar_ala писал:
монтировал /app/data, а DB_PATH смотрел в /app/bot.db


сижу вечером с ноутом, открыл compose — проверяю что там сейчас, а то после твоего поста стало стыдно. ща скину как есть:


volumes:
- ./data:/app/data
environment:
DB_PATH: /app/data/bot.db


named на этом боте больше не кручу. в теории если путь совпал — том живой и база на месте, на практике я себе не верю. один раз уже бэкапил красивый том, а база писалась мимо. я ещё минут двадцать винил скрипт бэкапа, пока ls не ткнул носом 🤦

у кого env и маунт до сих пор в разные стороны смотрят — сколько времени ушло, пока заметили?
1
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
03 сент. 2026 г., 22:23 (GMT+3)

tema_py писал:
у кого env и маунт до сих пор в разные стороны смотрят


у меня как-то compose был честный, а в .env рядом лежал старый DB_PATH на /app/bot.db. контейнер взял его, я полвечера смотрела в ./data.

daniyar_ala, bind тут не спасёт, если dotenv перебивает путь. ls по ./data тогда молчит честно, просто база пишет мимо.
2
tema_py
Junior
Сообщения: 25
Репутация: 11
Дата регистрации:
18.08.2026
03 сент. 2026 г., 22:27 (GMT+3)
вечером ssh-нулся, compose даже не открывал — сразу cat .env. daniyar_ala, том я уже не трогаю, тут другое.

masha_spb писал:
compose был честный, а в .env рядом лежал старый DB_PATH


у меня в yaml стояло DB_PATH: ${DB_PATH}, я себя умным считал. compose подставил из файла, а там /app/bot.db с прошлой жизни. контейнер живой, ./data на хосте лежит, а бот пишет мимо. я минут пятнадцать смотрел sqlite3 ./data/bot.db и не понимал, почему чаты за сегодня пустые ))

ща скину как оставил, без подстановок:


env_file:
- .env
environment:
DB_PATH: /app/data/bot.db


из .env путь вычистил. sqlite3 ./data/bot.db сейчас вечерние чаты видит. у кого yaml зелёный, а переменная всё равно едет из файла — сколько времени ушло, пока догнало?
0
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
03 сент. 2026 г., 23:16 (GMT+3)

tema_py писал:
sqlite3 ./data/bot.db сейчас вечерние чаты видит


путь совпал — это уже не «том живой, база мимо». на сегодня хватит этой победы.

daniyar_ala, я не к yaml бота сейчас. вечером, после вашей ветки, полезла не в .env — это уже было — а в свой ночной скрипт бэкапа тома. open webui, named volume, контейнер живой, я на диване с ноутом. статья как раз про volumes, я по ней когда-то и слепила helper: alpine, тот же том, tar в /backup. скрипт зелёный, архив весит сколько надо, я собой довольная ушла чай пить.

уже поздно, из чистого недоверия к себе распаковала один такой tar в /tmp. sqlite3 webui.db — открывается, чаты на месте. последних трёх дней нет. в самом вебui они есть, я только что в вкладке смотрела.

если совсем просто: пока контейнер пишет, рядом с webui.db лежат wal и shm. копируешь один db — получаешь файл, который sqlite честно открывает и честно врёт. postgres в такой ситуации хотя бы орёт при старте, это противно, но понятно. sqlite улыбается. я минут сорок сидела: интерфейс полный, копия как будто я в отпуск уехала. пока не сделала docker exec и ls по каталогу данных — и не увидела этот -wal, размером не шуточным.

на bind это хотя бы торчит на хосте. в ./data видно и mtime, и кучку хвостов. named volume без exec — чёрный ящик: tar красивый, внутри дыра. bind путь сам по себе не лечит, ты это уже разложил, но глаза на файлах он всё-таки даёт. я себе named оставила: вебui не бот, и не хочу, чтобы ./data на хосте кто-нибудь задел при деплое. зато больше не притворяюсь, что «бэкап тома» равен «бэкап базы».

как живу с этим сейчас, без геройства. ночной скрипт больше не делает голый tar по тому, пока контейнер пишет. сначала exec:

sqlite3 /app/backend/data/webui.db '.backup /app/backend/data/webui-snap.db'


это нормальный снимок, не копия с полузаписи. потом уже alpine tar только этот snap. живой webui.db руками не трогаю, wal не коллекционирую.

если лень в exec — останавливаю контейнер на минуту, tar всего тома, поднимаю. для домашнего вечера минута без вебui не смерть. бота, который пишет чаты, я бы так наобум не гасила: лучше .backup на горячую, чем docker stop посреди записи.

ещё споткнулась на ровном. один раз snap сняла, а рабочий каталог вебui смотрел чуть в сторону из‑за старой переменной. snap вышел пустой и гордый. рифма к твоему /app/data против /app/bot.db, только не в боте, а в «я же бэкаплю правильный volume». ls внутри контейнера перед архивом теперь обязательный, иначе снова архив надежды.

короче, volume backup из гайда работает, когда данные лежат. когда их пишут — это архив красивого файла. postgres — dump, sqlite — .backup, остальное — tar после stop. я себе это вечером на лбу написала, потому что третий раз одна и та же грабля, только сервис другой.

у кого named volume и sqlite внутри — wal в tar находили, или тоже сначала гордились размером архива?
2
kirill_nsk
Junior
Сообщения: 54
Репутация: 15
Дата регистрации:
26.08.2026
04 сент. 2026 г., 06:02 (GMT+3)
daniyar_ala спасибо) bind я как ты сказал вроде сделал.
это теперь просто папку копировать? или я снова мимо?
0
pavel_63
Junior
Сообщения: 32
Репутация: 20
Дата регистрации:
13.08.2026
04 сент. 2026 г., 07:04 (GMT+3)
kirill_nsk не мимо. Папку копировать — да, в этом и смысл бинда. Не том внутри докера, который потом хрен найдёшь, а каталог у тебя на диске. Как у меня папка с базами 1С: открыл проводник, скопировал на другой диск, и всё.

Только не копируй, пока бот пишет. Приехал как-то бухгалтер, просит «на всякий случай» снять копию, сама в базе сидит. Файл открылся, ошибок нет, вчерашний день на месте, сегодняшний — дыра. Она ещё полчаса меня убеждала, что ничего не удаляла.

Службу остановил — скопировал — запустил. Тогда копия честная. Пока процесс живой, файл откроется без ошибки и без последних часов. Это не докер придумал, виндовый шаринг баз так же врёт.

У тебя бот ночью молчит или крутится круглые сутки?
1
lexa92
Junior
Сообщения: 59
Репутация: 20
Дата регистрации:
09.08.2026
04 сент. 2026 г., 08:02 (GMT+3)
pavel_63 с бухгалтером история ровно про это, только kirill_nsk не базу 1С снимает, а sqlite. проводник файл откроет. чатов за утро в копии не будет.

pavel_63 писал:
Пока процесс живой, файл откроется без ошибки и без последних часов


у бота то же плюс -wal и -shm рядом с bot.db. снимешь ./data с хоста пока контейнер пишет — tar красивый, sqlite3 открывается, кусок дня пустой. на прошлой неделе сам так влетел: размер нормальный, я уже отвлёкся, diff с живой базой показал, что трёх часов просто нет.

не копируй живое. сначала стоп:

docker stop имя_бота
cp -a ./data /backup/data-$(date +%F)
docker start имя_бота

если без пауз крутится — не cp с хоста, а снимок изнутри:

docker exec имя_бота sqlite3 /app/data/bot.db '.backup /tmp/bot-ok.db'

этот файл уже честный, его и тащи. wal в архив сам не придёт.

kirill_nsk скинь `ls -l ./data`. без вывода не гадаю, wal там есть или ты снова смотришь в пустую папку?
0
masha_spb
Junior
Сообщения: 34
Репутация: 16
Дата регистрации:
11.08.2026
04 сент. 2026 г., 19:06 (GMT+3)
tema_py, пока ты yaml без подстановок дописывал, я вечером своего open webui трогала.

named volume уже не хвалю, это было. новое — перевела data на bind, как у тебя ./data. не из любви к чистоте: хотела, чтобы wal лежал на диске, а не в томе, куда я глазами не хожу.

сейчас ls ./webui-data — webui.db, -wal и -shm рядом. вчерашнего «архив открывается, трёх дней нет» с таким ls уже не будет, я хотя бы вижу, что копирую живое.

короче, bind путь не чинит и горячий бэкап не чинит. он только перестаёт врать пустой папкой. дальше либо стоп, либо снимок из sqlite. я себе стоп не делаю — вкладка открыта, я в ней же сижу.

я ещё минут пять смотрела на этот -wal и думала, какой я молодец что том переименовала. размер чуть меньше самой базы — неделю себе красиво паковала воздух)

ты ./data целиком в архив кладёшь, пока бот живой, или уже стопаешь?
0
andryukha
Junior
Сообщения: 40
Репутация: 15
Дата регистрации:
17.08.2026
05 сент. 2026 г., 11:37 (GMT+3)
masha_spb, утром на кухне с чаем, тред с вечера висел. дочитал про webui и этот tar.

masha_spb писал:
размер чуть меньше самой базы — неделю себе красиво паковала воздух)


окей, а сколько в цифрах? живой каталог и вчерашний архив. без двух чисел «весит сколько надо» — снова витрина, а не смета.

на хечнере я такие «полные» бэкапы держал три штуки и радовался гигабайтам в панели. пока один не распаковал: даты дырявые, а сверх тарифа уже сняли. закрывающие из подольска ещё тот квест. поэтому и спрашиваю не «правильно ли стопать», а что дешевле — пять минут простоя или неделя вот этих tar. ты вкладку не закрываешь, это понятно. я про строку в экселе, сам знаю что всех задолбал))

у кого размер копии и живого каталога хоть раз совпал — киньте. без этого опять будем спорить картинками.
0