#Что означает ошибка и при каких условиях возникает
При read_only: true контейнер не может записывать файлы в слой корневой файловой системы. Найдите путь записи и предоставьте отдельное временное или постоянное хранилище. Параметр read_only ограничивает запись в корневую файловую систему, но не запрещает запись в отдельно созданные доступные для записи тома или tmpfs. Исправление зависит от того, являются ли данные временными или должны переживать пересоздание.
#Как различить причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Приложение записывает в /tmp или каталог кэшаКонтейнер с read_only: true пытается создать временный файл в корневой файловой системе. | В журнале OSError: [Errno 30] Read-only file system с путём /tmp либо похожим. | Смонтировать tmpfs в подходящий временный каталог. |
| Приложение хранит состояние внутри слоя контейнераБаза или очередь пытается сохранять долговременные файлы в каталоге без volume. | Ошибка записи относится к /app/data, /var/lib/... или каталогу состояния. | Использовать именованный том/объектное хранилище после резервного копирования и миграции. |
| Каталог смонтирован только для чтенияВ Compose у bind mount или volume указан флаг :ro либо read_only: true. | В inspect Mounts виден RW=false у требуемого каталога. | Проверить намеренную политику доступа; изменить только при наличии оснований. |
| Запись идёт не в тот каталогПриложение сконфигурировано на путь внутри образа, а writable volume смонтирован в другое место. | В логах указан путь, отличный от Destination в Mounts. | Изменить путь в приложении или место монтирования после проверки. |
#Пошаговая диагностика и исправление
- Сохраните ошибку и путь, по которому нужна записьВ терминале каталога Compose выполните
docker compose logs --tail=100 app, заменив app именем своего сервиса. Ищите точный путь послеRead-only file systemи имя процесса. Не отключайте read_only целиком в качестве постоянного решения; этот параметр часто нужен для безопасности. Если контейнер хранит базу данных, перед любыми изменениями организуйте согласованную резервную копию средствами БД. - Проверьте действующую конфигурацию и томаОткройте compose.yaml в редакторе; найдите
read_only: true,tmpfs:иvolumes:соответствующей службы. Выполнитеdocker compose config -q— ошибок не должно быть. Далееdocker compose ps -q appдаст идентификатор,docker inspect --format "{{json .Mounts}}" ИДЕНТИФИКАТОРпокажет Destination и RW. Если нужный путь не фигурирует среди Mounts, он остаётся частью корневой системы, защищённой read_only. - Выясните, являются ли данные временнымиЕсли файл нужен только на время жизни контейнера — кэш, сокет, промежуточные данные, — подойдёт tmpfs. Если это база, пользовательские файлы или очередь заданий, tmpfs не подходит: его содержимое исчезнет после остановки контейнера. Обратитесь к документации приложения и уточните точный каталог записи. Не переносите данные с работающей БД простым копированием.
- Добавьте tmpfs для действительно временного путиДля учебного примера из статьи в compose.yaml у службы app добавьте
tmpfs: - /tmpпри сохраненииread_only: true. Временный каталог /tmp станет доступен для записи, хотя остальная корневая файловая система останется только для чтения. Проверьтеdocker compose config -q; при синтаксической ошибке восстановите резервную копию YAML и исправьте отступы. - Для постоянных данных используйте отдельный томЕсли приложение записывает, например, в /app/data, задайте именованный том для этого каталога. Убедитесь, что том не помечен :ro, а процесс внутри контейнера имеет права на запись; ошибки Permission denied и Read-only file system имеют разные причины. Для переноса данных сначала сделайте проверяемую резервную копию и согласуйте процедуру миграции. Не повышайте права контейнера до privileged и не назначайте chmod 777 без необходимости.
- Испытайте изменения в копии проектаСоздайте пустую учебную папку и сохраните предложенный compose.yaml. Откройте терминал в ней, выполните
docker compose up -dиdocker compose logs app. Команда Python создаёт /tmp/probe и печатает ok; контейнер может завершиться после успешной проверки (это нормально для одноразового теста). Если в журнале Errno 30, проверьте, правильно ли вставлены отступы tmpfs и дошла ли изменённая конфигурация до контейнера. - Примените настройки рабочего сервиса только после согласованияЕсли исправление подтверждено на копии, запланируйте пересоздание конкретного сервиса в рабочем окружении.
docker compose up -d --no-deps --force-recreate appизменяет работающий контейнер и вызывает простои, поэтому нельзя выполнять без уведомления и резерва важных данных. При откате верните исходный YAML и предыдущую подтверждённую версию приложения, затем повторно проверьте путь записи. - Проверьте, что корневая система всё ещё защищенаПосмотрите
docker compose config, где у app должен остаться read_only: true. Попробуйте штатную операцию приложения, требующую записи именно в разрешённый путь, и убедитесь в её успешности. Важно не проверять запись произвольными файлами в каталоге базы данных: это может нарушить порядок хранения. Если проблема сохранилась, сравните фактический путь ошибки с Mounts. - Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
#Минимальный пример для отдельного тестового проекта
services:
app:
image: python:3.12-alpine
read_only: true
tmpfs:
- /tmp
command: ["python", "-c", "from pathlib import Path; Path('/tmp/probe').write_text('ok'); print('ok')"]#Как проверить результат и что делать, если проблема осталась
Проблема решена, если штатная запись в выделенный путь успешна, а корневая файловая система остаётся read-only. Если появляется Permission denied, проверяйте владельца и права самого тома; если Errno 30 остаётся, фактический каталог записи, вероятно, не совпадает с точкой tmpfs/volume.