SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor

Cómo alojar Supabase en un VPS con Docker

Ejecute la pila oficial de Supabase con Docker en su servidor: cambie los secretos de demostración, entienda los 11 servicios, la RAM, las copias y las actualizaciones.

Qué va a configurar

Alojar Supabase por cuenta propia significa ejecutar en su propio servidor la pila oficial de Docker Compose: Postgres, una API REST delante de la base de datos, un servicio de autenticación, almacenamiento de archivos, websockets en tiempo real y el panel de Studio. Clona un repositorio, edita un archivo .env y arranca once contenedores que, en conjunto, funcionan como un proyecto de Supabase bajo su control.

La instalación es breve. El problema suele estar en el archivo .env. Incluye secretos de demostración publicados en el repositorio, y una pila iniciada con esos valores predeterminados queda expuesta a cualquiera que los encuentre. Esta guía explica qué secretos debe reemplazar, para qué sirve cada servicio, cuánta memoria necesita realmente la pila y cómo actualizarla sin eliminar la base de datos.

Si Compose es nuevo para usted, lea primero Conceptos básicos de Docker Compose en un VPS. Todo lo siguiente presupone que docker compose version ya muestra una versión.

Qué contiene realmente la pila

Supabase no es un solo programa. El archivo Compose inicia un conjunto de servicios independientes en una misma red. Saber qué función cumple cada uno permite convertir una lista extensa de nombres de contenedores en algo que se puede depurar.

  • db es PostgreSQL con las extensiones de Supabase cargadas. Todos los demás servicios se conectan a él. Si este contenedor no está saludable, todo lo demás también falla.
  • api-gw es la puerta de enlace de la API. Desde la versión de agosto de 2026 ejecuta Envoy. Antes era un servicio llamado kong, y Kong sigue disponible como sustitución opcional. Escucha en el puerto 8000 y enruta /rest/v1/, /auth/v1/ y /storage/v1/ al backend correspondiente. Es el único contenedor que debe exponer.
  • rest es PostgREST. Lee el esquema de Postgres y lo ofrece como una API REST. Por tanto, una tabla nueva se convierte en un endpoint nuevo sin escribir código.
  • auth es GoTrue. Emite los tokens web JSON (JWT) que identifican a los usuarios.
  • storage y imgproxy gestionan las cargas de archivos y el redimensionamiento de imágenes.
  • realtime transmite los cambios de la base de datos mediante websockets.
  • studio y meta son el panel y la API de administración que lo respalda.
  • functions es el runtime perimetral basado en Deno, y supavisor es el pool de conexiones de Postgres.

Los registros y los análisis (Logflare y vector) dejaron de estar en el archivo Compose predeterminado en junio de 2026. Ahora se encuentran en un archivo de sustitución opcional que se activa con sh run.sh config add logs. Es la misma combinación de archivos Compose descrita en ejecutar una pila mediante varios archivos Compose.

Por eso las cifras de recursos siguientes son las que son. No está ejecutando una base de datos. Está ejecutando una base de datos más diez servicios auxiliares.

Dimensionamiento: planifique 8 GB de RAM

La pila utilizaba aproximadamente entre 2.5 y 3 GB de memoria residente en una instalación nueva en julio de 2026, antes de añadir sus propios datos o tráfico. En esa medición, el servicio de analítica y el proceso Node.js de Studio eran los dos mayores consumidores individuales. Analytics ya no forma parte de la pila predeterminada, por lo que sólo consume memoria si activa la sobrescritura de logs. Un servidor con 2 GB iniciará los contenedores y después el kernel terminará uno mediante el OOM killer. El síntoma es un contenedor atascado en un ciclo de reinicios con el código de salida 137. La documentación de Supabase para instalaciones autogestionadas, revisada en octubre de 2026, indica 4 GB de RAM, 2 núcleos de CPU y 40 GB de SSD como requisitos mínimos. Como configuración recomendada, indica 8 GB, 4 núcleos y 80 GB.

