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 -yIf your WSL distro uses systemd, enable and start cron:
sudo systemctl enable cron
sudo systemctl start cron
sudo systemctl status cronIf systemd is not enabled, you can start cron with the service command:
sudo service cron start
sudo service cron statusFor modern WSL, I prefer systemd because services behave more like a normal Linux machine. Check /etc/wsl.conf:
[boot]
systemd=trueAfter changing /etc/wsl.conf, shut down WSL from Windows and start it again:
wsl --shutdown
wsl -d Ubuntu-24.04Create a cron log folder
Cron jobs should write logs. Without logs, troubleshooting becomes guesswork.
In WSL:
mkdir -p ~/cron-logsA 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 -eAfter saving, confirm cron picked it up:
crontab -l
tail -f ~/cron-logs/heartbeat.logRun 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 `
-ForceThis 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 cronThen 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.shContent:
#!/usr/bin/env bash
while true; do
date >> "$HOME/cron-logs/wsl-keepalive.log"
sleep 3600
doneMake it executable:
chmod +x ~/bin/wsl-keepalive.shThen 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 `
-ForceIf 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.logIf 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 --verboseUse 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:
- Enable systemd in WSL.
- Enable cron with
sudo systemctl enable cron. - Create cron jobs that call
schtasks.exe,powershell.exe, orpwsh.exewith full paths. - Log every cron job under
~/cron-logs. - Create a Windows startup Scheduled Task that launches the WSL distro after boot.
- Keep WSL alive with systemd services or a lightweight keep-alive process.
- 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.
💬 Comments