SSD Nodes Learn 8GB de RAM — $66/año
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-02

Cómo alojar Supabase con Docker en un VPS

Ejecuta la pila oficial de Supabase con Docker: cambia los secretos de demostración, entiende sus 14 servicios, calcula la RAM y configura copias de seguridad y actualizaciones.

Qué va a crear

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 archivo .env y ejecuta 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 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 depurar una lista extensa de nombres de contenedores.

  • db es PostgreSQL con las extensiones de Supabase cargadas. Todos los demás servicios se conectan a él. Si este contenedor no funciona correctamente, 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 publica 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 logs, y supavisor es el pooler de conexiones de Postgres.

Por eso los valores de recursos que aparecen a continuación son los que son. No se está ejecutando solo una base de datos. Se está ejecutando una base de datos junto con una docena de servicios auxiliares.

Dimensionamiento: planifique 8 GB de RAM

La pila usa aproximadamente 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 de Studio 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 OOM killer, normalmente analytics o db. El síntoma es un contenedor atascado en un ciclo de reinicios con el código de salida 137.

Asigne 8 GB de RAM y 4 vCPU para cualquier servicio del 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 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.

Instalar: clonar el repositorio oficial

El procedimiento compatible copia el directorio docker del repositorio principal a un directorio de proyecto propio. Esta separación es importante porque evita que un git pull posterior sobrescriba .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ó en el repositorio de origen. Para corregirlo, extraiga una copia más reciente del repositorio en lugar de editar las etiquetas manualmente.

Los secretos que debe cambiar antes del primer inicio

Hágalo antes de iniciar la pila, no después. Varios de estos valores se escriben en los datos durante el primer arranque. Cambiarlos después 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 y los tokens de Logflare en .env. Necesita openssl, que está presente en cualquier imagen normal de Ubuntu.

No establece dos valores. Debe editarlos manualmente en .env:

  • POSTGRES_PASSWORD. Use solo letras y dígitos. La puntuación aquí rompe las cadenas de conexión que varios servicios construyen uniendo 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. 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. Ambos son JWT firmados con JWT_SECRET. El gateway verifica esa firma en cada solicitud. Por eso, 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 siempre.

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 servidor y en ningún otro lugar.

Establezca SITE_URL y API_EXTERNAL_URL en la dirección a la que accederán realmente sus usuarios, por ejemplo, https://supabase.example.com. Auth genera los enlaces de confirmación de correo electrónico y las devoluciones de OAuth a partir de esos valores. Si los deja en http://localhost:8000, todos sus usuarios serán enviados a su propia máquina.

Después, compruebe los valores:

sh run.sh secrets

Inícialo y confirma que está en buen estado

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 las comprobaciones de estado se completan correctamente. Cada servicio debería 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 componente pueda conectarse.

Si un contenedor se reinicia, consulta 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 panel que configuraste.

No exponga el puerto 8000 a Internet pública

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

Coloque un proxy inverso delante de Kong y termine TLS (seguridad de la capa de transporte) allí. Enlace Kong a la dirección de loopback para impedir que cualquier otro servicio acceda 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 solicitudes a ese puerto. Traefik delante de varias aplicaciones de Compose explica la configuración de los certificados.

Cierre también el resto de los puertos en el firewall. Docker publica puertos escribiendo sus propias reglas de iptables, que una configuración ingenua de ufw no detecta. Esta trampa se explica en por qué los contenedores de Docker ignoran las reglas de ufw.

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

Los datos de Postgres se almacenan en un montaje bind 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 solo son coherentes durante un checkpoint. La restauración normalmente funcionará, pero a veces perderá silenciosamente las últimas transacciones. Este es el peor tipo de error posible en una copia de seguridad.

En su lugar, genere un volcado. 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 volcados fuera del servidor según una programación. Para eso sirven las copias de seguridad cifradas fuera del sitio con restic. Haga también una copia de seguridad de .env al mismo tiempo. Si pierde JWT_SECRET, todos los tokens emitidos dejan de ser válidos y todos los secretos cifrados almacenados dejan de poder leerse.

Los archivos subidos se encuentran 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. Cree siempre un volcado antes de actualizar.

docker compose pull
sh run.sh recreate

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

Para aplicar los cambios del archivo Compose, vuelva a clonar el repositorio original y copie su directorio docker sobre el proyecto, con cuidado de no sobrescribir .env.

El restablecimiento completo, que elimina todo incluido el contenido de la base de datos, se realiza mediante 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. La gateway verifica la firma de 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 autohospedado en una VPS de 2 GB?

No de forma fiable. La pila utiliza cerca de 3 GB desde julio de 2026 porque ejecuta unos catorce servicios. Por eso, una máquina de 2 GB pierde contenedores debido al out of memory killer y ves el código de salida 137 en docker compose ps. Usa 8 GB en producción y considera 4 GB como el mínimo para desarrollo individual.

¿Supabase autohospedado 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 de despliegue global de la plataforma alojada. Por tanto, tus funciones se ejecutan en tu único servidor, 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 a él 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 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.