Skip to content

Despliegue con Docker ​

Despliega Capyshop en produccion usando Docker Compose con la imagen de contenedor preconstruida y PostgreSQL con pgvector.

Requisitos Previos ​

  • Docker Engine 20+
  • Docker Compose v2+

1. Crear un Archivo Docker Compose ​

Crea un docker-compose.yml en tu directorio de despliegue:

yaml
services:
  postgres:
    image: pgvector/pgvector:pg17
    restart: always
    environment:
      POSTGRES_USER: capyshop
      POSTGRES_PASSWORD: changeme
      POSTGRES_DB: capyshop
    volumes:
      - postgres_data:/var/lib/postgresql/data

  app:
    image: capyshop/capyshop:latest
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://capyshop:changeme@postgres:5432/capyshop
      - BETTER_AUTH_SECRET=<generate-with-openssl-rand-base64-32>
      - MASTER_SECRET=<generate-with-openssl-rand-base64-32>
      - TRUSTED_ORIGINS=https://mystore.com
      - BASE_URL=https://mystore.com
    volumes:
      - ./data:/app/data
    depends_on:
      - postgres

volumes:
  postgres_data:

Reemplaza los valores de ejemplo:

VariableDescripcion
BETTER_AUTH_SECRETClave secreta para la firma de sesiones. Genera una con openssl rand -base64 32.
MASTER_SECRETSecreto maestro de la aplicacion para cifrado. Genera uno con openssl rand -base64 32.
POSTGRES_USERNombre de usuario de PostgreSQL (debe coincidir en postgres y DATABASE_URL).
POSTGRES_PASSWORDContrasena de PostgreSQL (debe coincidir en postgres y DATABASE_URL).
POSTGRES_DBNombre de la base de datos de PostgreSQL (debe coincidir en postgres y DATABASE_URL).
TRUSTED_ORIGINSLista de origenes confiables separados por comas para proteccion CSRF y redirecciones OAuth (ej., https://mitienda.com).
BASE_URLURL publica de la tienda, usada en correos, SEO, sitemaps (ej., https://mitienda.com).

Opcional: Configuracion de SMTP por Variables de Entorno ​

Por defecto, las credenciales SMTP se gestionan en Configuracion → Email del panel de administracion. Si prefieres configurar el SMTP en el momento del despliegue, puedes definir las siguientes variables de entorno. Cuando estan definidas, sobreescriben los valores del panel y los campos correspondientes en el formulario de Email se deshabilitan.

yaml
environment:
  # ...otras variables
  - SMTP_HOST=smtp.sendgrid.net
  - SMTP_PORT=587
  - SMTP_USER=apikey
  - SMTP_PASSWORD=tu-contrasena-smtp
VariableDescripcion
SMTP_HOSTHostname del servidor de correo (ej., smtp.sendgrid.net).
SMTP_PORTPuerto del servidor. Generalmente 587 para TLS o 465 para SSL. Debe ser entero en [1, 65535].
SMTP_USERNombre de usuario de inicio de sesion SMTP.
SMTP_PASSWORDContrasena de inicio de sesion SMTP. Almacenada en texto plano en el entorno — no pasa por la capa de cifrado de la base de datos.

Cada variable es independiente. Si solo se define SMTP_HOST, los otros tres campos SMTP siguen siendo editables en la interfaz administrativa y se leen de la base de datos.

Opcional: Configuracion de Analitica ​

Ajusta las ventanas de retencion y agregacion de analitica. Todas opcionales — se aplican valores predeterminados razonables si no se definen.

VariableDescripcion
ANALYTICS_RETENTION_DAYSDias para retener eventos brutos de analitica antes de la eliminacion automatica. Predeterminado: 90.
ANALYTICS_AGGREGATE_WINDOW_HOURSHoras anteriores de eventos brutos de analitica que el agregador reexamina en cada ejecucion. Menor = mas barato, mayor = mas tolerante a beacons tardios. Predeterminado: 24.
ANALYTICS_AGGREGATE_INTERVAL_MINUTESFrecuencia de ejecucion del agregador de analitica, en minutos. Debe dividir uniformemente 60 o ser multiplo de 60. Valores comunes: 15, 30, 60, 120, 360. Predeterminado: 60 (cada hora).

Opcional: Ciclo de Vida de la Cuenta ​

VariableDescripcion
INACTIVE_ACCOUNT_DELETION_YEARSAnos de inactividad antes de que una cuenta reciba un aviso de eliminacion (la cuenta se elimina 7 dias despues si sigue inactiva). Predeterminado: 3. Establece en 0 para desactivar.

Opcional: Tests y CI ​

Existen para tests automatizados — dejalas sin definir en produccion.

VariableDescripcion
ENABLE_AUTH_RATE_LIMITEstablece en false para desactivar el limitador de tasa de autenticacion (no recomendado en produccion). Predeterminado: activado.
ENABLE_CRONEstablece en false para mantener el planificador de cronjobs inactivo. Usado para tests E2E. Dejar sin definir en produccion. Predeterminado: activado.

Opcional: Almacenamiento de Assets en S3 ​

Por defecto, las imagenes subidas y otros assets se almacenan en disco local en data/files/. Para enviar las cargas a un bucket compatible con S3 y servirlas desde un CDN, define el modo de almacenamiento como s3 y configura las credenciales del bucket.

yaml
environment:
  # ...otras vars
  - ASSETS_STORAGE_MODE=s3
  - ASSETS_S3_ENDPOINT=https://s3.amazonaws.com
  - ASSETS_S3_REGION=us-east-1
  - ASSETS_S3_BUCKET=my-store-assets
  - ASSETS_S3_ACCESS_KEY_ID=AKIA...
  - ASSETS_S3_SECRET_ACCESS_KEY=...
  - ASSETS_PUBLIC_BASE_URL=https://cdn.mystore.com
  - ASSETS_MAX_BYTES=10gb
VariableDescripcion
ASSETS_STORAGE_MODElocal (predeterminado) o s3. Cuando es s3, las seis vars ASSETS_S3_* y ASSETS_PUBLIC_BASE_URL son obligatorias.
ASSETS_S3_ENDPOINTEndpoint de la API S3 (ej., https://s3.amazonaws.com, o la URL de tu proveedor para Cloudflare R2, Backblaze B2, MinIO, etc.).
ASSETS_S3_REGIONRegion del bucket (ej., us-east-1, auto para R2).
ASSETS_S3_BUCKETNombre del bucket.
ASSETS_S3_ACCESS_KEY_IDAccess key con permisos de lectura/escritura en el bucket.
ASSETS_S3_SECRET_ACCESS_KEYSecret access key.
ASSETS_PUBLIC_BASE_URLURL publica base que la tienda utiliza para cargar los assets (ej., tu dominio CDN apuntando al bucket).
ASSETS_MAX_BYTESLimite cumulativo opcional de almacenamiento entre todos los archivos. Acepta 10gb, 500mb o un recuento de bytes en bruto. Aplica en modo local y s3. Los limites por carga (5 MB imagen / 50 MB video) permanecen sin cambios.

Al migrar una tienda existente de local a s3, ejecuta el script de migracion incluido una vez dentro de un contenedor desplegado para subir los archivos existentes al bucket y pre-generar las variantes WebP:

bash
docker exec -it <container> node build/scripts/migrate-assets-to-s3.mjs

El script es idempotente — volver a ejecutarlo es seguro e ignora el trabajo que ya esta hecho. Si tus originales existentes residen en el host (por ejemplo, /docker/<store>/app_data/files/), monta esa ruta en /app/data/files dentro del contenedor para que el script pueda recogerlos; de lo contrario, recurrira a lo que ya este en el bucket.

2. Iniciar la Aplicacion ​

bash
docker compose up -d

Esto inicia dos servicios:

  • postgres — PostgreSQL 17 con pgvector (pgvector/pgvector:pg17), escuchando en el puerto 5432.
  • app — El contenedor de la aplicacion (capyshop/capyshop:latest), expuesto en el puerto 3000.

Al iniciar, el contenedor de la aplicacion ejecuta automaticamente las migraciones de base de datos (prisma migrate deploy) antes de iniciar el servidor.

3. Verificar ​

bash
# Verificar que ambos contenedores esten corriendo
docker compose ps

# Revisar los logs de la aplicacion
docker compose logs app

La aplicacion deberia ser accesible en http://<tu-host>:3000.

Proxy Inverso ​

Para despliegues en produccion con HTTPS, coloca un proxy inverso (como Traefik, Caddy o nginx) delante de la aplicacion. El proxy inverso se encarga de la terminacion TLS y reenvía el trafico al puerto 3000.

Habilitando Compresion Gzip con Traefik ​

Si utilizas Traefik como proxy inverso, habilita la compresion gzip para reducir el tamano de las respuestas y mejorar la velocidad de carga — un factor en el posicionamiento en motores de busqueda. Agrega las siguientes labels al servicio app en tu docker-compose.yml:

yaml
labels:
  - "traefik.http.middlewares.compress.compress.minresponsebodybytes=256"
  - "traefik.http.routers.STORE_NAME-app.middlewares=compress"

Reemplaza STORE_NAME con el nombre del router Traefik de tu tienda. La primera label define un middleware de compresion que aplica gzip a todas las respuestas mayores a 256 bytes. La segunda label asocia ese middleware al router de tu tienda.

Volumenes ​

Volumen / MontajeProposito
postgres_dataPersiste los datos de PostgreSQL entre reinicios de contenedores.
./data:/app/dataPersiste archivos subidos y datos de la aplicacion entre reinicios.

Compilar desde el Codigo Fuente ​

Si prefieres compilar la imagen tu mismo en lugar de usar la preconstruida:

bash
docker build -t capyshop .

El Dockerfile usa una compilacion multi-etapa:

  1. Instala todas las dependencias y compila la aplicacion.
  2. Copia solo las dependencias de produccion y el resultado de la compilacion a la imagen final.
  3. Ejecuta prisma migrate deploy y luego inicia el servidor en el puerto 3000.

Para usar tu imagen personalizada, actualiza el campo image en tu docker-compose.yml, o ejecuta de forma independiente:

bash
docker run -p 3000:3000 \
  -e DATABASE_URL=postgresql://user:pass@host:5432/db \
  -e BETTER_AUTH_SECRET=your-secret \
  -e MASTER_SECRET=your-secret \
  capyshop

Solucion de Problemas ​

ErrorCausaSolucion
app se cierra inmediatamenteVariables de entorno faltantes o invalidasRevisa docker compose logs app y verifica todas las variables de entorno requeridas
ECONNREFUSED hacia postgresLa aplicacion inicio antes de que la base de datos estuviera listaReinicia la aplicacion: docker compose restart app
P3009 - failed migrationsUna migracion anterior dejo un estado inconsistenteDROP TABLE IF EXISTS _prisma_migrations CASCADE; en la base de datos, luego reinicia
type "vector" does not existLa extension pgvector no fue creadaLa imagen pgvector/pgvector:pg17 la incluye, pero ejecuta CREATE EXTENSION IF NOT EXISTS vector; si usas otra imagen
Puerto 3000 ya en usoOtro proceso esta usando el puertoDetiene el proceso en conflicto o cambia el mapeo de puertos en docker-compose.yml
Archivos subidos perdidos tras reinicioVolumen ./data no montadoAsegurate de que la seccion volumes incluya ./data:/app/data