MAATRIX / Блог / Baserow на сервере: частые ошибки и решения

Baserow на сервере: частые ошибки и решения

MAATRIX

Baserow — open-source альтернатива Airtable: своя база данных, готовое REST API и веб-интерфейс из коробки, без подписки за каждого пользователя. На бумаге разворачивается одной командой docker compose up -d, но на практике на VPS почти всегда спотыкается об одну и ту же пятёрку проблем — контейнер уходит в рестарт-луп, backend не видит базу, домен отдаёт 502, файлы не грузятся или бэкап оказывается нерабочим в момент, когда он нужен. Разберём каждую по шагам, с конкретными командами.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Контейнер падает в рестарт-луп после `docker compose up`

Первый признак — docker compose ps показывает статус Restarting вместо Up, а docker compose logs baserow крутит один и тот же traceback по кругу. Причины почти всегда сводятся к трём вещам.

Не хватает памяти. Baserow — это не один процесс, а связка Django-backend, Celery-воркеров, Redis и (в all-in-one образе) встроенного PostgreSQL и Caddy. На VPS с 1 ГБ RAM это упирается в OOM ещё на старте: ядро убивает процесс, контейнер перезапускается, и цикл повторяется. Проверяется одной командой:

dmesg -T | grep -i "out of memory" | tail -5

Если видите записи рядом со временем падения — дело в лимите. Для комфортной работы с несколькими пользователями закладывайте от 2 ГБ RAM, а лучше 4 ГБ, если база разрастётся до десятков тысяч строк с вложениями.

Конфликт портов. Официальный all-in-one образ baserow/baserow слушает 80 и 443 (внутренний Caddy) либо порт из BASEROW_CADDY_ADDRESSES. Если на сервере уже висит nginx на этих портах, контейнер падает с bind: address already in use. Решение — непубличный порт и свой reverse-proxy поверх:

services:
  baserow:
    image: baserow/baserow:1.30.1
    environment:
      - BASEROW_PUBLIC_URL=https://baserow.example.com
      - BASEROW_CADDY_ADDRESSES=:8080
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - baserow_data:/baserow/data
    restart: unless-stopped
volumes:
  baserow_data:

Повреждённый volume. Если сервер выключался аварийно (OOM killer, ребут по питанию), файлы в /baserow/data могут остаться в неконсистентном состоянии — чаще всего страдает встроенный Postgres внутри тома, в логах будет PANIC: could not locate a valid checkpoint record. При наличии свежего бэкапа проще пересоздать том и восстановиться из дампа, чем чинить повреждённый data-каталог вручную.

Backend не подключается к PostgreSQL: `OperationalError` и `connection refused`

Если вы разворачиваете Baserow не как all-in-one образ, а классическим docker-compose с отдельными сервисами (backend, web-frontend, celery, db, redis — такой подход удобнее, когда база уже используется другими сервисами или нужна репликация), самая частая ошибка при первом запуске:

django.db.utils.OperationalError: could not connect to server: Connection refused
	Is the server running on host "db" and accepting TCP/IP connections on port 5432?

Разбирайте по порядку:

  1. Сервис db действительно поднялся. docker compose ps db — если статус не healthy, смотрите логи: docker compose logs db. Часто причина в том, что POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB в сервисе db не совпадают с DATABASE_USER/DATABASE_PASSWORD/DATABASE_NAME в сервисе backend — это два независимых блока env, легко рассинхронизировать при копировании чужого docker-compose.yml.
  2. Backend стартует раньше базы. depends_on без condition: service_healthy гарантирует лишь то, что контейнер запущен, а не что Postgres уже принимает соединения. Добавьте healthcheck:
db:
  image: postgres:16
  environment:
    - POSTGRES_USER=baserow
    - POSTGRES_PASSWORD=change_me
    - POSTGRES_DB=baserow
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U baserow"]
    interval: 5s
    retries: 10

