Guides / Infrastructure

The install takes fifteen minutes. The job it hands you does not end.

Supabase's own documentation says Docker is the easiest way to self-host and should take you under fifteen minutes, and that is true. The same page then lists what becomes yours the moment it works: server provisioning and maintenance, security hardening and OS updates, service configuration, Postgres maintenance, high availability and scalability, backups and disaster recovery, and monitoring and uptime. Self-hosted Supabase also mimics a single project, so branching, managed backups, point-in-time recovery, advanced metrics, ETL, and the platform management API are simply not there, and support is the community rather than a ticket queue. In the twelve weeks to the end of July 2026, Supabase announced five breaking changes that apply only to self-hosted instances, each needing a human to act. This guide is the honest version of that trade: when self-hosting is the right call, what the treadmill costs, the hardening list to finish before real users arrive, and the exact commands to move an existing project across.

The fifteen-minute install is not the job.

Every self-hosting decision goes wrong in the same place: the install works. One command fetches the stack, generates secrets, and starts a dozen containers, Studio comes up on port 8000, and the conclusion feels earned. But read the rest of the page you just followed. Supabase states plainly that when you self-host you are responsible for server provisioning and maintenance, security hardening and keeping the OS and services updated, service configuration and management, Postgres database maintenance, high availability and scalability, backups and disaster recovery, and monitoring and uptime. That is not a warning label; it is a job description, and it is the one you accepted.

Read the feature list just as closely, because self-hosting is not the managed platform with the invoice removed. In Supabase's words, self-hosted Supabase mimics a single project, and Studio does not support multiple organizations or projects. Platform-only features named as unavailable include branching, advanced metrics beyond logs, managed backups and point-in-time recovery, analytics and vector buckets, ETL, and the platform management API. Each absence has a practical cost. No branching and no second project means a staging environment is a second full stack on a second box, which also removes the cheapest safety trick for AI coding agents, the disposable branch database from our agent guardrails guide. No managed backups and no point-in-time recovery means the recovery window you thought you had is now something you build, test, and prove yourself, per the backups guide. And self-hosted Supabase is community-supported: when a container will not start at eleven at night, your escalation path is GitHub Discussions and Discord.

The prerequisites are stated just as clearly, and they are the real filter. The Docker guide assumes you are comfortable with Linux server administration basics, git, Docker and Docker Compose, and networking fundamentals such as ports, DNS, and firewalls, and it tells readers who are new to those topics to start with the managed platform instead. Sizing is published too: four gigabytes of RAM and two cores as the minimum, eight gigabytes and four cores recommended, forty gigabytes of SSD minimum and eighty or more recommended. Enabling logs and analytics, which are off by default, pushes that higher.

So self-host for the reasons Supabase itself gives: you need full control over your data, you have a compliance requirement that prevents you from using managed services, or you need an isolated environment. Those are real, and a client security questionnaire or a contractual data-residency clause is a specific, checkable version of them. What is not a reason is a vague feeling that local hosting is more compliant. POPIA does not require you to keep South Africans' data in South Africa; section 72 permits transborder flows on conditions our POPIA guide works through, and either way accountability stays with you rather than moving to whoever runs the server. The other bad reason is saving money. Price the server at the recommended size, then add the hours: a monthly release to apply, breaking changes to read and act on, TLS, SMTP, backups with restore drills, monitoring, and the night the disk fills. Compare that honestly against a managed plan before you decide the platform was the expensive option.

Self-hosting is a subscription to breaking changes.

