Backup Windows Server con Task Scheduler: guía programada completa

Configura backup automatizado en Windows Server usando Task Scheduler, robocopy y wbadmin. Carpetas, base SQL Server y rotación de medios.

El backup automatizado en Windows Server no requiere software de pago ni agente propietario. Las herramientas nativas — Task Scheduler, robocopy, wbadmin y el BACKUP DATABASE de SQL Server — cubren el 95% de los escenarios de producción cuando se combinan correctamente. Este tutorial muestra cómo construir una rutina diaria con rotación semanal, registros auditables y cobertura de archivos, estado del sistema y bases de datos relacionales.

La persona aquí es el sysadmin Windows Server que heredó (o está aprovisionando) una máquina con carpetas críticas, quizás un SQL Server, y necesita garantizar un RTO razonable sin depender de Veeam, Backup Exec u otras suites de pago. El foco es la confiabilidad operacional: programación estable, fallas que avisan, registros que puedes auditar dos meses después.

Tiempo estimado de ejecución: 30 a 45 minutos para configurar el primer ciclo completo, incluyendo prueba de restauración. Validamos en Windows Server 2019 y 2022 — los comandos son idénticos. En Windows Server 2016 funciona, pero algunas flags de robocopy tienen comportamiento diferente en paths largos.

Prerrequisitos

Lo que necesitas antes de empezar

Windows Server 2019 o 2022 con acceso administrativo local. Espacio en disco en el destino igual o mayor al doble del volumen de origen (para dos ciclos de backup). Si vas a usar wbadmin, necesitas instalar la característica “Windows Server Backup” vía Server Manager o Install-WindowsFeature Windows-Server-Backup. Para backup de SQL Server, sqlcmd instalado y usuario con permiso BACKUP DATABASE.

Sistema probado Windows Server 2019/2022
Cuenta recomendada Service account dedicada
Destino mínimo 2x el tamaño del origen
Característica opcional Windows-Server-Backup

Antes de crear cualquier tarea, prepara una cuenta de servicio dedicada. No uses Administrador local ni tu cuenta personal — cuando la contraseña cambie o la cuenta sea desactivada, el backup deja de correr silenciosamente. Crea una cuenta como svc_backup en el grupo Administradores locales (o grupo personalizado con permisos específicos en producción), con contraseña fuerte y la flag “La contraseña nunca expira” marcada.

Estructurando el destino del backup

Antes de scripts y tareas, define el layout del destino. Improvisar esto después rompe la rotación y dificulta la auditoría.

01

Crea la estructura de carpetas en el destino — puede ser un disco secundario, recurso compartido de red o volumen montado:

New-Item -Path "D:\Backup\Archivos" -ItemType Directory
New-Item -Path "D:\Backup\SQL" -ItemType Directory
New-Item -Path "D:\Backup\SystemState" -ItemType Directory
New-Item -Path "D:\Backup\Logs" -ItemType Directory

Separar archivos, base y estado del sistema en carpetas distintas facilita la rotación independiente — puedes retener 7 días de archivos pero 30 días de base, por ejemplo.

02

Aplica permisos NTFS restrictivos en el directorio raíz:

icacls "D:\Backup" /inheritance:r
icacls "D:\Backup" /grant:r "Administrators:(OI)(CI)F"
icacls "D:\Backup" /grant:r "svc_backup:(OI)(CI)F"
icacls "D:\Backup" /grant:r "SYSTEM:(OI)(CI)F"

El backup contiene datos sensibles — limitar el acceso a la cuenta de servicio, SYSTEM y Administradores reduce la superficie en caso de compromiso de un usuario común.

Backup de carpetas con robocopy

Robocopy es la herramienta nativa para copia incremental confiable de archivos. A diferencia de xcopy o copy, retoma transferencias interrumpidas, mantiene ACLs y tiene retry configurable — listo para producción.

03

Crea el script C:\Scripts\backup-archivos.ps1:

$origen = "C:\Datos"
$destino = "D:\Backup\Archivos"
$logPath = "D:\Backup\Logs\archivos-$(Get-Date -Format 'yyyy-MM-dd').log"

