[{"data":1,"prerenderedAt":190},["ShallowReactive",2],{"seo-verification":3,"blog-self-host-hasura-graphql-engine-vps-ma-en":6,"promo-bar-likely":20},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-self-host-hasura-graphql-engine-vps-ma-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"noindex":20,"publishedAt":21,"updatedAt":22,"category":23,"categories":29,"featuredImage":31,"bgImage":32,"posterImage":33,"relatedSolution":34,"intro":37,"sections":38,"ctaTitle":123,"ctaBody":124,"ctaButton":125,"ctaUrl":126,"relatedPosts":127},505,"self-host-hasura-graphql-engine-vps",{"fr":12,"en":10,"ar":13,"es":14},"self-host-hasura-vps","استضافة-hasura-على-vps-واجهة-graphql","autoalojar-hasura-api-graphql-vps","Self-Host Hasura on a VPS: Instant GraphQL API on PostgreSQL","Install Hasura on a VPS with Docker Compose: a GraphQL and REST API on PostgreSQL, role-based permissions, real-time subscriptions and HTTPS.",6,0,false,true,"2026-10-27T00:00:00+00:00","2026-11-04T16:30:29+00:00",{"id":24,"name":25,"slug":26,"color":27,"icon":28},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[30],{"id":24,"name":25,"slug":26,"color":27,"icon":28},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fself-host-hasura-vps-poster.svg",{"categorySlug":35,"appSlug":36},"development","hasura","Writing the same CRUD code for every table, then the same access checks, then the same API documentation: that is the work Hasura GraphQL Engine removes. This open-source engine (Apache-2.0 licensed) connects to a PostgreSQL database and immediately exposes a complete GraphQL API, with relationships, filters, aggregates and real-time subscriptions, protected by permissions declared per role. This guide installs it with Docker Compose on a VPS, next to its own PostgreSQL database, publishes it over HTTPS behind nginx and sets the security rules you must not forget.",[39,43,53,88,91,107,111,114,117,120],{"type":40,"title":41,"body":42},"h2","Why a generated API rather than a hand-written backend","In most applications, a large part of the server code merely translates HTTP requests into SQL: list, filter, paginate, create, update, delete, checking each time that the user is allowed to. That code is repetitive, it needs tests, and every new column means touching it again.\n\nHasura starts from the database schema. A tracked table becomes a GraphQL type, a foreign key becomes a nested field, and a permission rule written once applies to every query. Your own business logic — a payment, an email, a calculation — stays in your code, which Hasura calls by webhook. You keep the PostgreSQL database: nothing is locked into a proprietary format.",{"type":44,"title":45,"items":46},"ul","What Hasura brings",[47,48,49,50,51,52],"**Instant GraphQL API** — queries, mutations, filters, sorting, pagination and aggregates on every tracked table.","**Relationships** — foreign keys become nested fields, queryable in a single request.","**Real-time subscriptions** — a client subscribes to a query and receives changes over WebSocket.","**Role-based permissions** — row- and column-level rules, driven by the claims of a JWT.","**Actions and triggers** — webhooks called by a mutation, by a data change or on a cron schedule.","**Console and REST endpoints** — web administration, GraphiQL explorer, and turning a query into a REST route.",{"type":54,"title":55,"headers":56,"rows":60},"comparison","Hasura versus other ways to expose PostgreSQL",[5,57,58,59],"Hasura","Hand-written API","PostgREST",[61,66,71,75,80,85],[62,63,64,65],"API generated from the schema","Yes, GraphQL and REST","No","Yes, REST",[67,68,69,70],"Nested relationships in one request","Yes","Depends on the code","Yes, via embedding",[72,73,74,64],"Real time","GraphQL subscriptions","To be built",[76,77,78,79],"Permissions","Per role, row and column, in the console","In the code","PostgreSQL roles and RLS",[81,82,83,84],"Business logic","Actions and triggers as webhooks","Free","SQL functions",[86,87,64,64],"Admin interface","Web console",{"type":40,"title":89,"body":90},"Prerequisites","An Ubuntu 24.04 VPS with **1 GB of RAM** is enough to start: at rest, the Hasura engine and PostgreSQL use about 200 MB together. Plan for **2 GB** as soon as the database grows or subscriptions multiply. You need Docker with the Compose plugin, and a **domain name** pointing to the VPS, with nginx and an HTTPS certificate: your API will be called by browsers and mobile apps, so it must answer on a stable, encrypted address.",{"type":92,"title":93,"steps":94},"steps","Install Hasura with Docker Compose",[95,98,101,104],{"title":96,"body":97},"Prepare the secrets","Create the application directory and a `.env` file holding the database password and the **admin secret**, the API's master key. The file is readable by root only.\n```bash\nmkdir -p \u002Fopt\u002Fhasura && cd \u002Fopt\u002Fhasura\ncat > .env \u003C\u003CEOF\nDB_PASSWORD=$(openssl rand -hex 16)\nADMIN_SECRET=$(openssl rand -hex 24)\nEOF\nchmod 600 .env\n```",{"title":99,"body":100},"Create the Compose file","This `docker-compose.yml` starts PostgreSQL on the stack's internal network, with no published port, then Hasura once the database is ready. Hasura's port is published on the loopback only; the console is served from the image, with no call to the vendor's CDN, and telemetry is off.\n```yaml\nservices:\n  postgres:\n    image: postgres:17.11-alpine\n    restart: unless-stopped\n    environment:\n      POSTGRES_DB: hasura\n      POSTGRES_USER: hasura\n      POSTGRES_PASSWORD: ${DB_PASSWORD}\n    volumes:\n      - hasura-pgdata:\u002Fvar\u002Flib\u002Fpostgresql\u002Fdata\n    healthcheck:\n      test: [\"CMD-SHELL\", \"pg_isready -U hasura -d hasura\"]\n      interval: 10s\n      retries: 10\n  hasura:\n    image: hasura\u002Fgraphql-engine:v2.51.3\n    restart: unless-stopped\n    depends_on:\n      postgres:\n        condition: service_healthy\n    ports:\n      - \"127.0.0.1:8103:8080\"\n    env_file: .env\n    environment:\n      HASURA_GRAPHQL_DATABASE_URL: postgres:\u002F\u002Fhasura:${DB_PASSWORD}@postgres:5432\u002Fhasura\n      HASURA_GRAPHQL_ADMIN_SECRET: ${ADMIN_SECRET}\n      HASURA_GRAPHQL_ENABLE_CONSOLE: \"true\"\n      HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: \u002Fsrv\u002Fconsole-assets\n      HASURA_GRAPHQL_DEV_MODE: \"false\"\n      HASURA_GRAPHQL_ENABLE_TELEMETRY: \"false\"\nvolumes:\n  hasura-pgdata: {}\n```",{"title":102,"body":103},"Start the stack","On first start, Hasura creates its metadata tables in the `hdb_catalog` schema and connects the database as the `default` source. The `\u002Fhealthz` endpoint answers `OK` when the engine is ready.\n```bash\ndocker compose up -d\ncurl -fsS http:\u002F\u002F127.0.0.1:8103\u002Fhealthz   # OK\n```",{"title":105,"body":106},"Publish over HTTPS behind nginx","Add a `server` block for your domain, with a Let's Encrypt certificate. The `Upgrade` and `Connection` headers are required: subscriptions go through WebSocket.\n```nginx\nlocation \u002F {\n    proxy_pass http:\u002F\u002F127.0.0.1:8103;\n    proxy_http_version 1.1;\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n    proxy_set_header Host $host;\n    proxy_set_header X-Forwarded-Proto $scheme;\n}\n```",{"type":108,"title":109,"body":110},"tip","The admin secret never leaves the server","Without `HASURA_GRAPHQL_ADMIN_SECRET`, the console and the whole API are open to anyone who knows the address. With it, it grants **full rights**: read and write every table, run SQL, change the metadata. Keep it for the console and your admin scripts, and never put it in frontend or mobile app code.",{"type":40,"title":112,"body":113},"Create a table and query it","Open `https:\u002F\u002Fapi.example.com\u002Fconsole`, enter the admin secret, then, in the **Data** tab, create a `todos` table with the columns `id` (auto-increment integer), `title` (text) and `done` (boolean). Click **Track**: the table immediately shows up in the explorer of the **API** tab. From an admin script, the same query is sent with the admin secret:\n```bash\ncurl -s https:\u002F\u002Fapi.example.com\u002Fv1\u002Fgraphql \\\n  -H \"x-hasura-admin-secret: $ADMIN_SECRET\" \\\n  -H 'content-type: application\u002Fjson' \\\n  -d '{\"query\":\"{ todos { id title done } }\"}'\n```",{"type":40,"title":115,"body":116},"Authenticate your users and set permissions","Your users must not use the admin secret: they present a JWT issued by your identity provider (Logto, Authentik, Zitadel, Auth0…). Tell Hasura where to find the verification keys, and which role to give requests without a token, in the `.env` file:\n```bash\nHASURA_GRAPHQL_JWT_SECRET={\"type\":\"RS256\",\"jwk_url\":\"https:\u002F\u002Fauth.example.com\u002Foidc\u002Fjwks\"}\nHASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous\n```\n\nRestart the stack with `docker compose up -d`: the `hasura` service reads this file through `env_file`. The token must carry the `x-hasura-default-role`, `x-hasura-allowed-roles` and `x-hasura-user-id` claims. In each table's **Permissions** tab, then define what each role can read, insert or update — for example, the `user` role only sees rows where `user_id` equals `X-Hasura-User-Id`.",{"type":40,"title":118,"body":119},"Troubleshooting","**The console shows \"invalid x-hasura-admin-secret\"**: the secret you entered does not match the value in `.env`. Check it with `grep ADMIN_SECRET \u002Fopt\u002Fhasura\u002F.env`, then reload the page.\n\n**Hasura keeps restarting**: the database is unreachable. `docker compose logs hasura` shows the connection error; check that the password in the `HASURA_GRAPHQL_DATABASE_URL` URL is PostgreSQL's. If you changed it after the first start, PostgreSQL kept the old one: it only reads `POSTGRES_PASSWORD` when the volume is created.\n\n**Subscriptions drop**: the reverse proxy does not pass the WebSocket upgrade. Add the `Upgrade` and `Connection` headers shown above, and raise `proxy_read_timeout` if your clients stay idle for long.\n\n**A table does not appear in the API**: it exists in the database but is not tracked. Click **Track** in the **Data** tab, or track every table of the schema at once.",{"type":40,"title":121,"body":122},"One-click install from the ServOrbit Marketplace","The ServOrbit Marketplace installs Hasura on your VPS without a command line: it deploys PostgreSQL and the Hasura engine, generates the admin secret and the database password, connects the database as the `default` source and publishes the API over HTTPS on a subdomain of your domain or on the VPS's free subdomain. The admin secret is shown in your client area; all that is left is to open the console and create your tables.","Your GraphQL API on PostgreSQL, on your own VPS","A ServOrbit Ubuntu 24.04 VPS with 1 GB of RAM is enough to get started with Hasura. One-click install from the Marketplace, PostgreSQL included, admin secret generated for you, HTTPS on your domain or on the VPS's free subdomain.","Install Hasura","\u002Fapplications\u002Fdevelopment\u002Fhasura",[128,152,170],{"id":129,"slug":130,"slugs":131,"title":135,"excerpt":136,"readTime":137,"views":138,"isPinned":19,"noindex":19,"publishedAt":139,"updatedAt":140,"category":141,"categories":146,"featuredImage":31,"bgImage":32,"posterImage":148,"relatedSolution":149},56,"postgresql-on-a-vps-a-reliable-and-controlled-database",{"fr":132,"en":130,"ar":133,"es":134},"heberger-postgresql-vps","postgresql-على-خادم-vps-قاعدة-بيانات-موثوقة-ومتحكم-بها","alojar-postgresql-en-un-vps","PostgreSQL on a VPS: reliable, controlled database","Host PostgreSQL on a VPS: production config, pgBackRest backups, streaming HA, RDS\u002FSupabase\u002FNeon comparison and OOM troubleshooting.",9,1,"2026-04-25T00:00:00+00:00","2026-10-21T15:21:37+00:00",{"id":17,"name":142,"slug":143,"color":144,"icon":145},"Databases","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[147],{"id":17,"name":142,"slug":143,"color":144,"icon":145},"\u002Fblog\u002Fcovers\u002Fheberger-postgresql-vps-poster.svg",{"categorySlug":150,"appSlug":151},"databases","postgresql-stack",{"id":153,"slug":154,"slugs":155,"title":159,"excerpt":160,"readTime":161,"views":18,"isPinned":19,"noindex":19,"publishedAt":162,"updatedAt":163,"category":164,"categories":165,"featuredImage":31,"bgImage":32,"posterImage":167,"relatedSolution":168},60,"hosting-supabase-on-a-vps",{"fr":156,"en":154,"ar":157,"es":158},"heberger-supabase-vps","استضافة-supabase-على-خادم-vps","alojar-supabase-en-un-vps","Hosting Supabase on a VPS in 2026","Self-host Supabase on your Cloud VPS: Postgres, Auth, Storage and REST API behind Envoy. PG 15→17 migration, Kong→Envoy, GoTrue CVE.",15,"2026-04-21T00:00:00+00:00","2026-11-08T01:13:14+00:00",{"id":17,"name":142,"slug":143,"color":144,"icon":145},[166],{"id":17,"name":142,"slug":143,"color":144,"icon":145},"\u002Fblog\u002Fcovers\u002Fheberger-supabase-vps-poster.svg",{"categorySlug":150,"appSlug":169},"supabase",{"id":171,"slug":172,"slugs":173,"title":177,"excerpt":178,"readTime":179,"views":18,"isPinned":19,"noindex":19,"publishedAt":180,"updatedAt":181,"category":182,"categories":187,"featuredImage":31,"bgImage":32,"posterImage":189,"relatedSolution":31},282,"depends-on-is-not-enough-postgresql-healthcheck-in-compose",{"fr":174,"en":172,"ar":175,"es":176},"docker-compose-depends-on-healthcheck","depends-on-لا-يكفي-healthcheck-لـ-postgresql-في-compose","docker-compose-healthcheck-postgresql","depends_on is not enough: PostgreSQL healthcheck in Compose","Why `depends_on` alone does not guarantee PostgreSQL is ready, and how to configure a reliable healthcheck with `service_healthy` to avoid race conditions.",8,"2026-08-19T00:00:00+00:00","2026-09-07T11:26:10+00:00",{"id":183,"name":184,"slug":185,"color":186,"icon":185},3,"Deployment","deploiement","bg-success\u002F10 text-success",[188],{"id":183,"name":184,"slug":185,"color":186,"icon":185},"\u002Fblog\u002Fcovers\u002Fdocker-compose-depends-on-healthcheck-poster.svg",1794270486863]