Saltar al contenido principal

Perfiles de configuración

Siete configuraciones opinadas y probadas. Encuentra la que corresponde a tu despliegue, aplícala y sigue adelante.

Cada perfil es un punto de partida, no una camisa de fuerza — pero cada uno está construido a partir de lo que de verdad ha funcionado (y de lo que de verdad se ha roto) en despliegues reales.

Propósito​

Ahorrarte el ensayo y error. La mayoría de las preguntas de ajuste tienen una respuesta correcta una vez sabes qué tan grande es el despliegue y para qué sirve.

Cómo elegir un perfil​

PerfilParaLo único que de verdad importa
PredeterminadoEvaluar, primera instalaciónConfigurar bien los secretos
Servidor doméstico pequeñoUn Pi, un NAS, una mini-PCMantenlo liviano; aquí rTorrent está bien
Biblioteca de medios grandeCientos de torrents, miles de elementosUsa qBittorrent, no rTorrent
Alta disponibilidadNo puede caersePostgres externo + backups de verdad
EmpresarialMuchos usuarios, requisitos de auditoríaRBAC + 2FA + revisión de auditoría
RendimientoFunciona pero va lentoÍndices trigram + ajuste de Postgres
SeguridadExpuesto a internetNo publiques nada que no tengas que publicar

Predeterminado​

La configuración que viene de fábrica. Sensata, segura y sin opiniones fuertes — esto es lo que obtienes de cp .env.example .env.

Úsalo cuando: estás instalando por primera vez o evaluando.

# --- Requerido. El stack SE NIEGA a arrancar sin esto. ---------------------
POSTGRES_PASSWORD=<strong ALPHANUMERIC password>
ADMIN_PASSWORD=<strong password>

# Genera cada uno por separado con: openssl rand -base64 48
JWT_ACCESS_SECRET=<48+ random chars>
JWT_REFRESH_SECRET=<48+ random chars>
ENCRYPTION_KEY=<48+ random chars — MUST DIFFER from JWT_ACCESS_SECRET>

# --- Valores predeterminados sensatos -------------------------------------
NODE_ENV=production
PORT=4000
FRONTEND_PORT=8080
CORS_ORIGIN=http://localhost:8080

POSTGRES_USER=ultratorrent
POSTGRES_DB=ultratorrent
REDIS_HOST=redis
REDIS_PORT=6379

JWT_ACCESS_TTL=15m
JWT_REFRESH_TTL_DAYS=30

FILE_MANAGER_ROOTS=/downloads
SSRF_ALLOW_HOSTS=prowlarr

ADMIN_USERNAME=admin
ADMIN_EMAIL=admin@ultratorrent.local

PUID=1000
PGID=1000
TZ=Etc/UTC
docker compose --profile qbittorrent up -d --build
docker compose exec backend npx prisma db seed
Las cuatro reglas que impiden que el backend arranque
  1. JWT_ACCESS_SECRET y ENCRYPTION_KEY deben tener cada uno 32+ caracteres.
  2. Deben ser diferentes entre sí.
  3. Ninguno puede ser un valor por defecto tipo dev-* / change-me.
  4. POSTGRES_PASSWORD debe ser alfanumérica (va incrustada en una URL).

Esto se valida. Si el backend arranca en producción, es porque las cumpliste.

Lista de verificación

  • Los tres secretos generados con openssl rand -base64 48
  • ENCRYPTION_KEY ≠ JWT_ACCESS_SECRET
  • Contraseña del admin sembrado cambiada tras el primer inicio de sesión
  • .env respaldado fuera del host

Servidor doméstico pequeño​

Una Raspberry Pi 4/5, un NAS Synology/QNAP, una laptop vieja, una mini-PC. CPU modesto, 2–4 GB de RAM, un puñado de usuarios.

Úsalo cuando: tienes menos de ~50 torrents activos y una biblioteca de unos cientos de elementos.

# Empieza desde Predeterminado, y luego:

# La UI de administración del NAS normalmente ocupa el 8080.
FRONTEND_PORT=8123
CORS_ORIGIN=http://nas.local:8123

# Sé dueño de los medios como el usuario que ya los posee (p. ej. Plex).
# Encuéntralo con: id plex
PUID=1000
PGID=1000
TZ=America/New_York

# Mantén ajustado el límite del gestor de archivos.
FILE_MANAGER_ROOTS=/downloads

# Sesiones más largas — es una LAN de confianza, y volver a iniciar sesión molesta.
JWT_ACCESS_TTL=30m
JWT_REFRESH_TTL_DAYS=60