This is the part no tutorial covers, because tutorials describe day one and this is every month after it. Between the middle of May and the middle of July 2026, Supabase's changelog carried five breaking changes aimed specifically at self-hosted instances. On the managed platform these would have been someone else's migration. Here, each one is a task with your name on it:

  1. Analytics and Vector became opt-in, 3 June 2026. Logflare and Vector are no longer part of the default stack, which lowers the memory floor but silently removes the Logs Explorer. If you want logs, you now layer the optional compose file in yourself with sh run.sh config add logs before starting.
  2. The default Postgres image moved from 15 to 17, 17 June 2026. The changelog is explicit that Postgres 15 data does not auto-upgrade: you run the upgrade script, or you pin supabase/postgres:15.x and stay where you are. Pulling the newer image and hoping is the failure mode this note exists to prevent.
  3. Studio and postgres-meta switched from supabase_admin to postgres, 17 June 2026. Existing instances have to run utils/reassign-owner.sh to move ownership. This one is worth understanding as a pattern: Supabase removed superuser access from the dashboard on the hosted platform in late 2022, and its own documentation says that migration was never automatically applied to self-hosted instances, so self-hosters inherited four years of divergence and then a script to close it.
  4. API_EXTERNAL_URL now includes /auth/v1, week of 6 July 2026. A one-line default change with a real consequence: anyone using SAML single sign-on has to repoint their identity provider at the new /auth/v1/sso/saml/* endpoints. Miss it and enterprise sign-in breaks for exactly the customers who asked you to self-host.
  5. Envoy replaced Kong as the default API gateway on 11 August 2026. The newest one, shipped in self-hosted release v0.8.0. The kong service is now api-gw, a new API_GW_HTTP_PORT falls back to KONG_HTTP_PORT, and the bundled Caddy and nginx reverse proxies now forward to api-gw. If you rely on Kong's HTTPS listener or a custom kong.yml, opt back in with sh run.sh config add kong, which is precisely the set of things a production instance tends to have grown.

The cadence around those changes matters as much as the changes. Supabase publishes stable releases of the Docker Compose setup roughly once a month, with the versions in a release tested together, which is why they can lag the newest images on Docker Hub. You can pin individual service images to newer tags, and the documentation says in as many words that compatibility is then not guaranteed. Applying an update means stopping and starting services, and the docs note this may result in downtime for your applications and users. There is no maintenance window that someone else takes for you. Since release v0.7.1 on 3 August 2026 the repository ships an update.sh helper with a version-keyed upgrades.json manifest that gates breaking changes, and a .supabase-version stamp written by setup.sh, so an instance installed before then has no recorded base version for the tooling to reason from.

The operational answer is unglamorous and works. Subscribe to the self-hosted changelog and the repository's discussions, and treat them as inbound work rather than reading. Book a monthly maintenance window before you need one, and announce it, so applying a release is routine rather than an incident. Keep a second stack to rehearse on, because with no branching that is the only place a risky upgrade can fail safely. And trust your instance over any document about your instance: during the Postgres move, pages on either side of the change described different defaults, so ask the database what it is running before you plan around a version.

select version();

The list to work through before real users arrive.

The defaults are tuned for a laptop demo, not for the internet. Work through these in order; the first four are the difference between a private stack and an open one.

  1. Never start on the example secrets. The documentation puts it bluntly: placeholder passwords and keys ship in .env.example and you should never start your self-hosted Supabase using those defaults. Either use the Linux quick-start script, which generates every secret plus a random dashboard password and the asymmetric JWT signing key pair for you, or run sh utils/generate-keys.sh and then sh utils/add-new-auth-keys.sh by hand. Confirm what was actually set with sh run.sh secrets, and note the naming: the publishable key belongs in the browser and the secret key never does.
  2. Put TLS in front of it. Supabase is served over plain HTTP by default, and the documented approach for production is a reverse proxy such as Caddy or Nginx terminating TLS in front of the API gateway. This is not optional once OAuth providers are involved, and it is the same proxy you will use for the next item.
  3. Lock down Studio. The dashboard is protected by HTTP basic authentication, which is one shared credential with no roles, no multi-factor option, and no audit trail. A password must be set before you start Supabase, and it must contain at least one letter. Treat basic auth as a doormat rather than a door: restrict the dashboard by IP or put it behind your VPN at the proxy, and give nobody a reason to type that password over an untrusted network.
  4. Keep Postgres off the public internet. By default the database is reachable only through the Supavisor pooler. Exposing port 5432 directly means disabling Supavisor and adding a port mapping, and the documentation's own warning is that doing so bypasses pooling and exposes your database to the network, so restrict it to trusted IPs with firewall or network rules. Most apps never need this.
  5. Get the secrets off the box. Everything lives in one .env file by default, and Supabase strongly recommends a secrets manager for production deployments, naming Doppler, Infisical, Azure Key Vault, AWS and GCP Secrets Manager, and HashiCorp Vault. There is a second reason beyond server compromise: a plaintext .env beside your code is exactly the file an AI coding agent reads on its way to solving an unrelated problem, which is rule two of the guardrails guide.
  6. Match the platform's ownership model. Run utils/reassign-owner.sh, set POSTGRES_USER_READ_WRITE to postgres for the studio service and PG_META_DB_USER to postgres for meta, recreate, then confirm the dashboard is no longer acting as a superuser:
    select current_user;
    -- expected: postgres
    Doing this early also avoids the class of migration that works on the hosted platform and fails on yours.
  7. Row Level Security is still entirely your problem. Nothing about running the stack yourself changes the fundamentals: the publishable key is public by design, and a table without policies is readable by anyone who has it. Run the one-command exposure check from the RLS guide against your own domain, and expect the same answer you would have got on the managed platform. Self-hosting moves the server, not the security model.
  8. Build the backups and the monitoring, because nothing else will. Managed backups and point-in-time recovery do not exist here. A nightly job that dumps roles, schema, and data, plus the storage volume, to somewhere that is not this server is the minimum:
    supabase db dump --db-url "$DB_URL" -f roles.sql --role-only
    supabase db dump --db-url "$DB_URL" -f schema.sql
    supabase db dump --db-url "$DB_URL" -f data.sql --use-copy --data-only
    tar czf storage.tar.gz volumes/storage
    A backup you have never restored is a hypothesis, so schedule the restore drill from the backups guide onto the spare stack you already need for upgrades. Then close the observability gap: logs are opt-in, so either enable them and pay the memory, or ship logs off-box, and add an external uptime check because no one is paging you. Email needs a production SMTP server of your own, configured through the SMTP_ variables.

Moving an existing project onto your own server.

The database part is well documented and genuinely straightforward. Take three dumps through the Supabase CLI rather than raw pg_dump, because the CLI excludes internal schemas, strips reserved roles, and adds idempotent clauses; raw pg_dump output includes Supabase internals and causes permission errors on restore.

supabase db dump --db-url "$PLATFORM_URL" -f roles.sql --role-only
supabase db dump --db-url "$PLATFORM_URL" -f schema.sql
supabase db dump --db-url "$PLATFORM_URL" -f data.sql --use-copy --data-only

Restore into the self-hosted instance in one transaction, with triggers disabled for the data load so that things like encrypted columns are not processed twice:

psql --single-transaction \
  --variable ON_ERROR_STOP=1 \
  --file roles.sql \
  --file schema.sql \
  --command 'SET session_replication_role = replica' \
  --file data.sql \
  --dbname "$SELF_HOSTED_URL"

Then verify before you believe it: \dt public.* for the tables, select count(*) from auth.users; for the accounts, and select * from pg_extension; against the same query on the old project, since any non-default extension has to be enabled on the new instance first.

What the dump does not carry is the part that bites. Your schema, data, roles, RLS policies, functions, triggers, and auth.users all come across. JWT secrets and API keys, auth provider settings, edge functions, storage objects, SMTP configuration, and DNS do not. The user-visible consequence is worth planning as an event rather than discovering as an outage: JWT secrets differ between the platform and your instance, so tokens issued by the old project stop being valid and every signed-in user has to authenticate again. OAuth redirect URLs in Google, Apple, and GitHub consoles need repointing away from the platform hostname at the same moment. If you are coming from Lovable Cloud, the migration guide covers the export side and the password-reset choreography that goes with it.

Rehearse the restore on a throwaway stack first, because version skew is the usual failure. A managed project running Postgres 17 can emit SET transaction_timeout = 0, which fails on an older self-hosted image, and COPY blocks for tables newer Auth and Storage versions have but yours does not, such as auth.oauth_clients, storage.buckets_vectors, and storage.vector_indexes. The documented technique is to run the restore without --single-transaction first to collect every failure at once, comment out the offending statements, then do the real run with the transaction back on. Keeping your self-hosted version current is what stops this list from growing.

You are not saving money, you are changing who is on call Self-hosting is the right answer when control or a compliance requirement makes it the only answer, and a bad one when the goal is a smaller invoice. The install is fifteen minutes; the monthly release, the breaking changes, the backups nobody takes for you, and the restore you have to prove are permanent. Decide with that column filled in.

Questions

Questions founders ask.

Is self-hosting Supabase actually cheaper?

Rarely, once the true costs are on the page. Supabase's documented sizing for all components is four gigabytes of RAM and two cores minimum, eight gigabytes and four cores recommended, with forty to eighty gigabytes of SSD, and enabling logs and analytics raises it. Add somewhere to keep off-server backups. Then add the hours, which is where the money actually goes: a monthly release to apply, breaking changes to read and act on, TLS certificates, an SMTP provider, monitoring, restore drills, and the incidents. You also give up managed backups and point-in-time recovery, so the first serious data loss is billed in downtime rather than rands. Self-host for control or compliance, not for the invoice.

Does self-hosting make my app more secure or more POPIA compliant?

Not by itself, and the defaults start behind the managed platform rather than ahead of it: plain HTTP until you add a reverse proxy, placeholder secrets in the example environment file that you are told never to start with, a dashboard protected only by HTTP basic authentication, and every patch depending on you. POPIA does not require South African data to stay in South Africa; section 72 allows transborder flows on conditions, and accountability stays with you whichever way you host. Self-hosting is the right tool for a specific requirement such as a contractual data-residency clause, an air-gapped environment, or a client security questionnaire that rules out shared infrastructure. It is not a compliance shortcut.

What exactly do I lose compared with the managed platform?

Supabase names the gaps: self-hosted mimics a single project, Studio does not support multiple organizations or projects, and branching, advanced metrics beyond logs, managed backups and point-in-time recovery, analytics and vector buckets, ETL, and the platform management API are unavailable. Practically, staging becomes a second complete stack on another server, the disposable branch database that keeps AI coding agents away from production is gone, and the recovery window becomes whatever you build and test. Support becomes the community, through GitHub Discussions, GitHub Issues, and Discord, rather than a ticket with someone on the other end.

Can I let an AI coding agent set up and maintain my self-hosted stack?

For the first install, with supervision; for maintenance, not on its own. Self-hosting is the worst possible fit for a tool that answers from memory, because the details change monthly: five self-hosted breaking changes were announced in the twelve weeks to mid-July 2026 alone. Supabase's own agent benchmark, published on 31 July 2026, found that its skills earned their keep mainly in edge cases where models need to unlearn outdated pre-training knowledge, and that one popular harness read the Supabase docs in under forty per cent of scenarios even with those skills loaded. Point the agent at the current changelog and the release diff rather than letting it recall a gateway or Postgres version, and bound what it can touch using our AI coding agent guardrails guide.

How do I move a managed Supabase or Lovable Cloud project to my own server?

Dump roles, schema, and data separately with the Supabase CLI rather than raw pg_dump, then restore with psql in a single transaction with session_replication_role set to replica, and verify table counts, user counts, and extensions against the old project. Plan for what the dump leaves behind: JWT secrets and API keys, auth provider settings, edge functions, storage objects, SMTP configuration, and DNS. Because JWT secrets differ, tokens issued by the old project stop working and every user must sign in again, so schedule that as an announced event. Rehearse the whole restore on a throwaway instance first, since version skew between a Postgres 17 platform project and an older self-hosted image is the usual source of failures.

Before you take on the pager

Have someone check the plan before your users depend on it.

A production-readiness review tells you whether self-hosting is the right call for your app and your team, and if you are already running it, what is missing: TLS and dashboard exposure, secret handling, backups you have actually restored, the upgrade path you can survive, and whether Row Level Security holds. Ranked by the engineers who would do the work.