Skip to content
James Brooks // thatnerdknows
all writing
// writing

Self-hosting Supabase in a Proxmox LXC

June 28, 2026 · #supabase #proxmox #self-hosting

What you get: a full self-hosted Supabase stack — Postgres, Auth (GoTrue), REST (PostgREST), Realtime, Storage, Studio, Edge Functions, the Kong gateway and the Supavisor pooler — running in a single lightweight Proxmox container, behind your reverse proxy, for dev/experimentation. Footprint is small: the whole stack idles around ~1.3 GB RAM.

This guide also covers the thing that trips people up in 2026: Supabase’s new asymmetric (ES256) JWT + opaque API keys, and how to enable them without breaking existing clients.


1. Create the container (unprivileged Docker LXC)

An unprivileged LXC with nesting is fine for Docker and is the safer default:

# on the PVE host, adjust storage/IP
pct create 101 local:vztmpl/debian-12-standard_*.tar.zst \
  --hostname supabase --cores 4 --memory 6144 --rootfs local-lvm:40 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.15.160/24,gw=192.168.15.1 \
  --features nesting=1,keyctl=1 --unprivileged 1 --onboot 1
pct start 101
pct exec 101 -- bash -c 'apt-get update && apt-get install -y ca-certificates curl git && \
  curl -fsSL https://get.docker.com | sh'

2. Pull + configure the stack

pct exec 101 -- bash
git clone --depth 1 https://github.com/supabase/supabase /opt/supabase
cd /opt/supabase/docker
cp .env.example .env

Edit .env — the essentials: POSTGRES_PASSWORD, JWT_SECRET, DASHBOARD_USERNAME/DASHBOARD_PASSWORD (Studio is behind Kong basic-auth), and set the public URLs to your domain:

SITE_URL=https://supabase.example.com
API_EXTERNAL_URL=https://supabase.example.com
SUPABASE_PUBLIC_URL=https://supabase.example.com

Modern Supabase dropped the old analytics/Logflare + vector services, so there’s nothing heavy to trim anymore — just bring the compose up as-is.

docker compose up -d
docker compose ps          # db, kong, auth, rest, realtime, storage, studio, meta, supavisor, edge-functions...

3. Reverse-proxy it (front door = Kong on :8000)

Point your proxy at the Kong gateway (CONTAINER_IP:8000) — that’s the single entry for the REST/Auth/Storage APIs and Studio. Example intent (Traefik or nginx): supabase.example.com → http://192.168.15.160:8000, TLS at the edge, and (recommended for a homelab) restrict it to LAN-only. Add a local DNS record so the hostname resolves internally. Studio’s root returns a 307 → /project/default once authenticated — that’s normal.


4. The 2026 gotcha — turn on ES256 asymmetric keys

Supabase is moving from legacy symmetric-only JWTs (the classic anon/service_role keys) to asymmetric ES256 signing + opaque API keys (sb_publishable_… and sb_secret_…). The legacy keys are deprecated end-2026, so enable the new system now. Supabase ships a helper that does it non-breakingly:

cd /opt/supabase
./utils/add-new-auth-keys.sh --update-env
docker compose up -d

What it does: populates JWT_KEYS / JWT_JWKS, adds SUPABASE_PUBLISHABLE_KEY (anon) and SUPABASE_SECRET_KEY (service_role) plus the *_KEY_ASYMMETRIC variants, and uncomments GOTRUE_JWT_KEYS in the compose. Because the published JWKS still includes your legacy symmetric key, old anon/service_role tokens keep verifying — so nothing breaks. (Verify: hit /rest/v1/ with the old anon key and the new publishable key — both should return 200.) Rotate the opaque keys later with ./utils/rotate-new-api-keys.sh.

Store the secrets properly: POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, the new publishable/secret keys, SECRET_KEY_BASE, VAULT_ENC_KEY — into your password manager, not git. The operational copy lives only in the container’s /opt/supabase/docker/.env (gitignored).


Gotchas recap

  1. Unprivileged LXC + nesting=1,keyctl=1 — Docker + Supabase run fine; no need for a privileged container.
  2. Proxy to Kong :8000, not the individual services — Kong is the gateway for everything incl. Studio.
  3. Enable the ES256 key system now with add-new-auth-keys.sh --update-env — it’s non-breaking and future-proofs you before the end-2026 legacy-key deprecation.
  4. LAN-only unless you really need it public — Studio + the service_role key are powerful; don’t expose them to the internet without an auth gate in front.

Written from a working dev deployment: Supabase in an unprivileged Docker LXC on a Proxmox node (Postgres 17 + Kong), LAN-only behind Traefik, migrated to the ES256 publishable/secret key system. Adjust IPs/domain/versions to your setup.


Written by James Brooks — I run ThatNerdKnows (IT support + websites for small businesses). This is the deep end; if you’d rather just have it handled, that’s the day job.