MAATRIX / Блог / Как установить и настроить Grist на VPS

Как установить и настроить Grist на VPS

MAATRIX

Excel не тянет совместную работу, а Airtable упирается в лимиты бесплатного плана и хранит ваши данные на серверах в США без гарантий, что завтра не срежут функциональность. Grist — гибрид таблицы и реляционной базы: формулы почти как в Excel, но под капотом honest SQL (SQLite), и любую таблицу можно открыть как обычную БД через API или прямой SQL-запрос. Разворачиваем его на собственном VPS за 20 минут — с Docker Compose, HTTPS через Let's Encrypt и бэкапом, который вы полностью контролируете.

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

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

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

Что такое Grist и зачем разворачивать его самому

Grist — open-source проект (лицензия Apache 2.0), изначально сделанный бывшими инженерами Google Docs. По интерфейсу это таблица: строки, столбцы, формулы через =, drag-and-drop полей. Но внутри каждая таблица — это настоящая SQLite-таблица, и документ Grist — это по сути SQLite-файл .grist. Отсюда вытекают практические плюсы:

  • Формулы на Python, а не на урезанном DSL — можно писать произвольную логику, импортировать datetime, re и работать со ссылками между таблицами как с foreign key.
  • REST API из коробкиGET /api/docs/{docId}/tables/{table}/records отдаёт JSON, без танцев с сторонними коннекторами.
  • SQL-запросы прямо к документу через эндпоинт /sql, включая JOIN между таблицами внутри одного документа.
  • Данные физически на вашем диске — файл .grist можно скопировать, открыть sqlite3-клиентом, положить в git (хотя для бинарника это не идеально) или экспортировать в момент, когда захотите мигрировать на что-то другое.

Официальный SaaS (getgrist.com) даёт бесплатно 1 воркспейс и ограничивает число строк на бесплатном тарифе. Self-hosted версия — тот же код без искусственных потолков, но обслуживание (обновления, бэкапы, HTTPS) становится вашей задачей. Для команды до 10-20 человек и приватных данных (финмодель, CRM, склад) это разумный обмен.

Если присматривались к Baserow или NocoDB — Grist ближе по духу к Baserow, но формулы у него мощнее за счёт полноценного Python, а не ограниченного набора функций.

Требования к серверу

Grist — довольно лёгкое приложение: Node.js-сервер плюс SQLite, без отдельной СУБД и без тяжёлого фронтенд-рантайма в рантайме (сборка статическая).

РесурсМинимумКомфортно (команда 10-20 чел.)
CPU1 vCPU2 vCPU
RAM1 GB2-4 GB
Диск10 GB SSD20-40 GB SSD (зависит от вложений в документах)
ОСUbuntu 24.04 / Debian 12Ubuntu 24.04 LTS

Формулы на Python выполняются в отдельном sandbox-процессе — это добавляет накладные расходы при пересчёте больших таблиц, поэтому если документы будут на десятки тысяч строк с тяжёлыми формулами, закладывайте 2 vCPU / 4 GB с запасом, а не впритык.

Для теста хватит бюджетного VPS с оплатой из России картой или криптой, без блокировок по гео.

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

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

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

Установка через Docker Compose

Официально поддерживаемый способ развёртывания — Docker-образ gristlabs/grist. Разворачиваем его вместе с Caddy или Nginx как reverse proxy для HTTPS; ниже — вариант с Nginx, который проще встроить в уже существующую инфраструктуру.

Подключаемся по SSH, ставим Docker и Docker Compose plugin, если ещё не стоят:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
docker compose version

Создаём рабочую директорию и файл docker-compose.yml:

mkdir -p ~/grist/persist
cd ~/grist
nano docker-compose.yml

Содержимое:

services:
  grist:
    image: gristlabs/grist:latest
    container_name: grist
    restart: unless-stopped
    ports:
      - "127.0.0.1:8484:8484"
    environment:
      APP_HOME_URL: "https://grist.example.com"
      GRIST_SINGLE_ORG: "personal"
      GRIST_DEFAULT_EMAIL: "admin@example.com"
      TYPEORM_DATABASE: "/persist/home.sqlite3"
      GRIST_SESSION_SECRET: "замените-на-случайную-строку-32-символа"
    volumes:
      - ./persist:/persist

Ключевые переменные:

  • APP_HOME_URL — публичный URL, под которым будет доступен Grist. Без него ссылки внутри интерфейса (например, на общий доступ к документу) будут генерироваться неправильно.
  • GRIST_SINGLE_ORG — привязывает инстанс к одной организации без экрана выбора рабочих пространств при логине. Удобно, если Grist ставится для одной команды, а не как мультитенантный сервис.
  • GRIST_SESSION_SECRET — обязательно сгенерируйте случайную строку (openssl rand -hex 32), иначе сессии будут уязвимы.
  • Порт 8484 намеренно забинден только на 127.0.0.1 — наружу Grist будет смотреть через Nginx с TLS, напрямую порт наружу не выставляем.

Запускаем:

docker compose up -d
docker compose logs -f grist

В логах должна появиться строка вида Server available at http://localhost:8484 — это подтверждение, что контейнер поднялся и слушает порт.

Настройка Nginx и HTTPS

Ставим Nginx и Certbot, если их ещё нет:

sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx

Конфиг /etc/nginx/sites-available/grist:

