The usual way to run a bot on a Windows VPS is to start it in an RDP session and close the window. It works until an update reboots the server at 3 a.m. and nothing starts it again. Task Scheduler, which is already on every Windows Server, fixes this with no extra software. The setup below:
- starts the script at boot, before anyone logs in;
- restarts it within a minute if it crashes or is killed;
- never runs two copies at once.
We tested all three on our Windows Server 2022 image: killed the process, rebooted the server, and counted instances.
The example script
A stand-in for your bot: it writes a line every 10 seconds, so it's easy to see whether it's alive.
New-Item -ItemType Directory -Force C:\bots | Out-Null
Set-Content C:\bots\heartbeat.ps1 -Encoding ascii -Value @'
while ($true) {
Add-Content -Path C:\bots\heartbeat.log -Value ("{0:u} pid {1}" -f (Get-Date), $PID)
Start-Sleep -Seconds 10
}
'@
Register the task
Run in an elevated PowerShell:
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NoProfile -ExecutionPolicy Bypass -File C:\bots\heartbeat.ps1' -WorkingDirectory 'C:\bots'
$atBoot = New-ScheduledTaskTrigger -AtStartup
$everyMin = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew -StartWhenAvailable
Register-ScheduledTask -TaskName 'mybot' -Action $action -Trigger $atBoot, $everyMin -Settings $settings -User 'SYSTEM' -RunLevel Highest
Start-ScheduledTask -TaskName 'mybot'
What each piece does:
-AtStartupstarts the bot when Windows boots, with no logged-in user.- The one-minute repeat re-runs the task every minute. If the bot is alive, nothing happens; if it died, it starts again.
-MultipleInstances IgnoreNewis what makes the repeat safe: a run that finds the bot already running is skipped.-ExecutionTimeLimit 0turns off the default 72-hour limit, after which Task Scheduler would stop the bot.-User 'SYSTEM'runs it without a password or a session. SYSTEM has full rights on the server, so run only your own code this way.
For Python, replace the action:
$action = New-ScheduledTaskAction -Execute 'C:\Program Files\Python312\python.exe' -Argument 'C:\bots\bot.py' -WorkingDirectory 'C:\bots'
What we checked
Crash. We killed the bot's process with Stop-Process -Force. 46 seconds later the minute trigger had started a new one, and there was exactly one copy.
Reboot. We restarted the server and didn't log in. 40 seconds after boot the bot was writing to its log, with no user sessions on the server.
What didn't work. Two things look right and aren't:
- "If the task fails, restart every…" We set it, killed the process, and the task sat in Ready for minutes. It reacts to the task failing to start, not to your program dying later.
- A wrapper loop (
while ($true) { & bot; sleep 5 }). It restarts a crashed bot, but if the wrapper itself dies, its child keeps running, and the next start launches a second bot. We ended up with two copies, which for a Telegram bot means two pollers fighting over the same updates.
Check on it
Get-ScheduledTask -TaskName mybot | Select-Object TaskName, State
Get-ScheduledTaskInfo -TaskName mybot | Select-Object LastRunTime, NextRunTime
Get-Content C:\bots\heartbeat.log -Tail 5
To stop it for maintenance, Disable-ScheduledTask mybot first (otherwise the minute trigger starts it again), then stop the process. Enable-ScheduledTask mybot brings it back.
Logs
The task doesn't capture output. Have the bot write its own log file, as the example does, or redirect output in a small .cmd launcher and point -Execute at that file.
Which plan
A PowerShell or Python bot uses little CPU, but Windows itself takes 1 to 1.3 GB of RAM at idle on our image. Windows-Small (4 vCPU, 4 GB RAM, 50 GB, $11/mo) is the right size for a bot or two. On a NAT plan, outgoing connections (Telegram, exchange APIs, webhooks you call) work as usual. If the bot needs to receive webhooks or other inbound traffic, take Windows Small-IP ($28/mo) with its own IPv4.
Next steps: lock down RDP and, if you'd rather manage the bot over SSH, install OpenSSH. Payment is in USDT, USDC or ETH, and Windows is Server 2022 with your own licence (BYOL). All plans: Windows VPS.
Comments
No comments yet. Be the first.