Has lanzado el build de Electron, la página de release está activa y el primer ticket de soporte llega antes de que termine de hacerse el café. Un usuario dice que la app nunca encontró la actualización. Otro la descargó pero no puede instalarla. Un tercero sigue usando un binario antiguo con un flujo de autenticación roto, mientras tus logs no muestran nada útil.
Esa es la incómoda realidad de el auto update de apps Electron. El updater API es solo un componente. Un release de producción también depende del signing de la plataforma, políticas de transporte, manifests, hosting, lifecycle events, observabilidad, controles de rollout y un path de rollback. Si tratas cualquiera de estos como opcional, un patch rutinario puede convertirse en un incidente nocturno.
Table of Contents
- El incidente de actualización a las 2 A.M. que dio origen a esta guía
- Cómo elegir el camino correcto para Electron Updater
- Implementación del flujo de Auto-Update en el Main Process
- Configuración de CI/CD para manifiestos y releases firmados
- Rollouts, Channels y estrategia de Rollback
- Tratar el Auto-Update como un control de seguridad
- Runbook y checklist para actualizaciones en producción
El incidente de la actualización a las 2 a. m. que originó esta guía
El release pasó CI y parecía normal. El viernes a última hora, un desarrollador hizo push de un build de Electron sin firmar, y el job de publish subió suficientes assets para que el release pareciera completo. La aplicación arrancó en testing, pero nadie había probado el flujo de update desde un build de producción instalado.
A las 2:08 a. m., PagerDuty alertó al ingeniero de guardia. Un nuevo flujo de autenticación falló en parte de la flota, y los usuarios que recibieron el update no podían iniciar sesión. Otros usuarios se quedaron en la versión anterior porque el updater no podía verificar ni instalar el artefacto. Algunos clientes tenían un release roto, mientras que el resto de la flota ejecutaba una versión distinta sin explicación clara.
La investigación siguió cinco comprobaciones:
- Revisar el feed del release. El binario existía, pero los metadatos esperados no establecían claramente qué clientes debían recibirlo. Un manifest es un contrato entre el pipeline de release y los clientes instalados, no un detalle opcional del upload.
- Inspeccionar el signing. La versión no se firmó correctamente, por lo que la verificación falló en las plataformas afectadas. El firmado debe bloquear la publicación si falta o es inválido.
- Comparar los logs del cliente. Los errores de actualización nunca llegaban a la telemetría central. La aplicación ignoraba el evento y seguía funcionando, dejando al equipo sin evidencia fiable.
- Revisar controles de rollout. No había canales internos ni cohortes escalonadas. Todos los clientes aptos usaron el mismo feed, por lo que el fallo se propagó sin un punto de contención.
- Buscar un rollback. El equipo no tenía un procedimiento probado para republicar la versión anterior o redirigir a los clientes lejos de la versión defectuosa.
La documentación oficial de Electron deja claras las limitaciones de la plataforma. Linux no tiene soporte nativo para auto-updater, y las solicitudes de actualización en macOS deben cumplir con los requisitos de App Transport Security. La documentación también señala el firmado como un requisito previo para actualizaciones de macOS fiables y la verificación de releases. La documentación de autoUpdater de Electron define las restricciones de API, mientras que el sistema de release debe aplicar los controles operativos circundantes.
Lección postmortem: Un updater que no puede explicar qué pasó es un intento de instalación remota sin telemetría.
El coste fue más allá del tiempo de ingeniería. Los clientes perdieron la confianza en el cliente de escritorio, soporte tuvo que explicar comportamientos inconsistentes y el equipo pasó la siguiente jornada reconstruyendo un proceso de release que debería haber existido antes del incidente.
Trata el auto-update como un sistema operativo. La firma es un gate de release, los manifests definen el contrato del cliente, los rollout channels limitan la exposición y el rollback sigue siendo un camino probado en lugar de un invento de emergencia.
Cómo elegir el camino correcto para Electron Updater
A las 2 a.m., elegir el updater equivocado se convierte en un problema operativo. Una actualización de un binario nativo debe gestionar la firma, los manifests, los instaladores y el rollback. Un cambio de JavaScript o CSS solo en el renderer sigue un camino distinto. Las restricciones de hosting también importan: un proyecto pequeño alojado en GitHub no necesita los mismos controles de release que un servicio de distribución enterprise.
Para proyectos que usan electron-builder con publicación de artifacts firmados, electron-updater suele ser el default práctico. Su ecosistema cubre targets de publicación, release manifests, descargas de artifacts e instalación en el siguiente launch. Soporta varios modelos de hosting, pero tu equipo sigue siendo dueño de la firma, la disponibilidad del feed, la política de channels, los controles de rollout y el monitoreo. La integración de Electron updater para Capgo es relevante al evaluar un modelo de entrega híbrido para bundles de la capa web junto con lanzamientos nativos.
update-electron-app se adapta a equipos que buscan una integración ligera con GitHub Releases. Realiza comprobaciones al inicio y luego en intervalos recurrentes, lo que simplifica la configuración pero ofrece menos flexibilidad para la selección de canales, despliegues graduales y reglas de rollback personalizadas. El paquete es adecuado para procesos de release pequeños, siempre que GitHub Releases y su disponibilidad se ajusten a tus necesidades operativas.
| Opción | Control de Hosting | Soporte de Firma | Canales y Rollouts Graduales | Carga de Mantenimiento |
|---|---|---|---|---|
| electron-updater | S3, GitHub, HTTPS genérico y otros targets de publicación | Se integra con la firma de releases empaquetados | Base sólida; la política personalizada suele gestionarse mediante el feed | Moderado |
| actualizar-app-electron | Principalmente flujos de GitHub Releases simples | Usa el modelo de firma de Electron | Limitado sin servicios adicionales | Bajo |
| Squirrel.Windows o Squirrel.Mac | Flujo de distribución por plataforma | Depende de requisitos de firma de la plataforma | Posible, pero requiere infraestructura de release extra | Moderado para aplicaciones legacy |
| Servicio personalizado | Control total de manifests, auth, cohorts y feeds | Tú controlas el diseño de verificación y las claves | Máxima flexibilidad | Alto |
| Live Updates de Capgo | Entrega gestionada para bundles de la capa web | Usa su propio modelo de updater y entrega | Segmentación de audiencia y entrega por canales | Modelo operativo separado de las actualizaciones nativas |
Un servicio personalizado como Hazel, Nuts o un feed interno es ideal cuando el coste de implementación se justifica por la autorización de lanzamientos, segmentación de tenants, registros de auditoría o reglas de despliegue reguladas. El compromiso es la gestión continua. Tu equipo deberá definir la semántica del manifest, proteger las claves de firma, mantener la compatibilidad con el cliente y testear descargas fallidas, lanzamientos rechazados y el comportamiento de rollback.
Con Live Update puedes enviar cambios solo del renderer sin reconstruir el shell nativo. No sustituyen a las actualizaciones binarias cuando cambian Electron, los módulos nativos, los permisos o el comportamiento del instalador. Usa electron-updater a menos que necesites una lógica de rollout personalizada. Si necesitas lógica personalizada, construye sobre convenciones de manifest y artifacts ya establecidas en lugar de recrear el comportamiento de descarga y differential-update. El camino más fiable es aquel que tu equipo pueda observar, preparar y revertir bajo presión.
Implementar el flujo de Auto-Update en el main process
El main process debe encargarse de las comprobaciones y la instalación de actualizaciones. El renderer puede mostrar el estado, pero no debe decidir si una actualización del ejecutable es de confianza ni cuándo se cierra la aplicación.
Configura primero el objetivo de publicación
Una configuración mínima de electron-builder podría verse así:
{
"build": {
"appId": "com.example.desktop",
"publish": [
{
"provider": "s3",
"bucket": "example-electron-releases",
"channel": "stable"
}
],
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
}
Mantén separados los feeds beta y stable. Un channel es una política de release, no una etiqueta en la UI. Cada channel debe apuntar al artifact firmado y al manifest correctos.
Programa comprobaciones y expón eventos de ciclo de vida
Llamar checkForUpdates() solo durante el arranque es un error común en producción. Un usuario puede dejar la aplicación abierta días, así que el main process necesita un intervalo controlado y una estrategia de reintentos que respete el modo offline.
const { app, BrowserWindow, ipcMain } = require('electron');
const { autoUpdater } = require('electron-updater');
let mainWindow;
let isQuitting = false;
let retryDelay = 60 * 1000;
function sendUpdateStatus(status, payload = {}) {
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send('update-status', { status, ...payload });
}
}
function scheduleUpdateCheck() {
setTimeout(async () => {
try {
await autoUpdater.checkForUpdates();
retryDelay = 60 * 1000;
} catch (error) {
sendUpdateStatus('error', { message: error.message });
retryDelay = Math.min(retryDelay * 2, 30 * 60 * 1000);
}
scheduleUpdateCheck();
}, retryDelay);
}
app.whenReady().then(() => {
mainWindow = new BrowserWindow({
webPreferences: {
preload: require('path').join(__dirname, 'preload.js')
}
});
autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.on('checking-for-update', () => {
sendUpdateStatus('checking');
});
autoUpdater.on('update-available', info => {
sendUpdateStatus('available', { version: info.version });
});
autoUpdater.on('download-progress', progress => {
sendUpdateStatus('progress', { percent: progress.percent });
});
autoUpdater.on('update-downloaded', info => {
sendUpdateStatus('downloaded', { version: info.version });
});
autoUpdater.on('error', error => {
sendUpdateStatus('error', { message: error.message });
});
autoUpdater.checkForUpdates().catch(error => {
sendUpdateStatus('error', { message: error.message });
});
scheduleUpdateCheck();
});
ipcMain.handle('install-update', () => {
isQuitting = true;
autoUpdater.quitAndInstall(false, true);
});
app.on('before-quit', event => {
if (!isQuitting) {
return;
}
});
El comportamiento exacto del evento de actualización varía según la plataforma y la configuración de empaquetado, así que prueba desde artifacts instalados en lugar del modo desarrollo. La documentación de Electron también señala problemas de timing en el arranque en Windows, incluido el caso de primera ejecución de Squirrel. No dispares una comprobación de actualización antes de que la aplicación complete la inicialización específica de la plataforma que necesita.
Mantén informado al renderer sin bloquear el trabajo
El preload bridge debe exponer un API limitado:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('updates', {
onStatus(callback) {
ipcRenderer.on('update-status', (_event, status) => callback(status));
},
install() {
return ipcRenderer.invoke('install-update');
}
});
Una barra de progreso en el renderer puede ser deliberadamente simple:
window.updates.onStatus(status => {
const progress = document.querySelector('#update-progress');
const message = document.querySelector('#update-message');
if (status.status === 'progress') {
progress.hidden = false;
progress.value = status.percent;
message.textContent = `Downloading update, ${Math.round(status.percent)}%`;
}
if (status.status === 'downloaded') {
message.textContent = `Version ${status.version} is ready to install`;
}
if (status.status === 'error') {
message.textContent = 'The update could not be downloaded. We will retry later.';
}
});
Controla la instalación tras el consentimiento del usuario en producción, a menos que tu app deba reiniciarse de inmediato. Configura un isQuitting flag antes de quitAndInstall(), ya que los handlers de cierre de ventana podrían impedir que el instalador tome el control.