Motor: el rTorrent incluido funciona perfectamente bien a este tamaño. Su bug de fallos depende de la carga y es prácticamente invisible por debajo de ~100 torrents (un host con 7 torrents tuvo cero fallos en el mismo periodo en que un host con 752 torrents falló 44 veces).

docker compose --profile rtorrent up -d --build

Sáltate el catálogo de IMDb a menos que necesites la detección de episodios faltantes. Son 8.9 millones de filas y es opcional. Si sí lo importas, presupuesta la RAM y deja que los índices trigram terminen de construirse antes de escanear.

Detalles del NAS que te costarán una hora
  • El puerto 8080 normalmente está ocupado. Configura FRONTEND_PORT. No intentes reasignarlo con un override de Compose — Compose añade ports, así que el mapeo original sobrevive y sigue en conflicto.
  • Synology DSM elimina SETUID/SETGID de las capacidades por defecto del container, lo que rompe la bajada de privilegios de rTorrent y hace que corra como root (así que las descargas quedan con dueño root). El archivo Compose incluido las vuelve a añadir con cap_add: ["SETUID", "SETGID"]. No borres esa línea.
  • ¿Los medios son de otra app? No les hagas chown. Configura PUID/PGID con ese usuario (id plex) para que las descargas se escriban como él.

Lista de verificación

  • FRONTEND_PORT configurado a un puerto libre
  • PUID/PGID coinciden con quien debe ser dueño de los medios
  • cap_add: ["SETUID","SETGID"] sigue presente (Synology)
  • Perfil de rTorrent habilitado
  • Catálogo de IMDb omitido, o importado a propósito

Biblioteca de medios grande​

De cientos a miles de torrents. Miles de elementos de medios. El catálogo de IMDb importado. Aquí es donde viven las fallas interesantes.

Úsalo cuando: pasas de ~100 torrents activos, o de ~2,000 elementos de medios.

La única decisión que importa: no uses rTorrent​

# Empieza desde Predeterminado, y luego:
QBITTORRENT_PORT=8081
docker compose --profile qbittorrent up -d
docker compose logs qbittorrent | grep -i password # contraseña temporal del primer arranque

