Esta guía cubre el despliegue completo de Vitality Gym usando Dokploy , incluyendo todos los servicios: Backend (API), Frontend (App), Base de Datos (PostgreSQL), Cache (Redis), y Email (BillionMail).
VPS con mínimo 2GB RAM, 2 CPU, 40GB SSD
Dominio configurado (ej: vitality-gym.com)
Dokploy instalado en el servidor
curl -sSL https://dokploy.com/install.sh | sh
Accede al panel en http://tu-ip:3000 y configura tu cuenta admin.
Los siguientes puertos son utilizados por los servicios:
Puerto Servicio Descripción 3000API Backend Hono/Bun 4321App Frontend Astro 5432PostgreSQL Base de datos 6379Redis Cache 25, 587, 993BillionMail SMTP/IMAP
# Verificar si un puerto está en uso
netstat -tulpn | grep :5432
# Ver todos los puertos en uso
Puertos Ocupados
Si un puerto está ocupado, puedes cambiarlo en Dokploy:
Ir al servicio → Ports
Cambiar el Host Port (el puerto del contenedor puede mantenerse igual)
Actualizar las variables de entorno que referencien ese puerto
Por ejemplo, si el puerto 5432 está ocupado por otra instancia de PostgreSQL:
Cambia el Host Port a 5433
Actualiza DATABASE_URL a usar ...@database:5433/...
subgraph Project["Proyecto: vitality-gym"]
En Dokploy, ir a Projects → Create Project
Nombre: vitality-gym
Descripción: Sistema de gestión de gimnasio
Dentro del proyecto, click en Create Service → Database → PostgreSQL
Configurar:
Name: database
Image: postgres:17
Database Name: vitality_db
Username: vitality_user
Password: (genera una contraseña segura)
En Advanced → Volumes , agregar:
/var/lib/postgresql/data → vitality-db-data
En Environment , agregar:
Click Deploy
Create Service → Database → Redis
Configurar:
Name: redis
Image: redis:7
En Advanced → Volumes :
/data → vitality-redis-data
Click Deploy
Create Service → Compose
Nombre: billionmail
Pegar la configuración de BillionMail según su documentación
Configurar los dominios y DNS records
Una vez configurado, tendrás credenciales SMTP:
SMTP_HOST = mail.vitality-gym.com
SMTP_USER = noreply@vitality-gym.com
SMTP_PASS = tu_password_smtp
Create Service → Application
Configurar:
Name: api
Source: GitHub
Repository: tu repositorio
Branch: main
Build Type: Dockerfile
Dockerfile Path: packages/api/Dockerfile
En Domains , agregar:
Domain: api.vitality-gym.com (o testing.api.vitality-gym.com para staging)
HTTPS: Enabled (Let’s Encrypt)
En Ports :
Container Port: 3000
Scheme: https
En la pestaña Environment , configura:
DATABASE_URL = postgresql://vitality_user:PASSWORD@database:5432/vitality_db
AUTH_SECRET = genera_un_secret_seguro_de_32_chars
REDIS_URL = redis://redis:6379
# URLs (usar testing.* para staging)
FRONTEND_URL = https://app.vitality-gym.com # o https://testing.app.vitality-gym.com
BACKEND_URL = https://api.vitality-gym.com # o https://testing.api.vitality-gym.com
CORS_ORIGINS = https://app.vitality-gym.com,https://vitality-gym.com,https://testing.app.vitality-gym.com
SMTP_HOST = mail.vitality-gym.com
SMTP_USER = noreply@vitality-gym.com
SMTP_PASS = tu_password_smtp
UPLOAD_DIR = /app/packages/api/uploads
GOOGLE_CLIENT_ID = tu_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET = tu_client_secret
# VAPID Keys (generar con: npx web-push generate-vapid-keys)
VAPID_PUBLIC_KEY = BLxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
VAPID_PRIVATE_KEY = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
VAPID_SUBJECT = mailto:admin@vitality-gym.com
WOMPI_PUBLIC_KEY = pub_prod_xxxxxxxxxxxx
WOMPI_PRIVATE_KEY = prv_prod_xxxxxxxxxxxx
WOMPI_EVENTS_KEY = prod_events_xxxxxxxxxxxx
WOMPI_INTEGRITY_KEY = prod_integrity_xxxxxxxxxxxx
En Advanced → Volumes :
/app/packages/api/uploads → vitality-uploads
Click Deploy
Create Service → Application
Configurar:
Name: app
Source: GitHub (mismo repositorio)
Branch: main
Build Type: Dockerfile
Dockerfile Path: packages/app/Dockerfile
En Build Arguments :
BACKEND_URL = https://api.vitality-gym.com # o https://testing.api.vitality-gym.com
En Domains , agregar:
Domain: app.vitality-gym.com (o testing.app.vitality-gym.com para staging)
HTTPS: Enabled
En Ports :
Container Port: 4321
Scheme: https
Click Deploy
Después de que el API esté desplegado, accede a la terminal del contenedor:
# En Dokploy: Service → api → Terminal
bunx prisma migrate deploy --schema=./packages/database/prisma/schema.prisma
O desde tu máquina local con la DATABASE_URL de producción:
DATABASE_URL = " postgresql://user:pass@host:5432/db " bunx prisma migrate deploy
Cuando realices cambios en el schema.prisma después de que la aplicación ya esté en producción, sigue estos pasos para actualizar la base de datos sin perder datos:
Generar la migración localmente:
# En la raíz del proyecto
bun run db:migrate --name nombre_de_tu_migración
Esto creará una nueva carpeta en packages/database/prisma/migrations.
Subir los cambios al repositorio:
Haz commit y push de los nuevos archivos de migración.
Desplegar la nueva versión del API:
Dokploy detectará el cambio y reconstruirá el contenedor del API.
Aplicar la migración en producción:
Una vez que el nuevo contenedor esté corriendo, usa la terminal de Dokploy en el servicio api:
bunx prisma migrate deploy --schema=./packages/database/prisma/schema.prisma
# En el contenedor de API
bun run packages/database/seed.ts
Configura los siguientes registros DNS en tu proveedor:
Tipo Nombre Valor A @IP del servidor A apiIP del servidor A appIP del servidor A docsIP del servidor A testing.apiIP del servidor A testing.appIP del servidor CNAME wwwvitality-gym.com
Para BillionMail , agrega también los registros MX, SPF, DKIM y DMARC según su documentación.
dokploy logs api --follow
docker logs vitality-api -f
docker restart vitality-api
docker exec -it vitality-api sh
En el panel de Wompi, configura:
Producción: https://api.vitality-gym.com/api/v1/payments/webhook
Staging: https://testing.api.vitality-gym.com/api/v1/payments/webhook
Evento: transaction.updated
# Verificar conexión desde API
bun -e " console.log(await fetch('http://localhost:3000/health').then(r=>r.json())) "
psql postgresql://user:pass@database:5432/vitality_db -c " SELECT 1 "
# Ver estado de migraciones
bunx prisma migrate status
# Reset (¡CUIDADO EN PRODUCCIÓN!)
bunx prisma migrate reset --force
docker exec -t vitality-database pg_dumpall -c -U vitality_user > backup_ $( date +%Y%m%d ) .sql
cat backup.sql | docker exec -i vitality-database psql -U vitality_user
# Si usas docker-compose, el nombre del contenedor puede variar
docker-compose exec database pg_dumpall -c -U vitality_user > backup_ $( date +%Y%m%d ) .sql
cat backup.sql | docker-compose exec -T database psql -U vitality_user
Ir a Settings → Backups
Configurar destino (S3, local, etc.)
Programar backups diarios
Para alto tráfico, considera:
Horizontal Scaling: Múltiples instancias del API con load balancer
PgBouncer: Connection pooling para PostgreSQL
Redis Cluster: Para alta disponibilidad del cache
CDN: CloudFlare o similar para assets estáticos
Variables Requeridas
Variable Servicio Descripción DATABASE_URLAPI Conexión PostgreSQL AUTH_SECRETAPI Secret para JWT (32+ chars) REDIS_URLAPI Conexión Redis SMTP_*API Credenciales email FRONTEND_URLAPI URL del frontend BACKEND_URLAPI, App URL del backend CORS_ORIGINSAPI Orígenes permitidos GOOGLE_CLIENT_*API OAuth de Google UPLOAD_DIRAPI Directorio de uploads WOMPI_*API Pasarela de pagos VAPID_*API Push notifications