← Blog
#dotnet#hangfire#background jobs#csharp

Hangfire: el trabajo sucio que tu API no debería estar haciendo

April 30, 2026

Tu usuario hizo clic en "Confirmar pedido". Tu API recibe el request, guarda en base de datos, genera la factura en PDF, envía el email de confirmación, llama a la API del proveedor logístico, y recién entonces responde con un 200.

Todo eso en el mismo hilo, bloqueando el request, esperando respuestas externas que pueden tardar segundos o directamente fallar.

Eso no es una API. Es una cadena de dependencias disfrazada de endpoint.

El problema con hacer todo en el request

Cuando metés lógica pesada o llamadas externas dentro del ciclo de vida de un request HTTP, estás apostando a que todo va a salir bien al mismo tiempo. Y en producción, esa apuesta se pierde seguido.

El servidor de email está caído → tu request falla. La API del proveedor tarda 8 segundos → el timeout del cliente se dispara. El PDF tarda en generarse → el hilo queda bloqueado. El usuario hace doble clic → se ejecuta dos veces.

La solución no es manejar mejor los errores ahí adentro. Es sacar esas operaciones del request y procesarlas de forma asincrónica, con reintentos automáticos y persistencia. Para eso existe Hangfire.

¿Qué es Hangfire?

Hangfire es una librería open source para .NET que permite ejecutar background jobs de forma confiable. La clave está en esa última palabra: confiable. No es un simple Task.Run() con promesas. Es un sistema de colas con persistencia real — los jobs sobreviven reinicios de la app.

Funciona en tres partes: el cliente que encola el job, el storage que lo persiste (SQL Server, PostgreSQL, Redis), y el servidor que lo ejecuta en background. Pueden vivir en el mismo proceso o separados.

Los tres tipos de jobs

Fire-and-forget: se encola y se ejecuta una vez, lo antes posible. El caso más común — enviar un email, notificar a un webhook, registrar una auditoría.

BackgroundJob.Enqueue(() => emailService.SendConfirmation(orderId));

Delayed: se ejecuta después de un tiempo específico. Ideal para recordatorios, expiración de sesiones, follow-ups.

BackgroundJob.Schedule(() => reminderService.Send(userId), TimeSpan.FromDays(3));

Recurring: se repite según una expresión cron. Reemplaza tareas que antes vivían en un Windows Service aparte o en un cron del sistema operativo.

RecurringJob.AddOrUpdate("daily-report", () => reportService.Generate(), Cron.Daily);

El dashboard que vale más que mil logs

Hangfire viene con un dashboard web integrado. Podés ver en tiempo real qué jobs están en cola, cuáles están ejecutándose, cuáles fallaron y cuántas veces se reintentaron. También podés relanzar un job fallido manualmente con un clic.

Eso parece menor hasta que son las 2am, algo falló en producción, y en lugar de revisar logs cruzando timestamps, entrás al dashboard y ves exactamente qué job falló, con qué parámetros, y el stack trace completo.

Persistencia: por qué importa el storage

La diferencia entre Hangfire y un simple background thread es que los jobs se persisten antes de ejecutarse. Si la app se cae mientras un job está en cola, cuando reinicia lo retoma. Eso cambia completamente el contrato de confiabilidad.

Para la mayoría de los proyectos, SQL Server o PostgreSQL como storage es más que suficiente. Si necesitás mayor throughput o tenés miles de jobs por segundo, Redis es la opción. La elección del storage también determina la estrategia de reintentos y el TTL de los jobs completados.

Reintentos automáticos: el feature que cambia todo

Por defecto, si un job falla, Hangfire lo reintenta automáticamente hasta 10 veces con backoff exponencial. El primer reintento es en segundos, el décimo es horas después. Podés configurarlo por job o globalmente.

[AutomaticRetry(Attempts = 3)]
public void ProcessPayment(int orderId)
{
    // si falla, se reintenta 3 veces con backoff
}

Esto significa que una falla transitoria — el servidor de email caído por 5 minutos, un timeout de red momentáneo — se resuelve sola sin intervención humana ni lógica adicional en tu código.

Cuándo Hangfire no es suficiente

Hangfire resuelve bien el caso de un solo servicio con jobs simples. Pero tiene límites claros:

Si necesitás comunicación entre múltiples servicios, un message broker como RabbitMQ o Azure Service Bus es más apropiado. Si necesitás garantías de orden estricto o event sourcing, Hangfire no fue diseñado para eso. Si procesás millones de jobs por hora, probablemente necesitás una arquitectura dedicada.

Hangfire es la herramienta correcta para el 80% de los casos de background processing en aplicaciones .NET empresariales. Para el 20% restante, necesitás arquitectura de mensajería real.

Setup en cinco minutos

En una app ASP.NET Core:

// Program.cs
builder.Services.AddHangfire(config =>
    config.UseSqlServerStorage(connectionString));
builder.Services.AddHangfireServer();

app.UseHangfireDashboard("/jobs");

Eso es todo. Hangfire crea sus propias tablas en la base de datos al iniciar. El dashboard queda disponible en /jobs. Desde cualquier parte de la app podés encolar jobs con BackgroundJob.Enqueue().

Conclusión

La regla es simple: si una operación puede fallar, tarda más de 200ms, o no necesita que el usuario espere el resultado — no debería estar en el request. Debería estar en un job.

Hangfire no es la solución más sofisticada del ecosistema .NET. Pero es probablemente la más pragmática: fácil de instalar, fácil de monitorear, y lo suficientemente robusta para aguantar producción sin babysitting constante. A veces eso es exactamente lo que necesitás.

FungiCode.io

Software Development · Data Analysis

Work with me