Dos fallos requieren tests explícitos. Primero, un cliente en ejecución debe llamar checkForUpdates() periódicamente, no solo al iniciar. Segundo, el error evento debe llegar a los logs y la telemetría. Si la app lo ignora sin reportarlo, tu workflow de troubleshooting de la app se basará en suposiciones en lugar de evidencia.
Configuración de CI/CD para Manifests y Releases Firmadas
El release pipeline es la fuente de verdad de lo que instalan los usuarios. Un build local que funciona en la máquina de un dev no garantiza que el binario, manifest, firma y channel publicados correspondan a la misma release.
El modelo de publish de electron-builder espera que los metadatos de la release y el target de update viajen juntos. En muchas configuraciones, esto implica un artifact como latest.yml para Windows y latest-mac.yml para macOS, junto con los paquetes específicos de la plataforma y los archivos blockmap. Un manifiesto ausente puede hacer que un binario perfectamente válido sea invisible para los clientes.
Haz que la firma sea explícita en CI
Un patrón simplificado de GitHub Actions se ve así:
name: release
on:
push:
tags:
- "v*"
jobs:
build:
strategy:
matrix:
os: [macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run test
- run: npm run build
- name: Build and publish
shell: bash
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: npx electron-builder --publish always
Usa secrets específicos de la plataforma y mantén el material de firma fuera del repositorio. Un fallo en la firma debe detener el job, no generar un fallback sin firmar que alguien suba manualmente.
| Variable | Propósito |
|---|---|
CSC_LINK |
Certificado de macOS o referencia del certificado |
CSC_KEY_PASSWORD |
Contraseña para el material de firma de macOS |
WIN_CSC_LINK |
Certificado de Windows o referencia del certificado |
AWS_ACCESS_KEY_ID |
Credencial de publicación con acceso limitado |
AWS_SECRET_ACCESS_KEY |
Secret vinculado a la credencial de publicación |
La configuración de publicación debe identificar el proveedor y el canal de forma consistente:
{
"build": {
"publish": {
"provider": "s3",
"bucket": "example-electron-releases",
"channel": "stable",
"publishAutoUpdate": true,
"updaterCacheDirName": "example-desktop-updater"
}
}
}
Antes de publicar, condiciona el job a la versión del paquete, el tag, el commit SHA y el resultado del smoke-test. Tras la publicación, verifica que el feed contenga el manifest esperado y que este apunte al artifact exacto generado por ese job. La guía de configuración de CI/CD es útil al formalizar esos checks, y los equipos que comparan la orquestación de pipelines también pueden beneficiarse al entender cuándo usar Jenkins y Ansible juntos.
Los comandos que suelen exponer un release incompleto son intencionadamente aburridos:
npx electron-builder --publish never
test -f dist/latest.yml
test -f dist/latest-mac.yml
find dist -name "*.blockmap" -print
Esos checks no sustituyen a un test de instalación firmado. Pero sí detectan el error operativo de hacer upload de un binario sin los metadatos que los clientes necesitan para descubrirlo.
Rollouts, Channels y estrategia de Rollback
Un feed de releases debería comportarse más como un target de deployment que como una carpeta de descarga. Mantén interno, beta, y Último los canales deben estar separados, cada uno respaldado por su propio manifiesto y conjunto de artefactos firmados. La promoción debe mover una versión probada entre políticas, no sobrescribir un archivo mientras los clientes lo descargan.
La separación de canales también protege a producción de builds de prueba accidentales. El updater debe saber si un cliente pertenece a un grupo interno, a una audiencia beta o a la población estable antes de evaluar el feed.
Usa cohorts antes de un despliegue masivo
Un campo personalizado en el manifiesto puede permitir una entrega escalonada:
version: 4.8.0
path: Example-Setup-4.8.0.exe
sha512: signed-artifact-hash
rolloutPercentage: 10
El proceso principal puede asignar un bucket estable por usuario y luego compararlo con rolloutPercentage. La asignación estable es clave. Un usuario que alterna entre estados elegibles e inelegibles en cada comprobación tendrá un comportamiento impredecible, dificultando la interpretación de los reportes de soporte.
Expande el cohort solo después de que la versión haya superado su ventana de observación. La ventana exacta debe reflejar tu patrón de uso, pero la decisión debe basarse en señales, no solo en el calendario. Rastrea resultados de update-check, descargas completadas, salud del lanzamiento, crashes, excepciones del renderer y éxito de autenticación.
| Señal | Acción | Motivo |
|---|---|---|
| Los errores de feed o de firma superan el límite aprobado por el equipo | Pausar despliegue | Los clientes podrían no validar o detectar la versión |
| Los checks de post-actualización fallan al iniciar | Revertir feed | El binario puede instalarse pero fallar al arrancar |
| Aumentan las excepciones del renderer tras la promoción | Mantener en el cohort actual | El instalador nativo puede estar bien mientras que el nuevo code no lo está |
| Las métricas se mantienen en el presupuesto de la versión | Expandir cohort | La evidencia permite una mayor exposición |
No confundas un rollback con borrar un artifact. Los clientes actuales pueden tener metadatos en caché y algunos ya podrían estar usando la versión errónea. Un plan de rollback requiere una versión firmada previa, un cambio en el feed y un comportamiento del cliente que permita la recuperación.
Regla operativa: El rollback debe poder ejecutarlo el ingeniero de guardia sin necesidad de reconstruir la aplicación durante el incidente.
En la práctica, el runbook debería promover el manifiesto de la versión anterior al canal afectado, invalidar el marcador de staging y confirmar que los nuevos checks apunten a la versión segura. Si el problema está en el renderer code y no en el native shell, un rollback dirigido a la capa web puede ser más rápido. Una plataforma como lanzamientos graduales de Capgo puede ser relevante para esa capa de entrega independiente, pero no debe desdibujar el límite entre un rollback de binario nativo y uno de web-bundle.
Tratar el Auto-Update como un control de seguridad
Un updater de Electron descarga ejecutables code y puede instalarlos con mínima interacción del usuario. Eso convierte la ruta de actualización en un límite de seguridad, no solo en una funcionalidad de conveniencia. La documentación oficial de Electron describe restricciones de plataforma como macOS ATS, y la cobertura de seguridad ha documentado un escenario en 2022 donde atacantes que controlaban la infraestructura de updates podían servir paquetes maliciosos que aún pasaban los checks de code-signing, como se analiza en la documentación de seguridad de auto-update de Electron Builder.
El firmado de Code sigue siendo fundamental, pero no es todo el modelo de confianza. Firma cada release, verifica el certificado y la identidad durante el CI y mantén un procedimiento documentado de rotación de claves. En macOS, combina el firmado con la notarización y el hardened runtime adecuado para tu aplicación. En Windows, asegúrate de que la propiedad, la renovación y el acceso al build del certificado sean auditables. Linux requiere una estrategia específica de la distribución, ya que Electron no ofrece un updater universal integrado allí.
Protege los metadatos con el mismo cuidado que el binario
Un binario firmado puede asociarse con el release incorrecto si el canal de metadatos está comprometido o mal configurado. Considera añadir una firma de manifiesto verificada contra una clave pública embebida en la app, exige una versión mínima permitida y rechaza downgrades inesperados a menos que una ruta de recuperación autorizada lo permita explícitamente.
El feed también merece controles de producción:
- Restringe el acceso de publicación: Dale al CI solo los permisos necesarios para publicar los assets del release.
- Protege los secrets de firma: Mantén los certificados y las claves privadas en un gestor de secrets, no en archivos del repositorio.
- Fija las dependencias: Fija las versiones de Electron, electron-builder y dependencias transitivas en CI.
- Revisa los artifacts: Escanea los paquetes generados y compáralos con el commit y la versión previstos.
- Exigir transporte seguro: Sigue los requisitos de ATS y HTTPS estricto para las solicitudes de actualización.
- Monitorear fallos de verificación: Trata los fallos repetidos de firma o manifiesto como eventos de seguridad, no como ruido de red común.
El ecosistema de herramientas de Electron sigue añadiendo cobertura de empaquetado y updater, pero el mantenimiento no elimina la necesidad de realizar un modelado de amenazas. El objetivo práctico es asegurar que un atacante que comprometa un bucket, CDN o paso de build no pueda lograr que el cliente acepte un release no autorizado. La guía de verificación de firmas aporta contexto útil para diseñar esa capa de verificación adicional.

El Runbook y Checklist de Actualización en Producción
Un release solo está listo cuando otro ingeniero puede operarlo bajo presión. Mantén el checklist cerca del job de deployment y del canal de incidentes.
Gates de prelanzamiento
- Identidad de la versión: Confirma que la versión del paquete, el tag de release, el commit SHA y el changelog coinciden.
- Firma: Verifica que cada artefacto de la plataforma esté firmado y que la notarización o validación equivalente haya terminado.
- Contrato del manifiesto: Confirma
latest.yml,latest-mac.yml, los hashes, rutas y blockmaps coinciden con los artefactos subidos. - Seguridad del canal: Publica en el feed interno o beta antes de promover el canal de release.
- Telemetría: Se confirman las comprobaciones de actualización, el progreso de descarga, la finalización de la instalación, el estado de inicio y los errores.
Canary y rollout completo
- Control de cohortes: Empieza con una audiencia interna o beta muy reducida.
- Presupuesto de salud: Detén la promoción si los fallos de lanzamiento, descargas de updates, excepciones del renderer o errores de autenticación superan los límites aprobados.
- Aprobación de promoción: Requiere una decisión explícita de go/no-go antes de pasar el release al feed estable.
- Impacto en el cliente: Prepara los mensajes de soporte antes de un despliegue masivo, no después del primer incidente.
Respuesta ante incidentes
Si el nuevo binario no arranca, falla el spawning de procesos o las descargas de updates no terminan, detén la promoción de inmediato. Restaura el manifest firmado anterior, invalida el marcador de staging y verifica que los nuevos clientes resuelvan a la versión previa. Confirma mediante telemetría que el fleet se está recuperando antes de comunicar el cierre.
Los comandos exactos de rollback dependen de tu proveedor, pero la secuencia siempre debe estar documentada: cambia el manifest del channel a la versión anterior, invalida el tag de staging, lanza un marcador de forced-update si la recuperación lo requiere y verifica el downgrade path con telemetría en vivo. Un rollback que no ha sido probado es solo una esperanza.

Capgo ofrece un updater para Electron que permite entregar cambios firmados en la capa web, canales específicos, controles de rollout y observabilidad de actualizaciones sin tener que hacer un Native Build por cada cambio en el renderer. Si quieres separar los releases del binario nativo de la entrega controlada de JavaScript y CSS, visita Capgo y evalúalo junto a tu pipeline de release de Electron actual.