[{"data":1,"prerenderedAt":190},["ShallowReactive",2],{"seo-verification":3,"blog-self-hosting-grist-on-a-vps-open-source-airtable-alternative-ma-en":6,"promo-bar-likely":157},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-self-hosting-grist-on-a-vps-open-source-airtable-alternative-ma-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"noindex":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":27,"featuredImage":29,"bgImage":30,"posterImage":31,"relatedSolution":32,"intro":35,"sections":36,"ctaTitle":125,"ctaBody":126,"ctaButton":127,"ctaUrl":128,"relatedPosts":129},218,"self-hosting-grist-on-a-vps-open-source-airtable-alternative",{"fr":12,"en":10,"ar":13,"es":14},"self-host-grist-vps","استضافة-grist-على-vps-بديل-airtable-مفتوح-المصدر","alojar-grist-en-un-vps-alternativa-a-airtable","Self-Hosting Grist on a VPS: Open-Source Airtable Alternative","Deploy Grist v1.7.20 on a VPS: Docker Compose, nginx HTTPS, Python formulas, OIDC SSO, automated backups — open-source alternative to Airtable.",7,3,false,"2026-08-04T00:00:00+00:00","2026-11-04T16:30:26+00:00",{"id":17,"name":23,"slug":24,"color":25,"icon":26},"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[28],{"id":17,"name":23,"slug":24,"color":25,"icon":26},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fself-host-grist-vps-poster.svg",{"categorySlug":33,"appSlug":34},"development","grist","Google Sheets stores lists. Airtable adds relations between tables but charges $20 per user per month. Grist does both, open-source, on your own server: typed columns, references between tables, Python formulas running server-side, and a REST API generated automatically on every document. The result is a complete data modelling tool — no subscription, no vendor lock-in.",[37,41,50,53,66,69,72,75,78,122],{"type":38,"title":39,"body":40},"h2","Why Grist instead of Google Sheets or Airtable","Google Sheets is a spreadsheet: it stores data flat, calculations are limited to sheet functions, and relations between sheets rely on fragile VLOOKUPs. Airtable solves this with relational bases but at $20 per user per month, the bill adds up fast for an agency or technical team.\n\nGrist combines both worlds: a familiar spreadsheet interface over a real relational database. Each document contains tables with typed columns (text, numeric, date, reference, attachment), explicit relations between tables via reference columns, and Python formulas that run server-side — not in the browser. On your VPS, Grist costs $0 extra per month.",{"type":42,"title":43,"items":44},"ul","What Grist does that Google Sheets cannot",[45,46,47,48,49],"**Relations between tables**: a reference column in `Orders` points to `Customers` — not a breakable VLOOKUP, a real foreign key with autocomplete.","**Server-side Python formulas**: `import datetime`, `import re`, `import uuid` — and any pure-Python library. Formulas execute in a server sandbox, not the browser.","**Automatic REST API**: every document exposes `\u002Fapi\u002Fdocs\u002F\u003Cid>\u002Ftables\u002F\u003Ctable>\u002Frecords` without configuration. Integrate Grist with n8n, Make or any HTTP client.","**Multiple views on the same data**: grid, card, chart, calendar, custom widget — each view is filtered and configured independently without duplicating data.","**Per-row, per-column access rules**: restrict read or write access to a row based on user attributes — granular enough for multi-tenant documents.",{"type":38,"title":51,"body":52},"Requirements and Docker architecture","A VPS with **at least 1 GB of RAM** and Docker installed is sufficient to get started. Grist deploys as a single `gristlabs\u002Fgrist` container bundling Node.js (web server), Python (formula sandbox) and SQLite (document storage). A named volume `\u002Fpersist` stores `.grist` files — one file per document. No external service required: no PostgreSQL, no Redis, no Celery.\n\nThe default port is **8484**. Verify it is reachable from localhost before configuring the reverse proxy (`curl -sI http:\u002F\u002Flocalhost:8484\u002F`).",{"type":54,"title":55,"steps":56},"steps","Full Docker Compose and deployment",[57,60,63],{"title":58,"body":59},"Create the directory and docker-compose.yml","SSH into your VPS and create the stack directory:\n\n```bash\nmkdir -p \u002Fopt\u002Fstacks\u002Fgrist && cd \u002Fopt\u002Fstacks\u002Fgrist\n```\n\nCreate the following `docker-compose.yml`:\n\n```yaml\nservices:\n  grist:\n    image: gristlabs\u002Fgrist:1.7.20\n    container_name: grist\n    restart: unless-stopped\n    ports:\n      - \"127.0.0.1:8484:8484\"\n    volumes:\n      - grist_data:\u002Fpersist\n    environment:\n      GRIST_SESSION_SECRET: \"REPLACE_WITH_openssl_rand_hex_32\"\n      APP_HOME_URL: \"https:\u002F\u002Fgrist.yourdomain.com\"\n      GRIST_DEFAULT_EMAIL: \"admin@localhost.local\"\n      GRIST_SANDBOX_FLAVOR: \"gvisor\"\n\nvolumes:\n  grist_data:\n```\n\nGenerate the session secret before starting: `openssl rand -hex 32`. Never reuse this value across instances.",{"title":61,"body":62},"Start and verify","```bash\ndocker compose up -d\ndocker compose logs -f grist\n```\n\nWait for the `Grist server ready` message. Check the HTTP status:\n\n```bash\ncurl -sI http:\u002F\u002Flocalhost:8484\u002F\n```\n\nYou should get `HTTP\u002F1.1 200 OK` or a `302` redirect. If the container keeps restarting, see the Troubleshooting section below.",{"title":64,"body":65},"Configure nginx as HTTPS reverse proxy","Install nginx and certbot on the VPS, then create `\u002Fetc\u002Fnginx\u002Fsites-available\u002Fgrist.conf`:\n\n```nginx\nserver {\n    listen 80;\n    server_name grist.yourdomain.com;\n    return 301 https:\u002F\u002F$host$request_uri;\n}\n\nserver {\n    listen 443 ssl;\n    server_name grist.yourdomain.com;\n\n    ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fgrist.yourdomain.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fgrist.yourdomain.com\u002Fprivkey.pem;\n    ssl_protocols TLSv1.2 TLSv1.3;\n\n    add_header Strict-Transport-Security \"max-age=63072000; includeSubDomains\" always;\n    add_header X-Frame-Options SAMEORIGIN always;\n    add_header X-Content-Type-Options nosniff always;\n    add_header Referrer-Policy strict-origin-when-cross-origin always;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:8484;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_read_timeout 300s;\n    }\n}\n```\n\nObtain the certificate and enable the vhost:\n\n```bash\ncertbot certonly --nginx -d grist.yourdomain.com\nln -s \u002Fetc\u002Fnginx\u002Fsites-available\u002Fgrist.conf \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\nnginx -t && systemctl reload nginx\n```\n\nThen update `APP_HOME_URL` in the Compose file and restart: `docker compose up -d`.",{"type":38,"title":67,"body":68},"Advanced Grist formulas: VLOOKUP and cross-table references","Grist replaces fragile Google Sheets VLOOKUPs with **reference columns** and **typed lookup functions**. In an `Orders` table with a `Customer` column of type `Reference → Customers`, accessing the customer name is simply `$Customer.Name` — no VLOOKUP needed.\n\nFor aggregations, Grist provides `lookupRecords()` and `lookupOne()`:\n\n```python\n# In the Customers table — total order amount for a customer\nSUM(Orders.lookupRecords(Customer=$id).Amount)\n\n# In the Orders table — retrieve the customer's postal code\nCustomers.lookupOne(Name=$Customer.Name).PostalCode\n\n# Progressive reduction using standard Python\nimport datetime\ndelay = (datetime.date.today() - $OrderDate).days\n$Amount * (1 - 0.02 * delay \u002F\u002F 30)\n```\n\nPython formulas run inside a **gVisor sandbox** (`GRIST_SANDBOX_FLAVOR=gvisor`): the Python process has no access to the filesystem or network, isolating computations even in multi-tenant contexts.",{"type":38,"title":70,"body":71},"SSO authentication with OIDC (optional)","By default, `GRIST_DEFAULT_EMAIL` automatically logs in a single user without a password — sufficient for solo use or a trusted team on a private network. For multi-user deployments with real authentication, Grist supports **OpenID Connect (OIDC)** with Authentik, Keycloak, Google Workspace or any compatible provider.\n\nAdd these variables to the Compose file:\n\n```yaml\nenvironment:\n  GRIST_OIDC_IDP_ISSUER: \"https:\u002F\u002Fauth.yourdomain.com\u002Fapplication\u002Fo\u002Fgrist\u002F\"\n  GRIST_OIDC_IDP_CLIENT_ID: \"your-client-id\"\n  GRIST_OIDC_IDP_CLIENT_SECRET: \"your-client-secret\"\n  GRIST_OIDC_IDP_SCOPES: \"openid email profile\"\n  GRIST_OIDC_SP_HOST: \"https:\u002F\u002Fgrist.yourdomain.com\"\n```\n\nRemove `GRIST_DEFAULT_EMAIL` once OIDC is configured — the two variables are incompatible. The first user to log in via OIDC becomes the site administrator.",{"type":38,"title":73,"body":74},"Backups: Docker volumes and export","Grist documents live in the Docker volume `grist_data`, under `\u002Fpersist\u002Fdocs\u002F`. Two complementary strategies:\n\n**Volume snapshot** (full backup, recommended): mount `\u002Fpersist\u002Fdocs\u002F` in a Backrest job (available in the ServOrbit catalogue). Backrest snapshots the folder via restic and pushes encrypted increments to S3, Backblaze B2 or SFTP — without stopping Grist.\n\n```bash\n# Emergency manual backup\ndocker run --rm \\\n  -v grist_data:\u002Fsource:ro \\\n  -v \u002Fbackups\u002Fgrist:\u002Fdest \\\n  alpine tar czf \u002Fdest\u002Fgrist-$(date +%Y%m%d).tar.gz -C \u002Fsource .\n```\n\n**Per-document export**: in Grist, go to Document → Export → `.grist` (native SQLite format) or CSV\u002FXLSX per table. The `.grist` export is sufficient to migrate a single document to another instance.\n\nGrist also keeps a **per-document snapshot history** (Document → History) — useful for restoring a previous version without leaving the interface, up to the configured retention limit.",{"type":38,"title":76,"body":77},"Troubleshooting","**Container keeps restarting**: check the logs (`docker compose logs grist`). The most common cause is an empty or too-short `GRIST_SESSION_SECRET` — it must be at least 32 hexadecimal characters. Also check volume permissions: `docker exec grist ls -la \u002Fpersist`.\n\n**CORS errors on the API**: if you call `\u002Fapi\u002Fdocs\u002F…` from a different domain, add `GRIST_ALLOWED_HOSTS=grist.yourdomain.com` and make sure `APP_HOME_URL` matches the public URL exactly (with `https:\u002F\u002F`, no trailing slash).\n\n**Slowness on large datasets**: Grist re-evaluates all formulas on every change. On tables with more than 100,000 rows, limit `lookupRecords()` to indexed columns and avoid Python formulas with heavy imports in trigger columns. The `GRIST_MAX_ROWS_PER_TABLE` variable (unbounded by default) allows setting a preventive limit.\n\n**Access issues after domain change**: update `APP_HOME_URL`, clear browser cookies and restart the container. Active sessions use the old URL and become invalid.",{"type":79,"title":80,"intro":81,"headers":82,"rows":87,"conclusion":121},"comparison","Grist vs NocoDB vs Baserow: choosing your open-source spreadsheet","All three tools occupy the \"open-source Airtable\" niche, but with different positioning.",[83,84,85,86],"Criterion","Grist","NocoDB","Baserow",[88,93,98,103,108,112,117],[89,90,91,92],"Storage","SQLite per document (.grist file)","PostgreSQL or MySQL (shared DB)","PostgreSQL (shared DB)",[94,95,96,97],"Formulas","Server-side Python (sandbox)","No native formulas","Limited formulas, client-side JS",[99,100,101,102],"REST API","Automatic per document","Automatic per base","Automatic",[104,105,106,107],"SSO auth","Native OIDC","LDAP, OIDC (Enterprise)","SSO paid (plans > Pro)",[109,110,111,111],"Backups","One .grist file per document","PostgreSQL dump",[113,114,115,116],"Ideal use case","Data modelling, complex formulas","Team-oriented Airtable replacement","No-code interface for business teams",[118,119,120,120],"Resources (startup)","1 GB RAM, Docker","2 GB RAM, Docker + PostgreSQL","Grist is the only one of the three to isolate each document in a standalone SQLite file — simplifying backup and migration of individual documents. NocoDB and Baserow share a single PostgreSQL database, which eases centralised management but complicates per-project portability.",{"type":123,"body":124},"tip","To update Grist, change the image tag in the Compose file (`gristlabs\u002Fgrist:1.7.20` → next version), stop the container (`docker compose down`), then restart (`docker compose up -d`). The `grist_data` volume persists across versions. Check the release notes at github.com\u002Fgristlabs\u002Fgrist-core before each update — some versions introduce schema migrations that are not reversible.","Your Airtable, on your own server","Deploy Grist on a ServOrbit VPS and model your data without a subscription, without vendor lock-in — REST API, Python formulas, real-time collaboration.","Deploy Grist","\u002Fapplications\u002Fdevelopment\u002Fgrist",[130,148,167],{"id":131,"slug":132,"slugs":133,"title":137,"excerpt":138,"readTime":139,"views":140,"isPinned":19,"noindex":19,"publishedAt":141,"updatedAt":21,"category":142,"categories":143,"featuredImage":29,"bgImage":30,"posterImage":145,"relatedSolution":146},188,"self-host-nocobase-on-a-vps-no-code-platform-for-internal-tools",{"fr":134,"en":132,"ar":135,"es":136},"self-host-nocobase-vps","استضافة-nocobase-على-vps-منصة-بلا-كود-لأدواتك-الداخلية","alojar-nocobase-en-un-vps-no-code-herramientas-internas","Self-Host NocoBase on a VPS: No-Code Platform for Internal Tools","Deploy NocoBase on your VPS — the no-code\u002Flow-code platform to build internal tools, CRM and approval workflows. Apache-2.0, 23 k+ stars, v2.1.26.",10,1,"2026-07-22T00:00:00+00:00",{"id":17,"name":23,"slug":24,"color":25,"icon":26},[144],{"id":17,"name":23,"slug":24,"color":25,"icon":26},"\u002Fblog\u002Fcovers\u002Fself-host-nocobase-vps-poster.svg",{"categorySlug":33,"appSlug":147},"nocobase",{"id":149,"slug":150,"slugs":151,"title":155,"excerpt":156,"readTime":18,"views":140,"isPinned":19,"noindex":157,"publishedAt":158,"updatedAt":159,"category":160,"categories":161,"featuredImage":29,"bgImage":30,"posterImage":163,"relatedSolution":164},157,"how-to-self-host-nocodb-on-a-vps",{"fr":152,"en":150,"ar":153,"es":154},"self-host-nocodb-vps","كيفية-استضافة-nocodb-ذاتيا-على-خادم-vps","como-autoalojar-nocodb-en-un-vps","How to Self-Host NocoDB on a VPS","Deploy NocoDB on your VPS for a self-hosted, open-source Airtable alternative. Turn any database into a smart spreadsheet with REST and GraphQL APIs, via Docker Compose.",true,"2026-01-20T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":17,"name":23,"slug":24,"color":25,"icon":26},[162],{"id":17,"name":23,"slug":24,"color":25,"icon":26},"\u002Fblog\u002Fcovers\u002Fself-host-nocodb-vps-poster.svg",{"categorySlug":165,"appSlug":166},"databases","nocodb",{"id":168,"slug":169,"slugs":170,"title":174,"excerpt":175,"readTime":176,"views":18,"isPinned":19,"noindex":19,"publishedAt":177,"updatedAt":178,"category":179,"categories":185,"featuredImage":29,"bgImage":30,"posterImage":187,"relatedSolution":188},67,"hosting-meilisearch-on-a-vps",{"fr":171,"en":169,"ar":172,"es":173},"heberger-meilisearch-vps","استضافة-meilisearch-على-خادم-vps","alojar-meilisearch-en-un-vps","Hosting Meilisearch on a VPS","Host Meilisearch on your VPS: an instant, fault-tolerant and typo-tolerant search engine. A Docker, master key and SSL guide.",5,"2026-04-14T00:00:00+00:00","2026-09-08T22:00:01+00:00",{"id":180,"name":181,"slug":182,"color":183,"icon":184},6,"Databases","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[186],{"id":180,"name":181,"slug":182,"color":183,"icon":184},"\u002Fblog\u002Fcovers\u002Fheberger-meilisearch-vps-poster.svg",{"categorySlug":165,"appSlug":189},"meilisearch",1794270501267]