[{"data":1,"prerenderedAt":188},["ShallowReactive",2],{"seo-verification":3,"blog-self-host-hasura-graphql-engine-vps-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-self-host-hasura-graphql-engine-vps-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":33,"intro":36,"sections":37,"ctaTitle":122,"ctaBody":123,"ctaButton":124,"ctaUrl":125,"relatedPosts":126},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,"2026-10-27T00:00:00+00:00","2026-10-27T23:58:39+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fself-host-hasura-vps-poster.svg",{"categorySlug":34,"appSlug":35},"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.",[38,42,52,87,90,106,110,113,116,119],{"type":39,"title":40,"body":41},"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":43,"title":44,"items":45},"ul","What Hasura brings",[46,47,48,49,50,51],"**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":53,"title":54,"headers":55,"rows":59},"comparison","Hasura versus other ways to expose PostgreSQL",[5,56,57,58],"Hasura","Hand-written API","PostgREST",[60,65,70,74,79,84],[61,62,63,64],"API generated from the schema","Yes, GraphQL and REST","No","Yes, REST",[66,67,68,69],"Nested relationships in one request","Yes","Depends on the code","Yes, via embedding",[71,72,73,63],"Real time","GraphQL subscriptions","To be built",[75,76,77,78],"Permissions","Per role, row and column, in the console","In the code","PostgreSQL roles and RLS",[80,81,82,83],"Business logic","Actions and triggers as webhooks","Free","SQL functions",[85,86,63,63],"Admin interface","Web console",{"type":39,"title":88,"body":89},"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":91,"title":92,"steps":93},"steps","Install Hasura with Docker Compose",[94,97,100,103],{"title":95,"body":96},"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":98,"body":99},"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":101,"body":102},"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":104,"body":105},"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":107,"title":108,"body":109},"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":39,"title":111,"body":112},"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":39,"title":114,"body":115},"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":39,"title":117,"body":118},"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":39,"title":120,"body":121},"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","\u002Fmarketplace\u002Fdevelopment\u002Fhasura",[127,150,168],{"id":128,"slug":129,"slugs":130,"title":134,"excerpt":135,"readTime":136,"views":18,"isPinned":19,"publishedAt":137,"updatedAt":138,"category":139,"categories":144,"featuredImage":30,"bgImage":31,"posterImage":146,"relatedSolution":147},56,"postgresql-on-a-vps-a-reliable-and-controlled-database",{"fr":131,"en":129,"ar":132,"es":133},"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,"2026-04-25T00:00:00+00:00","2026-10-21T15:21:37+00:00",{"id":17,"name":140,"slug":141,"color":142,"icon":143},"Databases","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[145],{"id":17,"name":140,"slug":141,"color":142,"icon":143},"\u002Fblog\u002Fcovers\u002Fheberger-postgresql-vps-poster.svg",{"categorySlug":148,"appSlug":149},"databases","postgresql-stack",{"id":151,"slug":152,"slugs":153,"title":157,"excerpt":158,"readTime":159,"views":18,"isPinned":19,"publishedAt":160,"updatedAt":161,"category":162,"categories":163,"featuredImage":30,"bgImage":31,"posterImage":165,"relatedSolution":166},60,"hosting-supabase-on-a-vps",{"fr":154,"en":152,"ar":155,"es":156},"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 with Envoy Gateway. PG 15→17 migration, Kong→Envoy, GoTrue CVE.",14,"2026-04-21T00:00:00+00:00","2026-10-09T22:36:10+00:00",{"id":17,"name":140,"slug":141,"color":142,"icon":143},[164],{"id":17,"name":140,"slug":141,"color":142,"icon":143},"\u002Fblog\u002Fcovers\u002Fheberger-supabase-vps-poster.svg",{"categorySlug":148,"appSlug":167},"supabase",{"id":169,"slug":170,"slugs":171,"title":175,"excerpt":176,"readTime":177,"views":18,"isPinned":19,"publishedAt":178,"updatedAt":179,"category":180,"categories":185,"featuredImage":30,"bgImage":31,"posterImage":187,"relatedSolution":30},282,"depends-on-is-not-enough-postgresql-healthcheck-in-compose",{"fr":172,"en":170,"ar":173,"es":174},"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":181,"name":182,"slug":183,"color":184,"icon":183},3,"Deployment","deploiement","bg-success\u002F10 text-success",[186],{"id":181,"name":182,"slug":183,"color":184,"icon":183},"\u002Fblog\u002Fcovers\u002Fdocker-compose-depends-on-healthcheck-poster.svg",1793146033777]