server {
    listen 80;
    server_name grist.example.com;

    location / {
        proxy_pass http://127.0.0.1:8484;
        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;

        # Grist использует WebSocket для realtime-обновлений документа
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        client_max_body_size 200M;
    }
}

Важный момент, о котором часто забывают: Grist держит WebSocket-соединение для живого обновления документа между пользователями. Без строк proxy_set_header Upgrade и Connection "upgrade" совместная работа над документом будет ломаться — изменения одного пользователя не появятся у другого без перезагрузки страницы.

Активируем конфиг и получаем сертификат:

sudo ln -s /etc/nginx/sites-available/grist /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d grist.example.com

Certbot сам пропишет listen 443 ssl и настроит автопродление через systemd timer. Если сертификат не выдаётся с первого раза — почти всегда дело в DNS-записи, которая ещё не разрешается на IP сервера, или в закрытом 80 порту в фаерволе. Разбор типовых причин — в статье про ошибки Let's Encrypt.

Не забудьте открыть порты в UFW, если он включён:

sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH

Первый запуск и создание документа

Открываем https://grist.example.com в браузере. Первый пользователь, зашедший по логину/e-mail, автоматически становится владельцем организации (при заданном GRIST_DEFAULT_EMAIL вход по паролю не требуется — это режим для внутреннего использования; для публичного доступа см. раздел про авторизацию ниже).

Создаём новый документ, добавляем таблицу и пробуем формулу — например, столбец Total, вычисляемый как $Price * $Quantity. Grist подсвечивает синтаксис и автодополняет имена полей через $.

Проверить работу API — получите ключ в профиле (иконка аккаунта → API Key), затем:

curl -H "Authorization: Bearer ВАШ_API_КЛЮЧ" \
  https://grist.example.com/api/docs/DOC_ID/tables/Table1/records

DOC_ID виден в адресной строке при открытом документе (после /doc/). Ответ — JSON с массивом записей, готовый для интеграции с внешними скриптами, n8n, Zapier-подобными инструментами или собственным бэкендом.

Авторизация: от простого режима к полноценному SSO

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

Для продакшн-сценария есть два варианта. Первый — SAML/OIDC через внешний IdP (Google Workspace, Keycloak, Authentik), задаётся переменными GRIST_OIDC_*: правильный путь, если у команды уже есть корпоративный SSO. Второй, как временная мера, — базовая HTTP-аутентификация на уровне Nginx поверх location / { ... }:

sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd admin

и в конфиге сайта добавить auth_basic "Grist"; и auth_basic_user_file /etc/nginx/.htpasswd; перед proxy_pass. Это не замена нормальной авторизации (внутри всё равно один общий аккаунт), но пока не настроена OIDC — рабочая защита от случайного индексирования и брутфорса.

Бэкап и обновление

Все данные Grist лежат в смонтированном томе ./persist — это SQLite-файлы документов плюс метаданные. Бэкап сводится к копированию директории, но делать это нужно аккуратно: SQLite не любит копирование "на живую" при активной записи.

Простой скрипт с остановкой контейнера на секунды (приемлемо для ночного cron):

#!/bin/bash
BACKUP_DIR="/backup/grist/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
cd ~/grist
docker compose stop grist
tar -czf "$BACKUP_DIR/persist.tar.gz" persist/
docker compose start grist
# храним последние 14 бэкапов
find /backup/grist/* -maxdepth 0 -type d | sort | head -n -14 | xargs -r rm -rf

Вместо полной остановки можно использовать sqlite3 file.sqlite3 ".backup copy.sqlite3" для каждого документа — консистентный снимок без остановки контейнера, но потребует перебора всех .grist-файлов в persist/docs/. Для большинства команд простой в пару секунд ночью не критичен, поэтому проще держать вариант со stop/start. Общие принципы бэкапа БД на VPS, включая перенос копий на внешнее S3-хранилище — в статье про резервное копирование БД.

Обновление до новой версии:

cd ~/grist
docker compose pull
docker compose up -d

Перед обновлением на мажорную версию (смена latest на конкретный тег с ощутимым скачком номера) обязательно сделайте свежий бэкап persist/ — миграции формата документов у Grist обратно совместимы не во всех направлениях, откатиться проще с копией на диске, чем без неё.

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

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

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

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

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

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

Grist подходит для замены Airtable полностью?

Для большинства сценариев (CRM, учёт, справочники, простые формы) — да, с поправкой на то, что часть готовых интеграций и шаблонов у Airtable богаче за счёт размера экосистемы. Формулы и API у self-hosted Grist ничем не ограничены.

Можно ли открыть .grist-файл напрямую как SQLite-базу?

Да, это валидный SQLite-файл, sqlite3 document.grist откроет его в консоли. Но напрямую редактировать данные так не стоит — Grist хранит служебные метаданные о формулах и связях, которые легко сломать ручным UPDATE.

Нужна ли отдельная СУБД типа PostgreSQL?

Нет для самих документов — они в SQLite. PostgreSQL используется опционально только как home-database для метаданных организации при развёртывании через docker-compose с профилем postgres, но для одного-двух серверов это избыточно, дефолтного SQLite-хранилища метаданных достаточно.

Как перенести Grist на другой сервер?

Копируете директорию persist/ целиком (после остановки контейнера) на новый сервер, поднимаете тот же docker-compose.yml, меняете DNS на новый IP.

Формулы на Python — это безопасно, если документами делятся внешние пользователи?

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

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

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

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