Self-Hosted Supabase with Podman: A Rootless Docker Guide

Run self-hosted Supabase with Podman instead of Docker for rootless, daemonless containers. Setup, the socket gotchas, and honest trade-offs.

Cover Image for Self-Hosted Supabase with Podman: A Rootless Docker Guide

The official Supabase self-hosting stack assumes Docker. But a lot of teams have a hard requirement against running a root-owned daemon on their servers — corporate hardening policies, RHEL-based fleets that ship Podman by default, or security engineers who simply don't want a socket that grants effective root to anything that can reach it. If that's you, the good news is that a self-hosted Supabase deployment runs perfectly well on Podman, and the community-standard docker-compose.yml works with only a couple of changes. The bad news is that those changes aren't obvious, and the failure modes are confusing when you hit them.

This guide walks through running Supabase on Podman in rootless mode: what actually changes, the socket gotcha that trips up almost everyone, and where the trade-offs bite. If you're still deciding on your base setup, our production Docker Compose guide and the installation docs are the right starting point before you swap the runtime.

Why Podman instead of Docker for Supabase

Podman does the same job as Docker — pulls images, runs containers, reads Compose files — but with two architectural differences that matter for a database platform holding your production data.

No root daemon. Docker runs a persistent daemon (dockerd) as root. Anything that can talk to /var/run/docker.sock can effectively become root on the host. That's a large attack surface for a stack like Supabase, which exposes an API gateway, an auth service, and Postgres. Podman is daemonless: containers are child processes of the user that launched them, so there's no always-on privileged service to compromise.

Rootless by default. With Podman, a normal unprivileged user can run the entire Supabase stack without sudo. Inside the containers Postgres still thinks it's UID 999, but on the host it's mapped — via user namespaces — to an unprivileged subordinate UID. A container breakout lands the attacker as a nobody user, not root. For anyone building a production hardening plan, that containment is a meaningful upgrade.

The honest caveat: Podman is not a drop-in for every Docker workflow. Networking, volume ownership, and socket paths behave differently, and Supabase's Compose file was written against Docker. You will spend an hour reconciling that. Whether it's worth it depends entirely on your threat model.

Prerequisites

On a Debian/Ubuntu host you need three packages:

sudo apt install -y podman podman-docker docker-compose
  • podman — the container engine.
  • podman-docker — a shim that provides a docker command aliased to Podman, so tooling and scripts that shell out to docker keep working.
  • docker-compose — the standalone v2 Compose binary, which talks to Podman over its socket.

On RHEL, Fedora, or Rocky, Podman is usually already installed. Confirm your server meets the baseline in our system requirements — the full Supabase stack wants around 4 GB of RAM before you add headroom, and rootless Podman adds a little overhead on top.

Then clone the Supabase repo the same way you would for Docker:

git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
cp .env.example .env

The socket gotcha (read this before you start anything)

This is the single issue that generates the most "why is my stack broken on Podman" threads, so handle it first.

The Supabase vector service — the log router — mounts the Docker socket so it can read container logs. On rootless Podman, there is no /var/run/docker.sock. The socket lives under your user's runtime directory instead. If you don't tell Supabase where it is, vector starts, finds nothing, and exits cleanly — you'll see container supabase-vector exited (0) with no obvious error, and because analytics depends on it, half your stack refuses to come up.

Start the Podman socket for your user and enable it to persist:

systemctl --user enable --now podman.socket
loginctl enable-linger $USER   # keep the socket alive after you log out

Then point Supabase at it. Edit .env and set:

DOCKER_SOCKET_LOCATION=/run/user/1000/podman/podman.sock

Replace 1000 with your own UID (run id -u to check). This is the exact same DOCKER_SOCKET_LOCATION variable the official docs call out for rootless Docker — the value just points at Podman's socket path instead. Set it correctly up front and vector, analytics, and everything downstream will start normally.

Configure secrets — do not skip this

Before you bring anything up, replace every placeholder in .env. This is not Podman-specific, but it's the most common self-hosting security failure regardless of runtime, so it belongs in any honest guide.