robocopy $origen $destino /MIR /COPYALL /R:3 /W:10 /MT:8 /LOG:$logPath /NP /TEE

$exitCode = $LASTEXITCODE
if ($exitCode -ge 8) {
    Write-EventLog -LogName Application -Source "BackupScript" -EventId 1001 -EntryType Error -Message "Backup fallo con codigo $exitCode"
    exit 1
}
exit 0

Los parámetros aquí importan. /MIR espeja el origen en el destino (borra archivos que desaparecieron). /COPYALL preserva permisos NTFS, timestamps, owner y auditoría. /R:3 /W:10 intenta 3 veces con 10 segundos entre intentos — equilibra robustez y tiempo. /MT:8 paraleliza con 8 threads.

Cuidado con /MIR en destino compartido

La flag /MIR borra en el destino todo lo que no existe en el origen. Si apuntas dos servidores al mismo destino sin subcarpetas separadas, cada ejecución va a borrar los archivos del otro. Siempre usa destino dedicado por máquina o subcarpeta con nombre del hostname.

04

Registra la fuente del Event Log una vez (para que Write-EventLog funcione):

New-EventLog -LogName Application -Source "BackupScript"

Esto permite que el script escriba en Visor de Eventos > Aplicaciones, integrando con sistemas de monitoreo que leen el log de Windows.

Backup de base SQL Server

Robocopy no puede copiar archivos .mdf/.ldf mientras SQL Server los mantiene abiertos. La solución nativa es el comando BACKUP DATABASE, que genera .bak consistente sin downtime.

05

Crea el script C:\Scripts\backup-sql.ps1:

$fecha = Get-Date -Format 'yyyy-MM-dd_HHmm'
$destino = "D:\Backup\SQL"
$bases = @("MiApp", "PortalCliente")

foreach ($db in $bases) {
    $archivo = "$destino\$db-$fecha.bak"
    $query = "BACKUP DATABASE [$db] TO DISK = '$archivo' WITH COMPRESSION, CHECKSUM, INIT"
    sqlcmd -S localhost -E -Q $query
    if ($LASTEXITCODE -ne 0) {
        Write-EventLog -LogName Application -Source "BackupScript" -EventId 1002 -EntryType Error -Message "Backup SQL fallo: $db"
    }
}

COMPRESSION reduce el tamaño del .bak en 60-80% para la mayoría de los workloads. CHECKSUM verifica la integridad durante el backup — descubres corrupción en el momento correcto, no a la hora de la restauración. INIT sobrescribe medios anteriores si el nombre del archivo coincide.

Agrega COPY_ONLY si existe otro proceso de backup

Si una rutina mayor (full + differential) ya existe para SQL Server, agrega COPY_ONLY en el WITH para no interferir con la cadena de log. Sin esto, tu backup rompe el punto de restauración diferencial de la rutina principal.

Backup del estado del sistema con wbadmin

Para restauración completa del servidor (incluyendo registro, AD, metabase de IIS), usa wbadmin. Complementa a robocopy y SQL — raramente vas a restaurar todo, pero cuando lo necesites, lo vas a necesitar mucho.

06

Verifica que la característica esté instalada:

Install-WindowsFeature Windows-Server-Backup -IncludeManagementTools

Si ya estaba instalada, el comando retorna sin acción. En servidores recién aprovisionados, esto agrega el backup completo + cmdlets de PowerShell.

07

Crea el script C:\Scripts\backup-systemstate.ps1:

$fecha = Get-Date -Format 'yyyy-MM-dd'
$destino = "D:\Backup\SystemState"

wbadmin start systemstatebackup -backupTarget:$destino -quiet

if ($LASTEXITCODE -ne 0) {
    Write-EventLog -LogName Application -Source "BackupScript" -EventId 1003 -EntryType Error -Message "Backup SystemState fallo"
}

El backup de estado del sistema captura registro, base COM+, archivos de boot y (en controladores de dominio) el AD entero. Corre en 5-15 minutos en un servidor típico.

Programando con Task Scheduler

Con los scripts listos, la programación vía Task Scheduler garantiza ejecución no interactiva, con logging propio y retry en caso de falla.