Asigne 8 GB de RAM y 4 vCPU a cualquier instancia de la que dependa. 4 GB son suficientes para una instancia de desarrollo individual si acepta que una consulta pesada y una sesión de Studio simultáneas funcionarán lentamente. El disco también es importante, porque Postgres, el volumen de almacenamiento y, si activa los logs, los datos de logs se encuentran bajo el directorio del proyecto. Empiece con 40 GB y supervise su uso. Contar los servicios antes de elegir un plan es una práctica útil para cualquier software que aloje por su cuenta, porque PhotoPrism e Immich también tienen requisitos mínimos reales de RAM: la página de inicio rápido de Immich, revisada en octubre de 2026, exige al menos 6 GB de RAM y 2 núcleos de CPU. Si todavía necesita menos recursos de servidor para su proyecto, compárelo con lo que incluye realmente el plan gratuito en la nube de Supabase, ya que una aplicación pequeña que se mantenga por debajo de 500 MB y nunca permanezca inactiva durante una semana puede ejecutarse allí sin coste.

Instalación: clone el repositorio oficial

La ruta compatible copia el directorio docker del repositorio principal en un directorio de proyecto propio. Esta separación es importante porque evita que una posterior git pull sobrescriba su .env.

git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/* supabase-project
cp supabase/docker/.env.example supabase-project/.env
cd supabase-project
docker compose pull

docker compose pull descarga varios gigabytes de imágenes. Debe terminar con todos los servicios marcados como Pulled. Un error de manifest unknown indica que la etiqueta de imagen fijada se eliminó del repositorio original. La solución es descargar una copia más reciente del repositorio, no editar las etiquetas manualmente.

Los secretos que debe cambiar antes del primer arranque

Hágalo antes de iniciar el stack, no después. Varios de estos valores se escriben en los datos durante el primer arranque. Cambiarlos más tarde implica restablecer la base de datos.

El repositorio incluye un generador que produce todos los valores correctamente, incluidas las dos claves de API que deben firmarse con su nuevo secreto JWT.

sh utils/generate-keys.sh --update-env

Ese script escribe nuevos valores para JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY, los tokens de Logflare, las claves del protocolo S3 y MINIO_ROOT_PASSWORD en .env. Desde octubre de 2026 también escribe valores aleatorios para POSTGRES_PASSWORD y DASHBOARD_PASSWORD. Necesita openssl, que está presente en cualquier imagen normal de Ubuntu. Como ahora también vuelve a escribir la contraseña de la base de datos, ejecútelo con --update-env sólo antes del primer arranque, nunca contra un stack que ya contenga datos.

Todavía conviene comprobar manualmente dos valores en .env:

  • POSTGRES_PASSWORD. El script lo rellena con hexadecimal aleatorio, lo que es seguro. Si lo sustituye por uno propio, use sólo letras y dígitos. La puntuación aquí rompe las cadenas de conexión que varios servicios construyen concatenando cadenas. El fallo parece un error de autenticación y no un error de análisis, por lo que la investigación suele dirigirse al lugar equivocado.
  • DASHBOARD_USERNAME y DASHBOARD_PASSWORD. Son las credenciales de autenticación básica de Studio. El script establece una contraseña aleatoria, pero mantiene el nombre de usuario predeterminado supabase. La contraseña predeterminada incluida es literalmente this_password_is_insecure_and_should_be_updated. Si omitió el script, cámbiela ahora.

Debe entender por qué ANON_KEY y SERVICE_ROLE_KEY no se pueden inventar. Ambos son JWT firmados con JWT_SECRET. El gateway sólo acepta las claves exactas de .env y los servicios situados detrás verifican la firma con JWT_SECRET. Por tanto, una clave que no coincida se rechaza con HTTP 401. El gateway Envoy predeterminado responde 401 Unauthorized. La configuración de Kong actual también responde {"message":"Unauthorized"}; ejecuta Kong 3.9. Las instalaciones anteriores a marzo de 2026 ejecutaban Kong 2.8, que responde {"message":"Invalid authentication credentials"}. Este es el fallo más habitual en instalaciones autogestionadas: el operador cambió JWT_SECRET, pero conservó las claves de demostración. Genere las tres juntas, siempre.

Trate SERVICE_ROLE_KEY como una contraseña de root. Omite por completo la seguridad a nivel de fila. Debe estar en código del lado del servidor y en ningún otro sitio.

Establezca SITE_URL en la dirección de su aplicación y API_EXTERNAL_URL en la dirección pública de la API, con /auth/v1 al final; por ejemplo, https://supabase.example.com/auth/v1. Establezca SUPABASE_PUBLIC_URL en la misma dirección pública sin la ruta. Auth construye con esos valores los enlaces de confirmación de correo electrónico y de retorno de OAuth. Si los deja con sus valores predeterminados de localhost, todos sus usuarios serán enviados a su propia máquina.

Después, compruebe lo que tiene:

sh run.sh secrets

Inícielo y confirme que está operativo

sh run.sh start
docker compose ps

run.sh start envuelve docker compose up -d --wait, por lo que no devuelve el control hasta que se superan las comprobaciones de estado. Todos los servicios deben mostrar running (healthy) o running. El primer arranque tarda entre dos y cuatro minutos porque Postgres ejecuta sus scripts de inicialización antes de que cualquier otro servicio pueda conectarse.

Si un contenedor se reinicia continuamente, consulte sus registros por nombre de servicio:

docker compose logs db
docker compose logs auth

Studio estará disponible en el puerto 8000 y solicitará el nombre de usuario y la contraseña del dashboard que haya configurado.

No exponga el puerto 8000 a Internet

La puerta de enlace en 8000 utiliza HTTP sin cifrar. Todas las claves de API y todas las contraseñas de usuario atraviesan la red en texto claro. Además, las credenciales de Studio usan autenticación básica, que es una codificación base64 y no cifrado.

Coloque un proxy inverso delante, termine allí TLS (seguridad de la capa de transporte) y vincule la puerta de enlace a la dirección de loopback para que ningún otro proceso pueda acceder a ella. En docker-compose.yml, la línea del puerto bajo el servicio api-gw pasa a ser 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp, y el proxy reenvía las peticiones a ese puerto. En una instalación anterior al cambio a Envoy de agosto de 2026, la misma línea del puerto se encuentra bajo un servicio llamado kong. Traefik delante de varias aplicaciones de Compose explica la parte de los certificados. Ese mismo proxy termina atendiendo todo lo demás del servidor, desde esta pila hasta algo tan poco serio como una biblioteca de Jellyfin reconstruida como un videoclub de los años 90, y cada servicio necesita un nombre de host en lugar de otro puerto abierto. Si un panel es sólo para usted, omita el proxy y acceda al puerto de loopback mediante un túnel SSH. Es el mismo enfoque que utiliza self-hosted open-kritt para mantener por completo su interfaz de análisis fuera de Internet.

Cierre también el resto de los puertos en el firewall. Docker publica puertos escribiendo sus propias reglas de iptables, que una configuración de ufw sin ajustes adicionales no llega a controlar. por qué los contenedores de Docker ignoran las reglas de ufw explica este problema.

Cree una copia de seguridad de la base de datos, no del directorio

Los datos de Postgres se almacenan en un bind mount en ./volumes/db/data. Copiar ese directorio mientras el contenedor está en ejecución produce una copia inconsistente, porque Postgres almacena las escrituras en búfer y los archivos del disco sólo son coherentes en un checkpoint. La restauración normalmente funcionará, pero a veces perderá silenciosamente las últimas transacciones. Este es el peor tipo de fallo posible en una copia de seguridad.

En su lugar, genere un dump. pg_dumpall se ejecuta dentro del contenedor y produce una instantánea coherente:

docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql

Compruebe que el archivo no esté vacío antes de confiar en él. Después, transfiera esos dumps fuera del servidor de forma programada. Para eso sirven las copias de seguridad cifradas fuera del sitio con restic. Un dump programado que falla silenciosamente equivale a no tener ninguna copia de seguridad. Configure el trabajo de cron o systemd para que envíe una alerta al teléfono cuando termine con un código distinto de cero. Haga una copia de seguridad de .env al mismo tiempo. Si pierde JWT_SECRET, todos los tokens emitidos dejan de ser válidos y ningún secreto cifrado almacenado puede volver a leerse.

Los archivos subidos se almacenan en ./volumes/storage. Son archivos normales, por lo que una copia simple es suficiente.

Actualizar sin perder datos

Supabase fija las versiones de las imágenes en docker-compose.yml, por lo que nada cambia hasta que usted lo actualiza. Conviene aplicar este mismo principio de fijación de versiones en cualquier stack que monte manualmente. Por eso un relay de RustDesk autohospedado fija sus dos imágenes de servidor en lugar de seguir un tag mutable: una actualización debe hacerse cuando usted decida que tiene tiempo para ella. Cree siempre un volcado antes de actualizar.

docker compose pull
sh run.sh recreate

recreate detiene el stack y lo inicia de nuevo con las imágenes nuevas. Sus datos se conservan porque están en los bind mounts del host, no dentro de los contenedores. Consulte CHANGELOG.md en el repositorio antes de saltar a una versión mayor, ya que una actualización mayor de Postgres es un paso independiente que recreate no realiza. Postgres 17 es la versión predeterminada desde June 2026, y una instalación existente con Postgres 15 se actualiza a esa versión con sudo bash utils/upgrade-pg17.sh, que usa pg_upgrade para migrar los datos en el mismo lugar y solicita confirmación en cada paso mayor. Necesita que todos los contenedores estén en ejecución y disponer de espacio libre equivalente, como mínimo, al doble del tamaño de la base de datos más 5 GB. Supabase sigue recomendando crear una copia de seguridad independiente antes de empezar.

Para aplicar los cambios realizados en el archivo Compose, ejecute sh update.sh desde el directorio del proyecto, o sh update.sh --dry-run para comprobar primero qué cambiaría. El comando combina los archivos originales con sus modificaciones. Nunca sobrescribe los valores existentes en .env; sólo añade claves nuevas. También archiva la configuración en backups/ antes de escribirla, pero no la base de datos. Una instalación sin un archivo .supabase-version necesita --from y la versión desde la que se inició. Esto es especialmente importante para la versión de August 2026, que sustituyó el servicio kong por api-gw y requiere una actualización de docker-compose.yml. El procedimiento manual también funciona: vuelva a clonar el repositorio original y copie su directorio docker sobre el proyecto, con cuidado de no sobrescribir .env.

El restablecimiento completo destruye todo, incluida la base de datos, y devuelve .env a sus valores predeterminados. La configuración anterior se conserva como .env.old. Es un script independiente y solicita confirmación:

sh reset.sh

FAQ

¿Por qué mis llamadas a la API devuelven «Invalid authentication credentials»?

Ese mensaje exacto procede de Kong 2.8, la puerta de enlace incluida en la pila de las instalaciones anteriores a marzo de 2026. La puerta de enlace Envoy predeterminada y la sustitución actual de Kong, que ejecuta Kong 3.9, informan del mismo problema como un 401 simple Unauthorized. En cualquier caso, la clave enviada no coincide con ANON_KEY o SERVICE_ROLE_KEY en .env, o esas claves no se firmaron con JWT_SECRET que está actualmente en .env. En una pila que nunca ha tenido datos, regenere las tres con sh utils/generate-keys.sh --update-env. En una pila en ejecución, ejecute el script sin la opción y responda no, para que sólo muestre valores nuevos; copie JWT_SECRET, ANON_KEY y SERVICE_ROLE_KEY manualmente en .env y no modifique POSTGRES_PASSWORD. Después, ejecute sh run.sh recreate para que los servicios lean los valores nuevos.

¿Puedo ejecutar Supabase autohospedado en un VPS de 2 GB?

No de forma fiable. La documentación oficial de autohospedaje de Supabase indica que 4 GB de RAM es el mínimo desde octubre de 2026, y la pila permanecía cerca de 3 GB en julio de 2026 según una medición que incluía los servicios de registro, que ahora son opcionales. Un servidor de 2 GB pierde contenedores debido al asesino de procesos por falta de memoria, y verá el código de salida 137 en docker compose ps. Use 8 GB en producción y considere 4 GB como el mínimo para desarrollo individual.

¿Supabase autohospedado incluye funciones edge?

Sí. El archivo Compose incluye el runtime de funciones basado en Deno y sirve todo lo que coloque en ./volumes/functions. No incluye la red de despliegue global de la plataforma alojada, por lo que sus funciones se ejecutan en un único servidor y una única ubicación.

¿Cómo me conecto directamente a la base de datos Postgres?

Use docker exec -it supabase-db psql -U postgres para abrir un shell interactivo en el propio servidor. Para un cliente externo, conéctese a través de Supavisor en el puerto 5432 con el usuario postgres.<POOLER_TENANT_ID> y su POSTGRES_PASSWORD. No exponga ese puerto a Internet. Acceda mediante una VPN o un túnel SSH.

¿Por qué los correos de confirmación de autenticación enlazaban a localhost?

SITE_URL y API_EXTERNAL_URL en .env conservaban sus valores predeterminados. El servicio de autenticación genera todos los enlaces de confirmación y restablecimiento de contraseña a partir de esos dos valores, por lo que envía la dirección que se le indicó. Establezca ambos en su URL pública real y vuelva a crear la pila.