Encryption at Rest for Self-Hosted Supabase: A Guide

Managed Supabase encrypts data at rest by default. Self-hosters don't get it for free. Here's how to encrypt disks, volumes, and backups properly.

Cover Image for Encryption at Rest for Self-Hosted Supabase: A Guide

If you moved off Supabase Cloud to run your own instance, there's a security guarantee you quietly lost on the way out: encryption at rest. On the managed platform, every byte of your database and storage is encrypted with AES-256 on disk automatically. You never think about it. On a self-hosted Supabase box, that protection does not exist until you build it yourself — and most people don't realize the gap is there until an auditor, a compliance questionnaire, or a stolen disk image makes it painfully obvious.

This guide walks through what "encryption at rest" actually means for a self-hosted Supabase deployment, where the real risks are, and how to close the gap across your database, storage volumes, and backups without cargo-culting your way into a false sense of security.

What "encryption at rest" really protects against

Encryption at rest protects data on disk when the disk is not actively serving your running application. It is not a defense against a compromised application, a leaked service key, or a SQL injection bug — those attacks happen through a live, authenticated database that decrypts data for you. What it protects against is narrower but very real:

  • A stolen or decommissioned physical drive
  • A cloud provider snapshotting or reusing the underlying block storage
  • A backup file copied to an S3 bucket that later turns out to be public
  • A subpoena or seizure of the physical hardware
  • A misconfigured volume mount exposed to another tenant

Managed Supabase encrypts all customer data at rest with AES-256, which is why it can credibly answer "yes" on SOC 2 and HIPAA questionnaires. When you self-host, you inherit that responsibility entirely. This is closely tied to the broader data residency and compliance picture for self-hosted Supabase — encryption at rest is usually the first checkbox on any compliance form, and the easiest one to fail silently.

A critical clarification: encryption at rest is not the same as the TLS/SSL encryption that protects data in transit. You need both. TLS protects the wire; encryption at rest protects the platter. Conflating the two is the single most common mistake we see in self-hosting threat models.

Layer 1: Full-disk encryption with LUKS

The most robust and least fragile approach is to encrypt the entire volume your Docker data lives on. On Linux, that means LUKS (Linux Unified Key Setup). Because Supabase's self-hosted stack stores Postgres data, storage objects, and logs as bind mounts or named volumes under the Docker data root, encrypting that filesystem covers everything at once — no per-service configuration, no application changes.

A typical setup encrypts a dedicated block device before you ever start your containers:

# Encrypt a fresh block device (e.g. an attached cloud volume)
cryptsetup luksFormat /dev/sdb

# Open it and map to a friendly name
cryptsetup open /dev/sdb supabase_data

# Create a filesystem and mount it
mkfs.ext4 /dev/mapper/supabase_data
mount /dev/mapper/supabase_data /var/lib/supabase

# Point Docker / your compose volumes at /var/lib/supabase

The honest trade-off: LUKS needs a key to unlock the volume at boot. If you store that key in a plaintext file on the same machine, you've encrypted the front door and taped the key to it. The real-world answers are a remote unlock over SSH at boot, a TPM-bound key, or a network-bound disk encryption (NBDE/Tang) server. For most single-server self-hosters, accepting a manual passphrase entry on reboot is a reasonable starting point — just be honest with yourself about whether your server reboots unattended, because if it does, the encryption is only protecting you against drive theft while powered off, not a live snapshot.

This pairs naturally with the rest of your server hardening and installation setup — do it before you load production data, because retrofitting full-disk encryption onto a running instance means downtime and a full data migration.

Layer 2: Application-level encryption with Vault and pgsodium

Full-disk encryption is all-or-nothing: when the volume is unlocked, everything is readable to anything with database access. For your most sensitive columns — API tokens, third-party credentials, PII — you often want encryption that persists even when the database is online. That's where Supabase Vault comes in.

Vault is a Postgres extension (built on pgsodium) that stores secrets encrypted with authenticated encryption (AEAD) directly inside the database. The key insight for self-hosters: the encryption key lives outside the database, in your VAULT_ENC_KEY environment variable. So even a full pg_dump of your database produces ciphertext that's useless without the separately-held key.

-- Store a secret; it lands on disk encrypted
select vault.create_secret('sk_live_supersecret', 'stripe_key');

-- Decrypt only when you explicitly read it
select decrypted_secret from vault.decrypted_secrets
where name = 'stripe_key';

The non-negotiable rule: never store VAULT_ENC_KEY in the same place as your database backups. If both end up in the same S3 bucket, you've encrypted nothing. We cover the full key-management workflow — generation, rotation, and KMS integration — in our secrets management guide for self-hosted Supabase. One operational note for 2026: pgsodium is in pending deprecation upstream, with Vault becoming the supported front door, so build new work against the vault.* API rather than calling pgsodium directly.

Use this layer surgically. Encrypting every column with Vault destroys your ability to index and query efficiently, so reserve it for the handful of fields that genuinely need column-level protection on top of disk encryption.

Layer 3: The piece everyone forgets — encrypted backups

Here's where most self-hosted encryption strategies quietly fall apart. You can LUKS-encrypt your production disk perfectly, then run a nightly pg_dump that drops a plaintext .sql file onto an object store. Your data at rest is now exactly as secure as that bucket's permissions — which is to say, one misconfiguration away from a breach. Backups are data at rest too, and they're the copies most likely to leak because they travel.

A correct backup pipeline encrypts the dump before it leaves the host:

# Dump, compress, and encrypt to a recipient's public key
pg_dump "$DATABASE_URL" \
  | gzip \
  | gpg --encrypt --recipient [email protected] \
  > backup-$(date +%F).sql.gz.gpg

Now the file is opaque even if the destination bucket is compromised. The same discipline applies to your storage objects — the files users upload — which live entirely separate from the Postgres database and are easy to overlook. If this is new territory, our backup and restore guide breaks down why storage backups are the forgotten half of disaster recovery.

The catch with rolling your own: encrypted backups are worthless if you can't decrypt and restore them under pressure. Key loss is now a data-loss event, not just a security one. Whatever scheme you choose, document the recovery path and actually test it — see our backup creation docs and storage configuration for the operational details.

Where Supascale fits

Stitching these three layers together by hand — LUKS at boot, Vault key hygiene, encrypted backup pipelines with tested restores — is exactly the kind of undifferentiated operational work that pushes teams back toward managed hosting at 10x the price. Supascale handles the backup layer for you: automated, encrypted S3 backups with one-click restore, so the most leak-prone copies of your data are protected by default instead of by a fragile shell script you wrote at 2am. Encryption keys stay yours, the data stays on your infrastructure, and there's no vendor lock-in — you keep full data sovereignty while skipping the part where you reinvent a backup encryption pipeline.

It's a one-time $39.99 purchase for unlimited projects, which means the cost math for "do it properly" stops being a recurring tax on every instance you run.

Conclusion

Encryption at rest on self-hosted Supabase isn't a single switch — it's three honest layers: full-disk encryption (LUKS) for the platform, Vault for surgical column-level protection, and encrypted backups for the copies that travel. Skip any one and you've left the door you care about most wide open. The good news is that none of it is exotic; it's standard Linux and Postgres tooling applied deliberately, before you load production data rather than after a security review forces your hand.

If you'd rather not hand-roll the backup half of that equation, start with Supascale and get encrypted, restorable backups out of the box — then spend your time on the layers that genuinely need your judgment.

Further Reading