WSL is useful when an admin workflow lives between Windows and Linux. Sometimes I want Linux-style scheduling with cron, but the actual work still needs to happen on Windows: start a Windows Scheduled Task, run a PowerShell script, trigger a deployment task, or call a remote Windows server.

This pattern is not a replacement for a real job scheduler. It is a practical bridge. WSL cron can keep a Linux-style schedule, and each cron entry can call Windows tools such as powershell.exe, schtasks.exe, or wsl.exe path-aware scripts.

Quick answer

To use WSL cron for Windows scheduled jobs, enable cron inside WSL, create cron entries that call Windows commands such as schtasks.exe or powershell.exe, and log the output to files under WSL or Windows. For local Windows tasks, call schtasks.exe /Run /TN "TaskName" from cron. For remote Windows servers, call schtasks.exe /Run /S SERVER /TN "TaskName" or use PowerShell remoting. To make this reliable after reboot, enable systemd or cron in WSL, create a Windows startup scheduled task that launches the WSL distro, and keep a lightweight long-running process or systemd service active so WSL does not terminate.


When this pattern makes sense

I use this pattern when the schedule is easier to manage from Linux, but the target action is Windows-based.

Good examples:

  • Run a Windows Scheduled Task every night from a WSL cron schedule.
  • Trigger a local Windows cleanup task from a Linux script.
  • Start a remote Windows task from a WSL admin box.
  • Use Bash to prepare files, then call PowerShell for Windows work.
  • Keep cross-platform automation in one WSL environment.

Poor examples:

  • High-availability production scheduling.
  • Compliance jobs that require central reporting.
  • Jobs that must survive Windows shutdown without missed-run handling.
  • Large enterprise deployment where Intune, Configuration Manager, Ansible, or an RMM already exists.

WSL cron is useful, but Windows still owns the host. If Windows is off, rebooting, patched, or sleeping, WSL cron is not running.


Enable cron in WSL

On Ubuntu in WSL, install cron if it is missing:

sudo apt update
sudo apt install cron -y

If your WSL distro uses systemd, enable and start cron:

sudo systemctl enable cron
sudo systemctl start cron
sudo systemctl status cron

If systemd is not enabled, you can start cron with the service command:

sudo service cron start
sudo service cron status

For modern WSL, I prefer systemd because services behave more like a normal Linux machine. Check /etc/wsl.conf:

[boot]
systemd=true

After changing /etc/wsl.conf, shut down WSL from Windows and start it again:

wsl --shutdown
wsl -d Ubuntu-24.04

Create a cron log folder

Cron jobs should write logs. Without logs, troubleshooting becomes guesswork.

In WSL:

mkdir -p ~/cron-logs

A cron command can redirect output like this:

* * * * * echo "$(date) cron is running" >> ~/cron-logs/heartbeat.log 2>&1

Edit the current user’s crontab:

crontab -e

After saving, confirm cron picked it up:

crontab -l
tail -f ~/cron-logs/heartbeat.log

Run a local Windows Scheduled Task from WSL cron

From WSL, Windows executables are available through the Windows path. You can call schtasks.exe directly.

First test manually from WSL:

schtasks.exe /Run /TN "PwshTips-Maintenance"

If the task lives inside a folder in Task Scheduler, include the full task path:

schtasks.exe /Run /TN "\PwshTips\Daily-Maintenance"

Then add it to cron:

0 2 * * * /mnt/c/Windows/System32/schtasks.exe /Run /TN "\PwshTips\Daily-Maintenance" >> /home/sea/cron-logs/windows-task.log 2>&1

This runs the Windows Scheduled Task every day at 2 AM according to the Linux cron schedule inside WSL.

Important detail: cron runs with a small environment. Use full paths such as /mnt/c/Windows/System32/schtasks.exe and full log paths.


Run a PowerShell command from WSL cron

You can also call Windows PowerShell from cron:

30 2 * * * /mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Nightly-Job.ps1" >> /home/sea/cron-logs/powershell-job.log 2>&1

For PowerShell 7, call pwsh.exe if it is installed and in a known path:

30 2 * * * /mnt/c/Program\ Files/PowerShell/7/pwsh.exe -NoProfile -File "C:\Scripts\Nightly-Job.ps1" >> /home/sea/cron-logs/pwsh-job.log 2>&1

Quoting matters. Test the command manually in WSL before putting it in cron.


Run a task on a remote Windows server

If the remote server allows Task Scheduler remote management and your current Windows credentials have rights, schtasks.exe can trigger a remote task.

Manual test from WSL:

schtasks.exe /Run /S SERVER01 /TN "\PwshTips\Daily-Maintenance"

Cron entry:

0 3 * * * /mnt/c/Windows/System32/schtasks.exe /Run /S SERVER01 /TN "\PwshTips\Daily-Maintenance" >> /home/sea/cron-logs/remote-task-SERVER01.log 2>&1

If you need explicit credentials, schtasks.exe supports /U and /P, but I avoid putting passwords in crontab. A crontab is not a password vault. Prefer running under an account that already has the correct rights, or use a proper automation platform.