Regístralo en Infraestructura → Motores (tipo qBittorrent, URL base http://qbittorrent:8080).

rTorrent 0.9.8 tiene un bug de fallos, dependiente de la carga, que no tiene arreglo

internal_error: priority_queue_insert(...) se dispara durante la programación del anuncio al tracker. No tiene solución en la línea 0.9.8, y escala con tu cantidad de torrents. Mediciones reales de dos hosts corriendo el mismo build:

TorrentsFallos
70
75244 en 4 días (~10/día)

qBittorrent maneja miles de torrents sin problema. Múdate antes de sentirlo, no después.

Si la prueba de conexión de qBittorrent falla con 401: desactiva Enable Host header validation en Options → Web UI (el backend se conecta por el nombre del servicio qbittorrent, en el que qBittorrent no confía por defecto).

La segunda decisión: cada indexador necesita un minSeeders​

Esto no es un lujo. El filtro de seeders por indexador solo aplica cuando la columna está configurada — así que un indexador sin minSeeders te entrega lanzamientos con 0 seeders, y:

Un magnet con 0 seeders nunca puede obtener sus metadatos — y aun así el motor lo cuenta como una descarga activa todo el tiempo que lo intenta.

El resultado real fue un motor sosteniendo 1,137 torrents y moviendo 0 bytes: 1,114 de ellos tenían cero seeders, y con max_active_downloads: 100, exactamente 88 metaDL + 12 stalledDL = 100 espacios quedaron ocupados permanentemente por torrents que nunca iban a terminar. Los 1,034 saludables quedaron en cola detrás de ellos.

Configura minSeeders en cada indexador, y habilita la cola de estacionamiento (viene deshabilitada — pausa los torrents muertos para que dejen de ocupar espacios, y luego periódicamente los fuerza a arrancar para volver a revisar si aparecieron seeders).

La tercera decisión: índices trigram​

Con el catálogo de IMDb de 8.9 millones de filas, las búsquedas ILIKE sin índices GIN trigram toman 47.8 segundos cada una y dejarán a Postgres sin recursos hasta que los escaneos nunca terminen. Con ellos: 180 ms.

Los builds actuales los construyen automáticamente en tiempo de ejecución. Verifica que sean válidos:

SELECT c.relname, i.indisvalid
FROM pg_class c JOIN pg_index i ON i.indexrelid = c.oid
WHERE c.relname LIKE '%trgm%'; -- todos deben ser `t`

Ver Rendimiento.

Lista de verificación

  • qBittorrent, no rTorrent
  • minSeeders configurado en cada indexador
  • Cola de estacionamiento habilitada
  • Los tres índices trigram existen y son válidos
  • Postgres tiene 4 GB+ y random_page_cost reducido para SSD
  • Los escaneos no corren en paralelo con la importación de IMDb

Alta disponibilidad​

El tiempo fuera de servicio es inaceptable. Seamos honestos: UltraTorrent es una aplicación de instancia única — el backend no está diseñado para escalar horizontalmente detrás de un balanceador de carga (los cuerpos de las tareas corren en proceso; una segunda instancia duplicaría el trabajo programado). Aquí, HA significa recuperación rápida y confiable, no activo-activo.

Úsalo cuando: necesitas un objetivo de tiempo de recuperación estricto.

# Apunta a un Postgres EXTERNO, gestionado y replicado — el cambio de HA más
# valioso que puedes hacer.
DATABASE_URL=postgresql://ultratorrent:PASSWORD@postgres.internal:5432/ultratorrent?schema=public

# Redis también puede ser externo, pero no guarda estado durable — perderlo es barato.
REDIS_HOST=redis.internal
REDIS_PORT=6379

La arquitectura de HA​

El host de la app se vuelve ganado: si muere, lo reconstruyes desde el repositorio, restauras el .env, lo apuntas a la misma base de datos, y ya estás de vuelta.

Lo que de verdad te compra disponibilidad​

Haz estoPor qué
Postgres externo y replicado con recuperación a un punto en el tiempoEs el único componente irreemplazable
.env en un almacén de secretosSin ENCRYPTION_KEY, restaurar la base de datos es media restauración
Medios en un NAS/RAID, no locales al containerDesacopla tus medios del host de la app
restart: unless-stopped en todoYa es el valor por defecto. Es lo que hace sobrevivibles los fallos de rTorrent
Monitorea /api/system/ready, no solo /live/live dice que el proceso existe; /ready dice que sus dependencias son usables
Un simulacro de restauración ensayadoTu RTO es una adivinanza hasta que lo hayas medido
Alerta sobre RestartCountDetecta un bucle de fallos que docker compose ps esconde

Sondas de salud​

# Liveness — ¿el proceso está vivo? (esto es lo que usa el healthcheck del container)
curl -f http://localhost:8080/api/system/live || echo DOWN

# Readiness — ¿sus dependencias son usables?
curl -f http://localhost:8080/api/system/ready || echo NOT_READY
Aún sin verificar

Correr UltraTorrent detrás de un balanceador de carga con múltiples réplicas del backend no ha sido validado, y el modelo de tareas en proceso significa que el trabajo programado muy probablemente se duplicaría entre instancias. Trata las múltiples réplicas como no soportadas hasta que se demuestre lo contrario.

Lista de verificación

  • Postgres es externo, replicado, con PITR
  • El .env está en un almacén de secretos, no solo en el host
  • Los medios viven en almacenamiento redundante
  • /api/system/ready está monitoreado y alerta
  • RestartCount está monitoreado
  • El simulacro de restauración se ha corrido y cronometrado (ese es tu RTO real)
  • Solo corre una instancia del backend

Empresarial​

Muchos usuarios, roles de verdad, un requisito de auditoría.

Úsalo cuando: UltraTorrent se comparte más allá de un hogar.

# Empieza desde Predeterminado + Seguridad, y luego:

# Sesiones más cortas.
JWT_ACCESS_TTL=15m
JWT_REFRESH_TTL_DAYS=7

# El origen exacto de producción. Nunca `*`.
CORS_ORIGIN=https://ultratorrent.corp.example.com

# Estrecha el límite duro de archivos a exactamente lo que el motor escribe.
FILE_MANAGER_ROOTS=/downloads/media

# Confía solo en los indexadores que de verdad corres.
SSRF_ALLOW_HOSTS=prowlarr

Roles, en la práctica​

Asigna el menor rol que haga el trabajo:

PersonaRolCuidado con
TúSUPER_ADMINEl único rol que puede otorgar SUPER_ADMIN. Dáselo a la menor cantidad de gente posible.
Un coadministradorADMINISTRATORTodo excepto system.manage.
Alguien que administra sus propios mediosPOWER_USER⚠️ Incluye todos los files.* — eliminar, acciones en masa y limpieza incluidos.
Un usuario comúnUSERArchivos de solo lectura. Seguro.
Alguien que solo mira el panelREAD_ONLYSolo ver.
POWER_USER puede borrar tus archivos

Tiene todos los permisos files.*, incluyendo files.delete y files.cleanup. Si eso no es lo que quieres, crea un rol personalizado. Nota también que torrents.delete_data (elimina los datos del disco) es un permiso aparte de torrents.delete — otórgalos por separado.

La plataforma aplica por ti las protecciones contra escalación: solo un SUPER_ADMIN puede otorgar SUPER_ADMIN, ningún usuario puede editar sus propios roles, desactivar a un usuario revoca sus refresh tokens de inmediato, y las acciones en masa requieren el mismo permiso que su ruta dedicada — así que quien solo puede ver no puede colar una operación destructiva por /torrents/bulk.

Prácticas obligatorias​

  • 2FA en cada cuenta de administrador. La inscripción es confirmada, no a ciegas — un usuario debe probar que posee un código válido antes de que se active, así que nadie se queda fuera por accidente. Guarda los 10 códigos de recuperación de un solo uso.
  • Revisa el registro de auditoría mensualmente. Registra los inicios de sesión fallidos con el nombre de usuario intentado, cada acción destructiva, los cambios de rol y los cambios de configuración — y ahora nombra el medio al que apuntaba cada fila en lugar de mostrar un id opaco.
  • Claves API, no contraseñas compartidas, para el acceso de máquinas.

Lista de verificación

  • Cada usuario tiene el menor rol que funciona
  • 2FA inscrito en todas las cuentas de administrador; códigos de recuperación guardados
  • Otorgamientos de POWER_USER revisados (pueden borrar archivos)
  • Registro de auditoría revisado con regularidad
  • CORS_ORIGIN es el origen exacto de producción
  • FILE_MANAGER_ROOTS es lo más estrecho posible

Rendimiento​

Todo funciona — solo que va lento. Ver Rendimiento para el tratamiento completo; esto es la capa de configuración.

# docker-compose.override.yml
services:
postgres:
command:
- postgres
- -c
- shared_buffers=1GB
- -c
- work_mem=32MB
- -c
- maintenance_work_mem=512MB # hace que la construcción de índices sea mucho más rápida
- -c
- effective_cache_size=3GB
- -c
- random_page_cost=1.1 # estás en SSD; el valor por defecto de 4.0 asume disco giratorio

Luego, en orden de impacto:

  1. Verifica que los índices trigram existen y tienen indisvalid = true. Esto vale ~265× en las búsquedas de títulos de IMDb (47.8 s → 180 ms). Un índice INVALID es peor que ninguno — el planificador lo ignora, pero su nombre existe, así que IF NOT EXISTS se salta la reconstrucción para siempre.
  2. ANALYZE después de cualquier importación grande. Un plan viejo es un plan lento.
  3. random_page_cost=1.1 — la línea con más apalancamiento. El valor por defecto de 4.0 sesga al planificador en contra de los escaneos de índice, lo cual está mal en SSD.
  4. No corras un escaneo de biblioteca durante la importación de IMDb. Se pelean.
  5. Múdate a qBittorrent si tienes muchos torrents.
docker compose exec postgres psql -U ultratorrent -d ultratorrent -c "
EXPLAIN ANALYZE SELECT * FROM imdb_titles WHERE \"primaryTitle\" ILIKE 'Silo';"
# Quieres: Bitmap Index Scan. NO quieres: Seq Scan.

Lista de verificación

  • Índices trigram presentes y válidos
  • EXPLAIN muestra un Bitmap Index Scan
  • random_page_cost reducido para SSD
  • ANALYZE corrido recientemente
  • Postgres tiene suficiente shared_buffers

Seguridad​

Expuesto a internet, o simplemente paranoico. Va de la mano con Seguridad.

NODE_ENV=production

# El origen EXACTO. No `*`. No localhost.
CORS_ORIGIN=https://ultratorrent.example.com

# Todo de vida corta.
JWT_ACCESS_TTL=15m
JWT_REFRESH_TTL_DAYS=7

# El límite duro más estrecho posible.
FILE_MANAGER_ROOTS=/downloads/media

# Protección SSRF completa: déjalo VACÍO si no usas ningún indexador con IP privada.
# Si usas el Prowlarr incluido, TIENES que mantenerlo en la lista.
SSRF_ALLOW_HOSTS=prowlarr

No publiques nada que no tengas que publicar​

# docker-compose.override.yml — deja de publicar los puertos de los acompañantes.
services:
qbittorrent:
ports: !reset [] # accede a él solo por la red interna
prowlarr:
ports: !reset []
La superficie de control del motor no tiene autenticación

La interfaz SCGI/XML-RPC de rTorrent da control total del cliente, incluyendo ejecución de comandos (corre rm durante la eliminación con datos). El archivo Compose incluido correctamente la mantiene en expose (solo interna) — nunca la publiques. La misma precaución aplica a la Web API de qBittorrent: si publicaste su puerto para obtener la contraseña del primer arranque, deja de publicarlo después.

TLS en el borde​

docker compose --profile proxy up -d

Reemplaza la etiqueta de sitio :80 en deploy/Caddyfile con tu dominio para tener HTTPS automático con Let's Encrypt. Ver TLS.

La recomendación honesta​

Ponlo detrás de una VPN. WireGuard o Tailscale te dan acceso remoto con una fracción de la superficie de ataque. UltraTorrent mueve, elimina y ejecuta cosas sobre archivos. Hay muy poca ventaja en exponerlo públicamente.

Lista de verificación

  • TLS termina en un proxy inverso
  • CORS_ORIGIN es el origen exacto de producción
  • rTorrent / FlareSolverr no están publicados
  • Puertos de qBittorrent / Prowlarr sin publicar (o tras un firewall)
  • 2FA en cada cuenta que puede iniciar sesión
  • FILE_MANAGER_ROOTS estrechado
  • SSRF_ALLOW_HOSTS lista solo indexadores de confianza
  • chmod 600 .env
  • Registro de auditoría revisado con regularidad

Comparación​

PredeterminadoPequeñoGrandeHAEmpresarialRendimientoSeguridad
MotorqBittorrentrTorrent OKqBittorrentqBittorrentqBittorrentqBittorrentqBittorrent
Catálogo de IMDbopcionalomitirsísísísíopcional
Índices trigramautomáticosn/acríticoscríticoscríticoscríticosautomáticos
Postgresincluidoincluidoincluido + ajustadoexternoincluidoajustadoincluido
JWT_ACCESS_TTL15m30m15m15m15m15m15m
JWT_REFRESH_TTL_DAYS306030307307
2FArecomendadoopcionalrecomendadorequeridorequerido—requerido
Puertos publicados808080808080 + 8081vía LBvía proxy—solo 80/443
minSeedersconfigúraloconfigúraloobligatorioobligatorioobligatorioobligatorioconfigúralo
Cola de estacionamientoapagadaapagadaencendidaencendidaencendidaencendidaapagada

Solución de problemas​

SíntomaDesajuste de perfil probable
El motor se reinicia constantementeEstás con ajustes de Pequeño a escala Grande → cámbiate a qBittorrent
Los escaneos nunca terminanFaltan índices trigram → Rendimiento
Nada se descarga, todo en colaFalta minSeeders → Grande
El backend no arrancaSecretos → Predeterminado
Descargas con dueño rootPUID/PGID o falta cap_add → Pequeño

Detalle completo en Solución de problemas.

Consejos​

  • Los perfiles se combinan. Grande + Seguridad es una combinación perfectamente normal.
  • Elige tu motor antes de crecer, no después. Migrar de motor con 800 torrents es una faena; empezar con el correcto es gratis.
  • La regla de minSeeders es el seguro más barato de este documento. Una sola columna sin configurar dejó a un host de 1,137 torrents a cero bytes por segundo.

Preguntas frecuentes​

¿Puedo cambiar de perfil después? Sí. Son solo ajustes. Cambiar de motor significa volver a agregar los torrents al motor nuevo, así que hazlo temprano.

¿Cuál perfil para una Raspberry Pi? Servidor doméstico pequeño. Sáltate el catálogo de IMDb.

Tengo 300 torrents. ¿Pequeño o Grande? Grande. La tasa de fallos de rTorrent ya va subiendo con esa cantidad.

¿Necesito Prowlarr? No. Es un acompañante opcional que administra las definiciones de indexadores Torznab por ti. UltraTorrent arranca y funciona bien sin él. Ver Prowlarr.

¿Puedo correr múltiples instancias del backend? No está soportado. Los cuerpos de las tareas corren en proceso, así que una segunda instancia duplicaría el trabajo programado. Ver Alta disponibilidad.

Ver también​