The .env.example ships with well-known default values for POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, and the dashboard credentials. Those defaults are public and identical across every fresh clone — leaving any of them is equivalent to running with no password at all. The SERVICE_ROLE_KEY in particular bypasses Row Level Security entirely, so treat it like a root password: never bake it into an image, never expose it client-side.

Generate real secrets, then set a strong DASHBOARD_USERNAME and DASHBOARD_PASSWORD so Studio isn't sitting behind supabase/this_password_is_insecure_and_should_be_updated. Our walkthrough on securing the Studio dashboard covers locking that surface down properly.

Bring the stack up

With secrets set and the socket configured, start it the way you always would:

docker-compose up -d

Thanks to podman-docker, that docker-compose command drives Podman under the hood. Check status:

docker-compose ps

You want every service — db, auth, rest, realtime, storage, kong, studio, vector, analytics — reporting healthy. If vector exited, go back to the socket section; that's almost always the cause.

A few Podman-specific things to expect:

  • Volume permissions. Rootless Podman maps container UIDs to subordinate UIDs on the host via /etc/subuid. If a volume shows permission-denied errors, it's usually because those subordinate ranges aren't configured for your user. podman unshare chown on the volume path fixes ownership inside the namespace.
  • Ports below 1024. Rootless containers can't bind privileged ports by default. Bind Kong to something like 8000 and terminate TLS with a reverse proxy in front — which you'd want anyway, and which pairs naturally with a custom domain and free SSL.
  • You don't need every service. The default Compose file runs the whole platform. If you only use the database and auth, trimming the stack (see selective service deployment) cuts memory use and shrinks your attack surface — doubly valuable on the smaller VPS instances from our best VPS providers roundup.

The trade-offs, honestly

Podman buys you real security wins, but it isn't free:

  • The Compose file wasn't written for it. You're reconciling a Docker-first config against a different runtime. The socket variable is the main sharp edge, but subuid mapping and networking quirks can eat an afternoon the first time.
  • Backups get more nuanced. Rootless volumes live under your user's home in namespaced ownership. Your pg_dump and Storage-bucket backup routines need to account for that. Storage files are not included in a database dump either way — a gap we cover in the forgotten piece of self-hosted backups.
  • Fewer people have hit your exact problem. The Docker path is far more trodden. When something breaks on Podman, the answer is more often "read the source" than "find the Stack Overflow thread."

None of this is a reason to avoid Podman. It's a reason to budget the setup time and, more importantly, to make sure your operational layer — backups, restores, secret rotation, domains — doesn't get harder just because you changed runtimes.

Where Supascale fits

Swapping Docker for Podman hardens the runtime. It does nothing for the part that actually costs teams time: backups you have to script and test, secrets you rotate by hand, custom domains and SSL you wire up per project, and OAuth providers you configure by editing env files and restarting containers.

Supascale sits above the container runtime and handles that operational layer for you — automated S3 backups with one-click restore, custom domains with free SSL, a UI for OAuth provider configuration, and a full REST API for automation. It's runtime-agnostic: whether Supabase is running on Docker or rootless Podman underneath, the management workflow is the same. That means you get Podman's containment and the day-two operations without gluing together shell scripts. And it's a one-time purchase for unlimited projects, not a per-project monthly bill — so hardening your stack doesn't come with a recurring tax.

Conclusion

Running self-hosted Supabase on Podman is genuinely practical in 2026. Install podman, podman-docker, and docker-compose; point DOCKER_SOCKET_LOCATION at your rootless Podman socket; replace every default secret; and the standard Compose stack comes up daemonless and rootless. The payoff is a smaller attack surface and no root-owned daemon guarding your production database — a worthwhile trade for many teams, as long as you go in expecting to spend an hour on the socket and subuid details.

Once the runtime is sorted, don't let the operational work undo the security gains. Start with Supascale to keep backups, domains, and auth manageable on top of whatever container engine you choose.

Further Reading