08

Abre Task Scheduler como Administrador (taskschd.msc) y crea una tarea vía “Crear Tarea” (no “Crear Tarea Básica” — esa no expone todas las opciones).

En la pestaña General:

  • Nombre: Backup Diario - Archivos
  • Cuenta: svc_backup (clic en “Cambiar usuario o grupo”)
  • Marca: “Ejecutar tanto si el usuario inició sesión como si no”
  • Marca: “Ejecutar con los privilegios más altos”
  • Configurar para: Windows Server 2022 (o 2019)
09

En la pestaña Desencadenadores, agrega un nuevo desencadenador:

  • Iniciar tarea: Según una programación
  • Diariamente, a las 02:00
  • Marca “Habilitado”

Distribuir backups en horarios diferentes (archivos 02:00, SQL 03:00, system state 04:00) evita pico de I/O y contención en el destino.

10

En la pestaña Acciones, agrega “Iniciar un programa”:

  • Programa/script: powershell.exe
  • Agrega argumentos: -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\backup-archivos.ps1"

-NoProfile salta la carga del perfil de PowerShell (más rápido, más predecible). -ExecutionPolicy Bypass evita bloqueo por política de ejecución restrictiva sin alterar la configuración global.

No uses el campo 'Iniciar en (opcional)' para la ruta del script

Ese campo es el working directory, no la ruta del script. La ruta del script va entera entre comillas dentro de -File "...". Confundir los dos es la causa #1 de tareas que disparan pero el script nunca se ejecuta.

11

En la pestaña Configuración:

  • Marca “Permitir que la tarea se ejecute a petición”
  • Marca “Ejecutar tarea lo antes posible después de un inicio programado perdido”
  • Marca “Si la tarea falla, reiniciar cada: 10 minutos / hasta: 3 veces”
  • “Detener la tarea si se ejecuta durante más de: 4 horas”

Repite el proceso para backup-sql.ps1 (03:00) y backup-systemstate.ps1 (04:00, idealmente solo domingo — system state es grande).

Rotación de backups antiguos

Sin rotación, el destino se llena en semanas. Robocopy /MIR resuelve para el espejo de archivos, pero SQL .bak y wbadmin acumulan.

12

Crea C:\Scripts\rotacion.ps1 para borrar backups SQL con más de 14 días:

$limite = (Get-Date).AddDays(-14)
Get-ChildItem "D:\Backup\SQL\*.bak" | Where-Object { $_.LastWriteTime -lt $limite } | Remove-Item -Force

Get-ChildItem "D:\Backup\Logs\*.log" | Where-Object { $_.LastWriteTime -lt $limite } | Remove-Item -Force

Programa esta tarea para las 05:00 diariamente. wbadmin tiene rotación propia vía -maxBackups: en start systemstatebackup.

Verificación

Programar no basta — necesitas confirmar que el backup corre Y restaura.

Para confirmar ejecución:

Get-ScheduledTask -TaskName "Backup Diario - Archivos" | Get-ScheduledTaskInfo

La salida muestra LastRunTime, LastTaskResult (0 = éxito) y NextRunTime. Cualquier LastTaskResult diferente de 0 señala falla — investiga en el log de Eventos.

Para prueba real de restauración del SQL — esto es innegociable, hazlo mensualmente:

sqlcmd -S localhost -E -Q "RESTORE DATABASE [MiApp_TEST] FROM DISK = 'D:\Backup\SQL\MiApp-2026-05-29_0300.bak' WITH MOVE 'MiApp' TO 'C:\Temp\MiApp_TEST.mdf', MOVE 'MiApp_log' TO 'C:\Temp\MiApp_TEST.ldf', RECOVERY"

Backup nunca probado no es backup — es archivo grande ocupando disco.

Resolución de problemas

La tarea termina con 0x41306 (canceled by user)

Significa que la tarea alcanzó el “Detener la tarea si se ejecuta durante más de X” definido en Configuración. Aumenta el límite o investiga por qué el backup está demorando más de lo esperado — puede ser disco lento, red saturada o volumen creciente.

Robocopy retorna exit code 8 o superior

