SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-27

Cómo alojar Supabase en un VPS con Docker

Ejecuta la pila oficial de Supabase con Docker en tu VPS: reemplaza los secretos de demostración, entiende sus 14 servicios, calcula la RAM y actualiza sin perder datos.

Qué va a implementar

Alojar Supabase por cuenta propia significa ejecutar la pila oficial de Docker Compose en su propio servidor: 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 único archivo .env e inicia unos catorce 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 que sigue 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 pueda depurar.

  • db es PostgreSQL con las extensiones de Supabase cargadas. Todos los demás servicios se conectan a él. Si este contenedor no está en buen estado, todo lo demás también falla.
  • kong es la puerta de enlace de la API. Escucha en el puerto 8000 y dirige /rest/v1/, /auth/v1/ y /storage/v1/ al backend correspondiente. Es el único contenedor que debe exponerse.
  • rest es PostgREST. Lee el esquema de Postgres y lo ofrece como una API REST. Por eso, una tabla nueva se convierte en un endpoint nuevo sin escribir código.
  • auth es GoTrue. Emite los JSON Web Tokens (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 dashboard y la API de administración que lo respalda.
  • analytics (Logflare) y vector recopilan los registros. supavisor es el pool de conexiones de Postgres.

Por eso los valores de recursos que aparecen a continuación son los que son. No está ejecutando una base de datos. Está ejecutando una base de datos y una docena de servicios auxiliares.

Dimensionamiento: planifique 8 GB de RAM

La pila utiliza entre 2.5 y 3 GB de memoria residente en reposo en una instalación nueva, a fecha de julio de 2026, antes de añadir sus propios datos o tráfico. El servicio de análisis y el proceso Studio de Node.js son los dos mayores consumidores individuales. Un servidor con 2 GB iniciará los contenedores y después el kernel terminará uno mediante el asesino de procesos por falta de memoria, normalmente analytics o db. El síntoma es un contenedor atascado en un ciclo de reinicios con código de salida 137.

Asigne 8 GB de RAM y 4 vCPU para 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 serán lentas. El disco también importa, porque Postgres, el volumen de almacenamiento y los datos de registro se encuentran bajo el directorio del proyecto. Empiece con 40 GB y supervise el uso. Contar los servicios antes de elegir un plan es un hábito útil para cualquier aplicación que aloje por su cuenta, ya que PhotoPrism e Immich tienen unos requisitos mínimos de RAM muy superiores a los que sugieren sus páginas de inicio rápido.

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 la pila, no después. Varios de estos valores se escriben en los datos durante el primer arranque, por lo que 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

El script escribe valores nuevos para JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY y los tokens de Logflare en .env. Necesita openssl, que está presente en cualquier imagen normal de Ubuntu.

Hay dos valores que no establece y que debe editar manualmente en .env:

  • POSTGRES_PASSWORD. 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 orientarse en la dirección equivocada.
  • DASHBOARD_USERNAME y DASHBOARD_PASSWORD. Son las credenciales de autenticación básica de Studio. La contraseña predeterminada incluida es literalmente this_password_is_insecure_and_should_be_updated.

Entienda por qué no puede inventar ANON_KEY y SERVICE_ROLE_KEY. Ambas son JWT firmadas con JWT_SECRET. El gateway verifica esa firma en cada petición, por lo que una clave que no coincide con su secreto se rechaza con {"message":"Invalid authentication credentials"}. Este es el fallo más común en instalaciones autogestionadas: el operador cambia JWT_SECRET, pero conserva las claves de demostración. Genere las tres juntas y siempre de forma simultánea.

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

Establezca SITE_URL y API_EXTERNAL_URL en la dirección a la que realmente accederán sus usuarios, por ejemplo https://supabase.example.com. Auth construye con esos valores los enlaces de confirmación de correo electrónico y de retorno de OAuth. Si los deja en http://localhost:8000, 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

Kong usa HTTP sin cifrar en el puerto 8000. Cada clave de API y cada contraseña de usuario atraviesa la red en texto claro. Las credenciales de Studio usan autenticación básica, que es una codificación base64 y no cifrado.

Coloque un reverse proxy delante de Kong y termine allí TLS (seguridad de la capa de transporte). Vincule Kong a la dirección de loopback para que ningún otro servicio pueda acceder a él. En docker-compose.yml, la asignación de puertos kong se convierte en 127.0.0.1:8000:8000, y el proxy reenvía las peticiones a ese puerto. Traefik delante de varias aplicaciones de Compose explica la configuración de certificados. Ese mismo proxy terminará delante del resto de servicios del servidor, desde este stack hasta algo tan poco serio como una biblioteca de Jellyfin reconstruida como un videoclub de los 90, y cada uno necesita un nombre de host en lugar de otro puerto abierto. Si un panel sólo lo va a usar usted, omita el proxy y acceda al puerto de loopback mediante un túnel SSH. Es el mismo enfoque que usa self-hosted open-kritt para mantener su interfaz de análisis completamente fuera de Internet.

Cierre también el resto de puertos en el firewall. Docker publica puertos escribiendo sus propias reglas de iptables, que una configuración de ufw sin ajustes adicionales no detecta. 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 bloqueo 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 una etiqueta mutable: una actualización debe hacerse cuando usted la programe y disponga de tiempo. Haga primero un volcado, siempre.

docker compose pull
sh run.sh recreate

recreate detiene la pila y la inicia de nuevo con las imágenes nuevas. Los datos se conservan porque están en los montajes bind del host, no dentro de los contenedores. Lea CHANGELOG.md en el repositorio antes de realizar un salto de versión mayor, ya que las actualizaciones mayores de Postgres no son automáticas y requieren un volcado y una restauración.

Para incorporar cambios en el archivo Compose, vuelva a clonar el repositorio original y copie su directorio docker sobre el proyecto. Tenga cuidado de no sobrescribir .env.

El restablecimiento completo, que destruye todo, incluida la base de datos, se realiza con un script independiente que solicita confirmación:

sh reset.sh

FAQ

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

Tu ANON_KEY o SERVICE_ROLE_KEY no se firmó con el JWT_SECRET que está configurado actualmente en .env. El gateway verifica la firma en cada solicitud y rechaza las discrepancias. Regenera los tres valores juntos con sh utils/generate-keys.sh --update-env y ejecuta sh run.sh recreate para que los servicios lean los valores nuevos.

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

No de forma fiable. La pila consume cerca de 3 GB en reposo desde julio de 2026 porque ejecuta unos catorce servicios. Por eso, un servidor de 2 GB pierde contenedores debido al OOM killer y aparece el código de salida 137 en docker compose ps. Usa 8 GB en producción y considera 4 GB el mínimo para desarrollo individual.

¿Supabase autogestionado incluye edge functions?

Sí. El archivo Compose incluye el runtime de funciones basado en Deno y sirve todo lo que coloques en ./volumes/functions. No incluye la red global de despliegue de la plataforma alojada. Por tanto, tus funciones se ejecutan en tu único servidor y en una sola ubicación.

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

Usa docker exec -it supabase-db psql -U postgres para abrir un shell interactivo en el propio servidor. Para un cliente externo, conéctate a través de Supavisor en el puerto 5432 con el usuario postgres.<POOLER_TENANT_ID> y tu POSTGRES_PASSWORD. No expongas ese puerto a Internet. Accede 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 crea cada enlace de confirmación y restablecimiento de contraseña a partir de esos dos valores. Por eso envía la dirección que tiene configurada. Establece ambos valores con tu URL pública real y vuelve a crear la pila.