- Arquitectura
- Requisitos
- Configuración del entorno
- Deploy con Coolify
- Docker Compose (producción)
- Tailscale
- Desarrollo local
- Variables de entorno
Cliente (browser / móvil)
│
▼
nginx :80 (reverse proxy)
│
├── /api/* ──► api (Fastify :3001) ──► db (PostgreSQL :5432)
│
└── /* ──► SPA (dist estático de Vite)
| Servicio | Imagen | Rol |
|---|---|---|
nginx |
Dockerfile.nginx |
Reverse proxy + sirve el SPA |
web |
Dockerfile.frontend |
Compila React con Vite (solo en build) |
api |
Dockerfile.backend |
Fastify + Prisma, aplica migraciones al arrancar |
db |
postgres:16-alpine |
Base de datos, datos persistentes en volumen pgdata |
- Docker + Docker Compose
make- Node.js 22 (solo para desarrollo local)
Copia el archivo de ejemplo y rellena los valores:
cp .env.example .env# PostgreSQL
DB_NAME=gymtracker
DB_USER=gymuser
DB_PASSWORD=una_clave_segura_aqui
# Solo para desarrollo local
DATABASE_URL=postgresql://gymuser:una_clave_segura_aqui@localhost:5432/gymtracker
# Genera cada uno con: openssl rand -base64 32
JWT_SECRET=cadena_aleatoria_larga_minimo_32_caracteres
JWT_REFRESH_SECRET=otra_cadena_diferente_para_refresh💡 Para generar secrets seguros:
openssl rand -base64 32
Este proyecto está optimizado para desplegarse en Coolify usando Docker Compose.
- Conecta tu repositorio en Coolify → New Resource → Docker Compose
- Desactiva "Build Secrets" en Configuration → General (evita que Coolify corrompa los Dockerfiles)
- Configura las variables de entorno en la tab Environment Variables con los siguientes valores:
DB_NAME=gymtracker
DB_USER=gymuser
DB_PASSWORD=<password_seguro>
JWT_SECRET=<openssl rand -base64 32>
JWT_REFRESH_SECRET=<openssl rand -base64 32>
- Asigna un dominio al servicio
nginxen la sección FQDN - Despliega — Coolify construirá las 3 imágenes y levantará el stack automáticamente
⚠️ Nota: Las variables deben marcarse como Available at Runtime, no solo en build time.
make buildEste comando:
- Construye la imagen del frontend (Vite → dist estático)
- Construye la imagen del backend (TypeScript compilado, 2 stages)
- Construye la imagen de nginx (config embebida)
- Levanta los servicios en orden:
db→api→nginx - El backend aplica las migraciones de Prisma automáticamente
La app queda disponible en: http://localhost:3000
make build # Reconstruir imágenes y levantar
make deploy # git pull + reconstruir + levantar
make logs # Logs en tiempo real de nginx y api
make logs-api # Logs solo del backend
make logs-db # Logs solo de PostgreSQL
make restart # Reiniciar nginx y api sin tocar la BD
docker compose down # Parar servicios (datos persisten)
docker compose down -v # Parar y borrar todos los datos ⚠️Los datos de PostgreSQL se almacenan en el volumen Docker pgdata. Sobreviven a reinicios y a docker compose down. Solo se eliminan con docker compose down -v.
make build
tailscale ip -4 # Obtén tu IP de Tailscale, ej: 100.80.118.36
# Accede desde cualquier dispositivo del tailnet:
# http://100.80.118.36:3000make build
tailscale serve --bg 3000
tailscale serve status # Muestra la URL HTTPS del tailnet
# https://nombre-maquina.tailnet-name.ts.netPara desactivar:
tailscale serve resetRequiere Node.js 22 instalado. Usa hot-reload con Vite + tsx watch.
# 1. Levantar solo la BD
make db-up
# 2. Instalar dependencias (primera vez)
cd frontend && npm install && cd ..
cd backend && npm install && cd ..
# 3. Arrancar en paralelo
make dev- Frontend (Vite):
http://localhost:5173 - Backend (Fastify):
http://localhost:3001 - El proxy de Vite redirige
/api/*al backend automáticamente
cd backend && npm run dev &
cd frontend && npm run dev -- --host
# Vite mostrará: Network: http://100.80.118.36:5173/make db-migrate # Aplicar nuevas migraciones de Prisma
make db-studio # Abrir Prisma Studio (explorador visual de BD)
cd frontend && npm run lint
cd frontend && npx tsc -b
cd backend && npx tsc| Variable | Descripción | Cómo generarla |
|---|---|---|
DB_NAME |
Nombre de la base de datos | ej. gymtracker |
DB_USER |
Usuario de PostgreSQL | ej. gymuser |
DB_PASSWORD |
Contraseña de PostgreSQL | openssl rand -base64 32 |
JWT_SECRET |
Secret para access tokens | openssl rand -base64 32 |
JWT_REFRESH_SECRET |
Secret para refresh tokens | openssl rand -base64 32 (diferente) |
APP_URL |
URL pública del frontend | https://tuapp.com |
| Variable | Descripción | Ejemplo |
|---|---|---|
DATABASE_URL |
URL directa para Prisma (make dev) |
postgresql://gymuser:pass@localhost:5432/gymtracker |
Sin SMTP_HOST la app funciona igual, pero los correos se imprimen en los logs del contenedor.
| Variable | Descripción | Ejemplo |
|---|---|---|
SMTP_HOST |
Servidor SMTP | mail.tudominio.com |
SMTP_PORT |
Puerto SMTP | 465 (SSL) o 587 (STARTTLS) |
SMTP_SECURE |
SSL directo en puerto | true para 465, false para 587 |
SMTP_USER |
Email remitente | [email protected] |
SMTP_PASS |
Contraseña / App Password SMTP | — |
SMTP_FROM |
Nombre visible en el correo | Gym Tracker <[email protected]> |
Hecho con ☕ y 🏋️