Los exit codes de robocopy son bitmask. Códigos 0-7 son éxito (con avisos). 8+ indica error real — archivo bloqueado, permiso denegado, destino sin espacio. Siempre revisar $LASTEXITCODE -ge 8 en el script, no -ne 0.

La tarea no dispara aunque el horario sea correcto

Verifica que la cuenta de servicio tenga el derecho “Iniciar sesión como proceso por lotes” (secpol.msc > Derechos de usuario > Iniciar sesión como proceso por lotes). Sin esto, Task Scheduler no puede iniciar el proceso en la cuenta. El error aparece como 0x4 o “tarea lista” eternamente.

Próximos pasos

Con la rutina diaria estable, expande en tres direcciones: replicación del destino hacia storage externo (no confíes en disco local — un incendio borra origen y backup juntos), monitoreo vía Zabbix o similar leyendo el Event Log de las tareas, y ejercicios trimestrales de restauración completa cronometrando el RTO real.

Si estás colocando Windows Server en producción y quieres infraestructura con snapshots a nivel de hipervisor complementando este backup nativo, una VPS Hostini ya viene con volumen principal en SSD NVMe y snapshots programables — útil como segunda capa antes del desastre que nadie quiere enfrentar.

Preguntas frecuentes

¿Puedo usar wbadmin para hacer backup solo de carpetas específicas en lugar del volumen entero?

Sí. El parámetro `-include:` acepta una lista de rutas separadas por comas, como `-include:C:\Datos,C:\Inetpub`. Esto evita generar imagen del volumen completo cuando solo necesitas carpetas específicas. El destino (`-backupTarget:`) sigue requiriendo un disco o recurso compartido de red con espacio suficiente.

Task Scheduler dispara el job pero la tarea termina con código 0x1 — ¿qué revisar primero?

0x1 es genérico de Windows y casi siempre indica problema de permisos o ruta inválida. Confirma que la opción "Ejecutar con los privilegios más altos" esté marcada, que la cuenta usada exista y no esté bloqueada, y que la ruta del script o ejecutable no tenga espacios sin escapar. El registro en `Eventos de Windows > Registros de Aplicaciones y Servicios > Microsoft > Windows > TaskScheduler > Operational` muestra el error real.

¿Robocopy mantiene permisos NTFS y timestamps en el backup?

Los mantiene cuando usas las flags `/COPYALL` (copia datos + atributos + timestamps + seguridad + owner + auditoría) o `/COPY:DAT` para un subconjunto. Sin esas flags, robocopy copia solo datos y atributos básicos, perdiendo las ACL. En servidores de archivos con permisos granulares, `/COPYALL` es obligatorio.

¿Cómo hago rotación semanal manteniendo solo 7 backups diarios sin stack de scripts complejos?

Robocopy no tiene retención nativa, pero puedes combinarlo con `forfiles /P D:\Backup /D -7 /C "cmd /c del @path"` en un paso separado de la tarea programada. Esto borra cualquier archivo con más de 7 días en el destino. Para wbadmin, usa el parámetro `-maxBackups:` o versiona manualmente por carpeta con fecha.

¿El backup de SQL Server requiere detener el servicio o puede correr con la base online?

El backup nativo vía `BACKUP DATABASE` en SQL Server corre 100% online — es el método recomendado y genera archivo `.bak` consistente sin downtime. Intentar copiar los archivos `.mdf`/`.ldf` directamente con robocopy mientras SQL Server está corriendo va a fallar porque los archivos están en uso exclusivo.

¿Puedo programar el backup para que corra incluso cuando nadie está logueado en el servidor?

Sí — esa es la configuración por defecto y recomendada para producción. En la pestaña "General" de la tarea, selecciona "Ejecutar tanto si el usuario inició sesión como si no" y proporciona las credenciales de la cuenta de servicio. Usa una cuenta dedicada con contraseña que no expira para evitar quiebra silenciosa de la programación cuando se cambian contraseñas.

Temas:
Próximos pasos Cloud Ryzen con NVMe y protección DDoS siempre activa.Pon en producción en un VPS Hostini →
¿Te resultó útil este tutorial?
Hablar por WhatsApp