Use PowerShell remoting from WSL

Another option is to call Windows PowerShell or PowerShell 7 and use remoting from there.

Example cron command:

15 3 * * * /mnt/c/Program\ Files/PowerShell/7/pwsh.exe -NoProfile -Command "Invoke-Command -ComputerName SERVER01 -ScriptBlock { Start-ScheduledTask -TaskName 'PwshTips-Maintenance' }" >> /home/sea/cron-logs/remote-pwsh.log 2>&1

This is easier to extend when you want richer logic:

  • check whether the remote server is online
  • start the task
  • wait for the task to complete
  • collect the task result
  • export a CSV report

For larger scripts, do not put everything in the cron line. Put the PowerShell logic in a .ps1 file and call the file from cron.


Start WSL automatically when Windows boots

Cron inside WSL will not run until the WSL distro starts. On a server, I normally create a Windows Scheduled Task that starts WSL at boot or at user logon.

Run this in an elevated PowerShell window on Windows:

$action = New-ScheduledTaskAction `
    -Execute 'wsl.exe' `
    -Argument '-d Ubuntu-24.04 --exec /bin/bash -lc "systemctl start cron; nohup sleep infinity >/dev/null 2>&1"'

$trigger = New-ScheduledTaskTrigger -AtStartup

Register-ScheduledTask `
    -TaskName 'Start-WSL-Cron' `
    -Action $action `
    -Trigger $trigger `
    -Description 'Start Ubuntu WSL and keep cron available after Windows boot' `
    -User 'SYSTEM' `
    -RunLevel Highest `
    -Force

This starts the WSL distro after Windows boots and runs a lightweight process so the distro does not immediately exit.

If systemctl start cron does not work under the account running the task, use systemd enablement inside WSL instead:

sudo systemctl enable cron

Then simplify the Windows startup task:

$action = New-ScheduledTaskAction `
    -Execute 'wsl.exe' `
    -Argument '-d Ubuntu-24.04 --exec /bin/bash -lc "nohup sleep infinity >/dev/null 2>&1"'

The enabled cron service should start when the distro starts.


Keep WSL alive

WSL can stop when no Linux processes are running. If cron is running under systemd, that may be enough. If your distro still exits, keep one harmless long-running process active.

Create a small keep-alive script inside WSL:

mkdir -p ~/bin
nano ~/bin/wsl-keepalive.sh

Content:

#!/usr/bin/env bash

while true; do
    date >> "$HOME/cron-logs/wsl-keepalive.log"
    sleep 3600
done

Make it executable:

chmod +x ~/bin/wsl-keepalive.sh

Then point the Windows startup task at it:

$action = New-ScheduledTaskAction `
    -Execute 'wsl.exe' `
    -Argument '-d Ubuntu-24.04 --exec /home/sea/bin/wsl-keepalive.sh'

$trigger = New-ScheduledTaskTrigger -AtStartup

Register-ScheduledTask `
    -TaskName 'Keep-WSL-Alive' `
    -Action $action `
    -Trigger $trigger `
    -User 'SYSTEM' `
    -RunLevel Highest `
    -Force

If you run WSL as a specific user instead of SYSTEM, adjust paths and permissions. Test after a reboot, not only from an already-open terminal.


Check whether WSL and cron are running

From Windows:

wsl --list --running
wsl -d Ubuntu-24.04 --exec bash -lc "systemctl is-active cron || service cron status"

From WSL:

ps aux | grep cron
crontab -l
tail -n 20 ~/cron-logs/heartbeat.log

If a Windows task is not starting, check both sides:

  • WSL cron log
  • Windows Task Scheduler history
  • the Windows task’s own script log
  • permissions of the account running the startup task
  • whether the WSL distro name is correct

List WSL distros:

wsl --list --verbose

Use the exact distro name in wsl.exe -d.


Practical cautions

There are a few traps with this setup.

First, cron time comes from the WSL environment, but the Windows task runs on the Windows host. Keep time zones simple and confirm both sides agree.

Second, cron does not load your interactive shell profile the same way a terminal does. Use full paths.

Third, avoid storing Windows passwords in crontab. If credentials are needed, use a proper service account design, managed secrets, or a real automation platform.

Fourth, WSL is not the same as a Windows service. It is reliable enough for many admin workflows, but I would not use it as the only scheduler for critical production work without monitoring.


Final pattern

My preferred pattern is:

  1. Enable systemd in WSL.
  2. Enable cron with sudo systemctl enable cron.
  3. Create cron jobs that call schtasks.exe, powershell.exe, or pwsh.exe with full paths.
  4. Log every cron job under ~/cron-logs.
  5. Create a Windows startup Scheduled Task that launches the WSL distro after boot.
  6. Keep WSL alive with systemd services or a lightweight keep-alive process.
  7. Test local and remote Windows task execution manually before relying on cron.

This gives a useful bridge: Linux cron controls the schedule, while Windows Scheduled Tasks do the Windows work.