[{"data":1,"prerenderedAt":289},["ShallowReactive",2],{"seo-verification":3,"blog-hosting-supabase-on-a-vps-ma-en":6,"promo-bar-likely":288},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-hosting-supabase-on-a-vps-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":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":33,"intro":36,"sections":37,"ctaTitle":229,"ctaBody":230,"ctaButton":231,"ctaUrl":232,"relatedPosts":233},60,"hosting-supabase-on-a-vps",{"fr":12,"en":10,"ar":13,"es":14},"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,0,false,"2026-04-21T00:00:00+00:00","2026-11-08T01:13:14+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},6,"Databases","bases-de-donnees","bg-teal-500\u002F10 text-teal-400","database",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fheberger-supabase-vps-poster.svg",{"categorySlug":34,"appSlug":35},"databases","supabase","Supabase is the open source alternative to Firebase built on PostgreSQL: database, authentication, storage, edge functions, and an auto-generated API. Since August 2026, the self-hosted stack has migrated from Kong to **Envoy** as the internal API proxy. Self-hosting it on a VPS gives you a complete backend without project caps or usage-based billing, with your data in your own Postgres.",[38,42,52,55,77,117,120,123,126,129,132,153,184,187,202,205,208],{"type":39,"title":40,"body":41},"h2","Why self-host Supabase on a VPS","Supabase is not a black box: it is a set of open source services (PostgreSQL, GoTrue for auth, PostgREST for the API, Realtime, Storage, Envoy as a gateway, formerly Kong) orchestrated together. The cloud version bills per project and per monthly active user, and pauses inactive free projects. By self-hosting on a VPS, you get a complete backend-as-a-service without those limits, with direct access to your PostgreSQL for migrations, extensions (`pgvector`, `PostGIS`) and tuning. Ideal for an agency hosting several client apps, a bootstrapped SaaS looking to control costs, or any project requiring data sovereignty. You handle updates and backups yourself, in exchange for total freedom.",{"type":43,"title":44,"items":45},"ul","Concrete benefits of a self-hosted Supabase",[46,47,48,49,50,51],"Complete backend: Postgres, Auth, Storage, Realtime and REST\u002FGraphQL API in one stack.","Direct access to the underlying PostgreSQL: SQL migrations, extensions and RLS policies without limits.","No pausing or billing per monthly active user.","Studio included: a web administration interface to manage tables, auth and buckets.","AI extensions via `pgvector` for RAG and semantic search, freely enabled.","Multi-projects on the same VPS: practical for an agency or development studio.",{"type":39,"title":53,"body":54},"Hardware and software requirements","Self-hosted Supabase starts around ten containers (Postgres, Envoy, GoTrue, PostgREST, Realtime, Storage, Studio, Meta, Imgproxy...), making it a more resource-intensive stack than a plain database. Count a minimum of 2 vCPU and 4 GB of RAM for a test environment, and prefer 4 vCPU with 8 GB of RAM and 80 GB of SSD for production. On the software side: Ubuntu 22.04\u002F24.04 LTS, Docker and Docker Compose v2, a domain name pointing to the VPS (e.g. `api.myapp.com`) to expose the gateway over HTTPS, and a reverse proxy such as Caddy, Traefik or Nginx to handle SSL. Also plan to generate strong secrets (`JWT_SECRET`, `anon` and `service_role` keys).",{"type":56,"title":57,"steps":58},"steps","Deploy self-hosted Supabase with Docker",[59,62,65,68,71,74],{"title":60,"body":61},"Clone the official repository and prepare the environment","Fetch the `docker` folder from the Supabase repository, copy `.env.example` to `.env`, then generate unique secrets: `POSTGRES_PASSWORD`, `JWT_SECRET`, and the `ANON_KEY` \u002F `SERVICE_ROLE_KEY` keys derived from the JWT. Never deploy with the example values.",{"title":63,"body":64},"Configure URLs and the Studio password","In `.env`, set `SITE_URL`, `API_EXTERNAL_URL` and `SUPABASE_PUBLIC_URL` with your domain, and secure Supabase Studio with `DASHBOARD_USERNAME` and `DASHBOARD_PASSWORD`: Studio gives full admin access to your project.",{"title":66,"body":67},"Launch the stack","Start with `docker compose up -d`, then follow service startup via `docker compose ps`. The first Postgres initialisation and internal migration application take one to two minutes; check for the absence of errors in `docker compose logs`.",{"title":69,"body":70},"Place a reverse proxy with SSL","Put Caddy or Traefik in front of the API gateway (Envoy, port 8000) to expose your API over HTTPS. Caddy automatically obtains and renews Let's Encrypt certificates: a simple `myapp.com { reverse_proxy localhost:8000 }` block is enough. Never expose Postgres publicly. Yet the official compose publishes the gateway (8000) and the Supavisor pooler (5432 and 6543) on all interfaces, and ports published by Docker bypass ufw: prefix them with `127.0.0.1:` in `docker-compose.yml` (for example `127.0.0.1:${POSTGRES_PORT}:5432`), or close them in your provider's firewall.",{"title":72,"body":73},"Enable RLS policies","Connect to Studio, create your tables, then enable Row Level Security on each one and define your policies. Without RLS, your `anon` key exposes all your data: this is the setting to configure before exposing anything.",{"title":75,"body":76},"Back up Postgres and Storage","Schedule a daily `pg_dump` of the database and archive the Storage volume (file buckets) to external storage. Test a full restoration on a staging VPS before relying on it in production.",{"type":78,"title":79,"headers":80,"rows":84},"comparison","Supabase vs Appwrite: which self-hosted BaaS to choose?",[81,82,83],"Criterion","Supabase","Appwrite",[85,89,93,97,101,105,109,113],[86,87,88],"Database","Native PostgreSQL, full SQL","MariaDB internally, document\u002Fcollection API",[90,91,92],"Data model","Relational, Postgres RLS","Documents and collections, rule-based permissions",[94,95,96],"Auto-generated API","REST (PostgREST) + GraphQL","REST, GraphQL and multiple SDKs",[98,99,100],"Authentication","GoTrue, OAuth, magic links","Integrated auth, OAuth, teams, JWT",[102,103,104],"Resource footprint","Heavier (~10 containers)","Moderate, more compact stack",[106,107,108],"Vector search \u002F AI","Native via pgvector","Not native, must be externalised",[110,111,112],"Best for","Relational apps, RAG, advanced SQL","Mobile\u002Fweb apps oriented around documents",[114,115,116],"Learning curve","Comfortable if you know SQL","Fast for front\u002Fmobile developers",{"type":118,"body":119},"tip","Update Supabase with care: since images are versioned in `docker-compose.yml`, always do a full `pg_dump` and a VPS snapshot before a `docker compose pull && docker compose up -d`. Some version bumps touch the internal service schema. For production, isolate Postgres on a dedicated high-IOPS volume and note that `pgvector` ships with the image: it can be enabled at any time with `create extension vector;`, without rebuilding the database.",{"type":39,"title":121,"body":122},"Deploy from the official Supabase stack, not a homemade compose","It is tempting to write a minimal docker-compose with only the few services you need. In practice, this breaks: the supabase\u002Fpostgres image runs its own migration script (migrate.sh) that provisions the base roles (supabase_admin, authenticator, supabase_auth_admin...) and the entire auth schema, and expects Supabase SQL initialisation files to be mounted as files with variable expansion at run time. A standalone inline compose cannot reproduce this (Docker Compose interpolates variables inside inline configs, leaving the auth schema half-migrated and GoTrue failing with 'must be owner of function auth.uid()'). The robust approach — the one ServOrbit uses — is to deploy the official Supabase Docker files, pinned to a specific version, and let migrate.sh do the bootstrapping exactly as upstream intends.",{"type":39,"title":124,"body":125},"Back your instance up automatically","Supabase's Docker deployment backs up nothing on its own — it is the gap self-hosters report most. Two mechanisms complement each other. The first, `pg_dump`, produces a full logical copy: run it from the host on a cron, write it to a volume separate from the database's own, and keep several generations. The second, WAL archiving, journals every transaction and lets you rewind to a precise moment rather than to the last dump. The first is enough for most projects; the second becomes necessary as soon as losing a day of writes is unacceptable. Either way, a backup is only worth what a restore proves: spin up a blank instance and replay your dump into it before you actually need to.",{"type":39,"title":127,"body":128},"Trimming the stack: the analytics service","Until self-hosted release 0.5.0 (June 2026), the official Docker composition shipped Logflare, Supabase's internal analytics service, under the name `analytics`. It is now optional: removed from the default `docker-compose.yml`, it is enabled with the `docker-compose.logs.yml` override. It is essential neither to the API, nor to authentication, nor to storage: it is an observability tool. On an older instance it may still be running, and on a VPS with tight resources it often reports an `unhealthy` state without the rest of the stack suffering. First measure what it consumes on your setup with `docker stats` before deciding: if your monitoring already goes through an external tool, you lose nothing; if you relied on its logs, turn it off only after wiring up a replacement.",{"type":39,"title":130,"body":131},"Kong→Envoy migration: what changes, and who it breaks","On 11 August 2026, with its self-hosted release 0.8.0, Supabase made **Envoy** the default API gateway for self-hosted installations; Kong moves to an optional override. Envoy had already shipped for several releases via `docker-compose.envoy.yml` — what changes is the **default**.\n\nIn `docker-compose.yml` the service is now called `api-gw` and the container `supabase-envoy`, but **the `kong` network alias is kept**: services that call the gateway by that name keep working. The HTTP port stays **8000**, set through `API_GW_HTTP_PORT`, so your Caddy or Nginx reverse proxy needs no change.\n\n⚠️ **Supabase calls this a breaking change for a SUBSET of self-hosters**, and you need to know whether you are one. Three cases: you relied on **Kong's built-in HTTPS port 8443** — it is no longer shipped by default, TLS termination now goes through `docker-compose.caddy.yml` or `docker-compose.nginx.yml`; you had a **customised** `volumes\u002Fapi\u002Fkong.yml` — routes and plugins must be ported to the Envoy configuration, which lives in `volumes\u002Fapi\u002Fenvoy\u002F`; your scripts reference the gateway **by service name**. In all three cases you can also stay on Kong: `sh run.sh config add kong`.",{"type":56,"title":133,"steps":134},"Move to Envoy without breaking your gateway",[135,138,141,144,147,150],{"title":136,"body":137},"Find out what you are running","Run `docker compose ps`. A `supabase-kong` container means you are still on Kong; `supabase-envoy` means the switch is done. Also check whether `volumes\u002Fapi\u002Fkong.yml` differs from the repository's — that file decides whether the migration costs you any work.",{"title":139,"body":140},"Back up before touching the compose file","Take a full `pg_dump` and, if your host allows it, a VPS snapshot. Switching gateway does not touch the PostgreSQL schema, but it changes the entry path of every request: a fast rollback beats a live diagnosis.",{"title":142,"body":143},"Decide: port or stay","If your `kong.yml` is the repository's, there is nothing to port. If it holds routes or plugins of your own, two paths: translate them into Envoy configuration under `volumes\u002Fapi\u002Fenvoy\u002F`, or stay on Kong with `sh run.sh config add kong` while you do. Staying is a legitimate decision, not a failure.",{"title":145,"body":146},"Pull the new compose and restart","Pull the latest `docker` folder from the official Supabase repository, then `docker compose down && docker compose up -d`. Check that `api-gw` reaches `healthy` and that the API answers on port 8000.",{"title":148,"body":149},"Restore TLS if you relied on Kong's 8443","The built-in HTTPS port is no longer shipped. Add TLS termination with `docker-compose.caddy.yml` or `docker-compose.nginx.yml`, or let your existing reverse proxy handle it in front of port 8000. This is the most common breakage, and it only shows on the first direct HTTPS call.",{"title":151,"body":152},"Check storage and signed URLs","Test a download from a private bucket through a pre-signed URL. If you get a 403 after the switch, look first at `Host` header rewriting and at your storage service's public URL setting: that is where the two gateways differ.",{"type":78,"title":154,"headers":155,"rows":159},"Kong and Envoy, point by point",[156,157,158],"Aspect","Kong (before August 2026)","Envoy (default since)",[160,164,168,172,176,180],[161,162,163],"Service in `docker-compose.yml`","`kong`","`api-gw` — `kong` kept as a network alias",[165,166,167],"Container","`supabase-kong`","`supabase-envoy`",[169,170,171],"HTTP port","8000","8000 — via `API_GW_HTTP_PORT`",[173,174,175],"Built-in HTTPS port","8443","**removed** — TLS via `docker-compose.caddy.yml` \u002F `.nginx.yml`",[177,178,179],"Configuration","`volumes\u002Fapi\u002Fkong.yml`","`volumes\u002Fapi\u002Fenvoy\u002F` (versioned YAML)",[181,182,183],"Opting back out","—","`sh run.sh config add kong`",{"type":39,"title":185,"body":186},"CVE-2026-31813: OIDC flaw in GoTrue — update now","A vulnerability was published in March 2026 in GoTrue (Supabase Auth, Supabase's authentication service) under the reference CVE-2026-31813. It affects ID token sign-in with the Apple and Azure providers: GoTrue validated the token against whatever issuer the token named, including an issuer controlled by the attacker.\n\nIn practice, an attacker who signs, from their own OIDC issuer, an ID token carrying a victim's email address obtains a valid session (AAL1 level) on that victim's account. Only instances with the Apple or Azure provider enabled are affected. Instances that only use email\u002Fpassword authentication or magic links are **not** exposed.\n\nThe fix is included in **Supabase Auth (GoTrue) 2.185.0**. To check your version: `docker compose exec auth gotrue version`. If your gotrue version is older than 2.185.0, update immediately.",{"type":56,"title":188,"steps":189},"Update GoTrue to fix CVE-2026-31813",[190,193,196,199],{"title":191,"body":192},"Identify the current version","From your Supabase stack directory: `docker compose exec auth gotrue version`. If the output shows a version \u003C 2.185.0, your instance is exposed to CVE-2026-31813. Also note the overall stack version: `grep 'SUPABASE_VERSION' .env` (if pinned) or `docker compose images | grep supabase`.",{"title":194,"body":195},"Back up before updating","Before any update, back up the database: `(umask 077; docker compose exec -T db pg_dumpall -h localhost -U supabase_admin > \u002Froot\u002Fsupabase-backup-$(date +%F).sql)`, never in `\u002Ftmp`, which every account on the server can read. Updating GoTrue does not involve a PostgreSQL schema migration, but a prior backup remains the rule before any version change in production.",{"title":197,"body":198},"Update Supabase Auth to 2.185.0 or newer","In your `docker-compose.yml`, update the `supabase\u002Fgotrue` image tag to `v2.185.0` or newer, or if following the full stack, pull the latest compatible version: `docker compose pull && docker compose up -d auth`. Updating GoTrue alone does not require restarting other services. Verify with `docker compose exec auth gotrue version` that the new version is active.",{"title":200,"body":201},"Verify OIDC authentication","After the update, test a complete authentication flow with each configured OIDC provider: login, logout, re-login. Check in GoTrue logs (`docker compose logs auth`) that no error messages related to `iss` validation appear under normal usage conditions.",{"type":118,"title":203,"body":204},"Temporarily disable Apple and Azure if you cannot update immediately","If an immediate update is not possible (maintenance window required), reduce the exposure by temporarily disabling the Apple and Azure providers. In self-hosted, they are set through the `auth` service's environment variables: set `GOTRUE_EXTERNAL_APPLE_ENABLED` and `GOTRUE_EXTERNAL_AZURE_ENABLED` to `false`, then run `docker compose up -d auth`. Email\u002Fpassword authentication keeps working. This workaround is temporary: it does not fix the flaw, it only reduces the attack surface while you prepare the update.",{"type":39,"title":206,"body":207},"PostgreSQL 15 → 17 migration: pin before you pull","On 17 June 2026, with its self-hosted release 0.6.0, Supabase switched the default PostgreSQL image in its official `docker-compose.yml` from version 15 to version 17 (a change announced on 18 May 2026 in discussion [#46080](https:\u002F\u002Fgithub.com\u002Forgs\u002Fsupabase\u002Fdiscussions\u002F46080)). This change is **destructive** if you ignore it: the vendor warns never to start PostgreSQL 17 on a data directory created by PostgreSQL 15. Pulling the new compose carelessly on an instance still on PG 15 leaves you with a database that no longer starts.\n\nSecond trap: the `timescaledb`, `plv8`, `plcoffee` and `plls` extensions are **not built for the PG 17 image**. None is installed by default; if you added one and need to keep it, the vendor advises not to run the upgrade. Check before going further: `docker compose exec db psql -U postgres -c \"\\dx\"` lists the active extensions.",{"type":56,"title":209,"steps":210},"Migrate from PostgreSQL 15 to 17 without losing your data",[211,214,217,220,223,226],{"title":212,"body":213},"Stay on PG 15 before any `docker compose pull`","First check your version: `docker compose exec db psql -U postgres -c \"SELECT version();\"`. If it starts with 15, keep the PG 15 image until the migration is done: the repository provides the `docker-compose.pg15.yml` override, which pins `supabase\u002Fpostgres:15.8.1.085`. Add it with `sh run.sh config add pg15`, or pass it by hand: `docker compose -f docker-compose.yml -f docker-compose.pg15.yml up -d`. It is also the rollback target of the migration script.",{"title":215,"body":216},"Back up the data and the pgsodium key","In a root shell, start with `umask 077`: the files you create will be readable by you only. Copy the data directory: `cp -a .\u002Fvolumes\u002Fdb\u002Fdata .\u002Fvolumes\u002Fdb\u002Fdata-manual-backup`. Then back up the pgsodium root key, which lives in the `db-config` named Docker volume, not in that directory: `docker compose run --rm db cat \u002Fetc\u002Fpostgresql-custom\u002Fpgsodium_root.key > .\u002Fpgsodium_root.key.backup`. Without it, Vault secrets become unrecoverable. On top of that, a logical export: `docker compose exec -T db pg_dumpall -h localhost -U supabase_admin > .\u002Fpg15_dump.sql`. Copy these files to external storage.",{"title":218,"body":219},"Check extensions and disk space","`docker compose exec db psql -U postgres -c \"\\dx\"` lists the active extensions. If `timescaledb`, `plv8`, `plcoffee` or `plls` appear and you need them, stop there: they do not exist for PG 17. Otherwise, the script will offer to drop them. Also check free space: you need at least twice the database size, plus 5 GB.",{"title":221,"body":222},"Fetch the up-to-date docker folder","Pull the latest version of the `docker` folder from the official Supabase repository, which contains `utils\u002Fupgrade-pg17.sh`. Leave all the stack's containers running: the script requires it. Do not delete any Docker volume by hand, especially `db-config`, which holds the pgsodium key.",{"title":224,"body":225},"Run the official migration","`sudo bash utils\u002Fupgrade-pg17.sh`. The script stops the stack, runs `pg_upgrade` from a temporary PG 15 container, re-enables extensions in a PG 17 container, swaps the data directories while keeping the original in `.\u002Fvolumes\u002Fdb\u002Fdata.bak.pg15`, then restarts Supabase on PG 17. It asks for confirmation at each major step (`--yes` to skip).",{"title":227,"body":228},"Verify, then clean up","Check the version with `docker compose exec db psql -U postgres -c \"SELECT version();\"`, then `docker compose ps`, and test a full API call. If `docker-compose.pg15.yml` is still listed in `COMPOSE_FILE`, remove it with `sh run.sh config remove pg15`. Only once everything is verified, delete `.\u002Fvolumes\u002Fdb\u002Fdata.bak.pg15`, `.\u002Fvolumes\u002Fdb\u002Fpgsodium_root.key.bak.pg15` and the `.\u002Fvolumes\u002Fdb\u002Fpg17_upgrade_bin_*.tar.gz` archive to reclaim space.","Self-host your Supabase backend","The ServOrbit Cloud VPS with a preconfigured Docker template and generous RAM hosts the entire Supabase stack: Postgres, Auth, Storage, and API over HTTPS.","Launch my Cloud VPS","\u002Fvps-cloud",[234,253,270],{"id":235,"slug":236,"slugs":237,"title":241,"excerpt":242,"readTime":243,"views":244,"isPinned":19,"noindex":19,"publishedAt":245,"updatedAt":246,"category":247,"categories":248,"featuredImage":30,"bgImage":31,"posterImage":250,"relatedSolution":251},56,"postgresql-on-a-vps-a-reliable-and-controlled-database",{"fr":238,"en":236,"ar":239,"es":240},"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":23,"name":24,"slug":25,"color":26,"icon":27},[249],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fheberger-postgresql-vps-poster.svg",{"categorySlug":34,"appSlug":252},"postgresql-stack",{"id":254,"slug":255,"slugs":256,"title":260,"excerpt":261,"readTime":243,"views":18,"isPinned":19,"noindex":19,"publishedAt":262,"updatedAt":263,"category":264,"categories":265,"featuredImage":30,"bgImage":31,"posterImage":267,"relatedSolution":268},57,"hosting-mysql-on-a-vps",{"fr":257,"en":255,"ar":258,"es":259},"heberger-mysql-vps","استضافة-mysql-على-خادم-vps","alojar-mysql-en-un-vps","Hosting MySQL on a VPS: complete guide","Install MySQL 8.4 LTS on a VPS: prerequisites, security hardening, InnoDB performance, secure remote access, automated backups, and troubleshooting.","2026-04-24T00:00:00+00:00","2026-11-09T20:55:18+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[266],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fheberger-mysql-vps-poster.svg",{"categorySlug":34,"appSlug":269},"mysql-stack",{"id":271,"slug":272,"slugs":273,"title":277,"excerpt":278,"readTime":279,"views":18,"isPinned":19,"noindex":19,"publishedAt":280,"updatedAt":281,"category":282,"categories":283,"featuredImage":30,"bgImage":31,"posterImage":285,"relatedSolution":286},58,"hosting-redis-on-a-vps",{"fr":274,"en":272,"ar":275,"es":276},"heberger-redis-vps","استضافة-redis-على-خادم-vps","alojar-redis-en-un-vps","Hosting Redis on a VPS: the complete guide","Deploy Redis on your Cloud VPS: RAM\u002FCPU requirements, security (ACL, TLS), RDB\u002FAOF persistence, troubleshooting, and high availability.",10,"2026-04-23T00:00:00+00:00","2026-09-08T21:59:59+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[284],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fheberger-redis-vps-poster.svg",{"categorySlug":34,"appSlug":287},"redis-stack",true,1794270421361]