backend:
  image: baserow/backend:1.30.1
  depends_on:
    db:
      condition: service_healthy
  environment:
    - DATABASE_HOST=db
    - DATABASE_NAME=baserow
    - DATABASE_USER=baserow
    - DATABASE_PASSWORD=change_me
  1. База и backend в разных docker-сетях. Если Postgres поднят отдельным compose-файлом, а Baserow — своим, имя хоста db просто не резолвится. Создайте общую внешнюю сеть (docker network create shared_net, подключите оба стека) либо укажите в DATABASE_HOST реальный IP контейнера с базой.

Если база настроена по гайду по установке PostgreSQL на VPS, проверьте ещё pg_hba.conf — Postgres часто слушает только localhost, и если backend работает не в docker, а как systemd-сервис на том же сервере, подключение упадёт, пока не поправите listen_addresses и правило в pg_hba.conf.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

502 Bad Gateway при открытии домена через reverse-proxy

Baserow сам по себе не отдаёт TLS, если вы вынесли его за отдельный nginx или Caddy (а не используете встроенный Caddy из all-in-one образа). Типичная ошибка — домен открывается, но отдаёт 502, хотя curl http://127.0.0.1:8080 изнутри сервера работает нормально.

Три причины встречаются чаще всего:

  • BASEROW_PUBLIC_URL не совпадает с реальным доменом. Переменная критична — Baserow использует её и для генерации ссылок в письмах, и для проверки Origin/CSRF на API-запросах. Если в переменных окружения стоит http://localhost или старый домен, фронтенд стучится не туда. Проверьте фактическое значение: docker compose exec backend env | grep BASEROW_PUBLIC_URL.
  • Nginx проксирует раньше, чем контейнер готов. При старте all-in-one образа внутренние сервисы (Postgres, миграции, Caddy) поднимаются последовательно, и на это уходит от 30 секунд до пары минут при первом запуске — reverse-proxy в этот момент получит 502. Проверьте docker compose logs -f baserow до появления строки о готовности сервера.
  • Неверный proxy_pass при работе через WebSocket. Baserow использует WebSocket-соединения для realtime-обновлений в таблицах (кто сейчас редактирует ячейку). Без заголовков апгрейда соединения realtime будет рваться, а иногда и вся страница зависать при загрузке. Минимальный рабочий блок для nginx:
server {
    listen 443 ssl;
    server_name baserow.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Если вы предпочитаете Caddy вместо связки nginx + certbot, апгрейд соединений он делает по умолчанию — см. гайд по установке Caddy с авто-SSL на VPS, конфиг там короче.

Файлы во вложениях не загружаются или не открываются

Отдельная категория ошибок — таблица создаётся, строки добавляются, но поле "Файл" либо не даёт загрузить вложение (спиннер и таймаут), либо файл загружается, а потом отдаёт 404 при открытии.

Причина 1 — лимит на тело запроса у reverse-proxy. Nginx по умолчанию режет запросы больше 1 МБ. Для вложений поднимите лимит явно:

client_max_body_size 100m;

Причина 2 — том с медиа потерялся. Без внешнего S3-совместимого хранилища (AWS_ACCESS_KEY_ID, AWS_STORAGE_BUCKET_NAME) файлы лежат внутри docker-тома, в /baserow/data/media. Проверьте, что он смонтирован:

docker compose exec baserow ls -la /baserow/data/media/user_files | head

Если каталог пуст, а в базе есть записи о файлах — том был подменён (например, docker compose up запустили после смены имени проекта, и Docker создал новый volume вместо старого). Называйте volume в compose-файле явно, не полагайтесь на автогенерируемое имя.

Причина 3 — права на каталог. Если /baserow/data смонтирован как bind mount, права владельца могут не совпадать с UID процесса внутри контейнера (docker compose exec baserow id). При расхождении — chown -R <uid>:<gid> /path/to/baserow/data на хосте.

Импорт больших таблиц из CSV зависает или падает по таймауту

Импорт CSV на 50–100 тысяч строк через веб-интерфейс — частый сценарий переезда с Airtable или Excel, и именно на нём Baserow чаще всего спотыкается на слабом VPS.

Что происходит: импорт идёт через Celery-воркер асинхронно, но если у вас всего один воркер с дефолтными настройками, большой файл забивает очередь, и запрос на фронтенде получает таймаут от reverse-proxy раньше, чем воркер закончит обработку — хотя импорт на бэкенде продолжает идти. Пользователь видит ошибку, но данные в итоге либо появляются, либо нет — если воркер тоже упал по памяти.

Практические шаги:

  • Увеличьте proxy_read_timeout минимум до 300 секунд для путей /api/, чтобы фронтенд не рвал соединение раньше времени.
  • Проверьте, что Celery-воркер вообще запущен (в разбитом compose — отдельный сервис celery), и посмотрите логи на предмет MemoryError или Killed во время импорта.
  • Для файлов на десятки мегабайт разумнее разбить CSV на части по 10–20 тысяч строк — это снижает пиковое потребление памяти воркером.
  • Для регулярной синхронизации из внешней системы удобнее не использовать веб-импорт вообще, а писать данные напрямую через REST API Baserow — так вы контролируете батчи и получаете внятные коды ошибок.

Бэкап, восстановление и обновление без потери данных

Baserow хранит данные в двух местах: PostgreSQL (структура таблиц, значения ячеек, права доступа) и файловое хранилище (вложения). Бэкапить нужно оба, и раздельно.

Для встроенного Postgres в all-in-one образе:

docker compose exec baserow su - postgres -c "pg_dump baserow" > baserow_$(date +%F).sql
docker compose exec baserow tar -czf - -C /baserow/data/media . > baserow_media_$(date +%F).tar.gz

Для разбитого compose с отдельным сервисом db — обычный pg_dump из контейнера базы, плюс архив тома с медиа (если возникают проблемы с самим подключением к базе, см. разбор ошибок PostgreSQL с отказом в соединении). Не полагайтесь на снапшот диска у провайдера как единственный бэкап — на живой базе он может зафиксировать несогласованное состояние, если не остановить запись или не использовать pg_dump/WAL-архивирование.

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

Обновление версии — отдельный источник проблем: Baserow при старте новой версии образа автоматически прогоняет Django-миграции, и если между вашей текущей и целевой версией пропущен мажорный релиз, миграции могут упасть на середине, оставив базу в промежуточном состоянии. Обновляйтесь постепенно, читайте changelog на предмет breaking changes и перед каждым обновлением снимайте свежий дамп — откат на предыдущий образ без отката базы не сработает, если миграции успели частично примениться.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли развернуть Baserow без Docker, напрямую на сервере?

Технически да — репозиторий содержит инструкции по установке backend на Python/Django и frontend на Node.js, но это ощутимо больше ручной работы: свой Postgres, свой Redis, свои systemd-юниты. Docker-образ существует именно для того, чтобы не собирать это руками, и на VPS почти всегда проще.

Сколько ресурсов реально нужно для 5–10 пользователей?

Для небольшой команды достаточно 2 vCPU и 2–4 ГБ RAM — этого хватает и на сам Baserow, и на встроенные Postgres с Redis. Если на сервере есть что-то ещё, закладывайте запас: Baserow не самый лёгкий сервис из-за связки Django + Celery + Postgres в одном стеке.

Baserow поддерживает подключение к внешней (не встроенной) базе данных?

Да, в разбитом варианте docker-compose backend подключается к любому PostgreSQL 12+ через стандартные DATABASE_HOST/DATABASE_PORT/DATABASE_NAME. Удобно, если уже есть управляемый Postgres или нужен общий сервер БД для нескольких приложений.

Что делать, если после сбоя embedded Postgres не поднимается вообще?

Если бэкапа нет, а данные важны — не запускайте контейнер повторно, это может усугубить повреждение. Скопируйте /baserow/data на другой диск и уже на копии пробуйте pg_resetwal или восстановление через single-user режим Postgres. Это крайняя мера и не всегда даёт полный результат — отсюда правило регулярных дампов из раздела выше.

Нужен ли отдельный worker для realtime-обновлений в таблицах?

В all-in-one образе всё уже включено. В разбитом compose убедитесь, что WebSocket-эндпоинты backend проксируются с заголовками Upgrade/Connection, как в разделе про 502 — иначе realtime тихо не заработает, а обычный HTTP API будет отвечать нормально, что сбивает с толку при диагностике.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →