Fixing clock drift in WSL2 using Windows Terminal

Summary

When you try to update your Linux installation using commands like apt update then, if your system clock is wrong, you may encounter errors like this:

E: Release file for http://archive.ubuntu.com/ubuntu/dists/focal-updates/InRelease is not valid yet (invalid for another 1d 3h 15min 12s). Updates for this repository will not be applied.
E: Release file for http://archive.ubuntu.com/ubuntu/dists/focal-backports/InRelease is not valid yet (invalid for another 1d 3h 15min 34s). Updates for this repository will not be applied.
E: Release file for http://security.ubuntu.com/ubuntu/dists/focal-security/InRelease is not valid yet (invalid for another 1d 3h 15min 1s). Updates for this repository will not be applied.

It's pretty easy to realise what's gone wrong here. My system clock is just over a day behind the timestamp of the latest update files, so it thinks that those files are not yet valid.

This is not a big problem, just update the system clock and the command will work. However, if the Linux system in question is running inside WSL2 on Windows, then it's not quite as straightforward to fix as it ought to be and you're likely to encounter this error more often than you might expect, especially if you're using nested virtualisation.

However, if you're running Windows Terminal, then you can use a simple menu item I've created to fix it for you. You can also run the fix manually, but I find it significantly easier to use Windows Terminal.

Since running any command doesn't take zero time, rather than making it run by magic every time you try to use your WSL2 environment, I've deliberately made it be something that you have to instigate manually (albeit with a single menu command which can be run with a keyboard shortcut). I considered doing something clever, but this fix feels better and it's very lightweight and also more portable as it should work on any machine running WSL2.

Background

Hyper-V is a great virtualisation environment and I've got it running on an old Dell PowerEdge R720xd in my home lab. One of the nice things about Hyper-V is that I can shut down the physical host server (e.g. through the web interface of Windows Admin Center) and it will save the state of any virtual machines which are running. When I start the server again, those same virtual machines will automatically start running again (this is all configurable, of course, but this is how my system is configured). And their system clocks are synchronised with the host machine, which is something that most hypervisors do.

Cutting a long story (slightly) short, I have a Windows 10 virtual machine running inside Hyper-V server. Then I'm running WSL2 inside Docker inside that virtual machine, so we're talking nested virtualisation (I can't be the only one who's thinking, "It's turtles all the way down"). If I turn off my Hyper-V server for any reason then, when I switch it back on and the virtual machines start running again, my Windows 10 VM has the correct system time, but my WSL2 environment (in my case Ubuntu 20.04) doesn't. The clock restarts from whenever I shut it down. To illustrate this point, I powered off my server just over a day ago. When I switched it back on and logged in to my dev machine, I opened Windows Terminal and ran the following command:

Get-Date; wsl.exe date

This is a simple way of displaying the system date for Windows (by using the PowerShell Get-Date command) and also for WSL2 (by using wsl.exe date to run the date command inside the default WSL environment, which in my case is Ubuntu running inside WSL2).  

This is what I saw:

Over 30 hours of clock drift
11 November 2020 23:14:01
Tue Nov 10 16:41:10 GMT 2020

As you can see, there's over thirty hours of clock drift, which translates precisely to the amount of time my server was not running. That all makes sense.

To prove that this is a problem, I tried to update my WSL2 environment and got the error I mentioned in the summary at the top of this article:

Cannot update WSL2 due to system clock being wrong

The Method

In order to fix this without having to enter the correct date and time manually, you need to install the ntpdate command in your WSL2 environment (e.g. via sudo apt install ntpdate). I tried various ways of setting the system clock automatically and this was the only one that I found worked reliably (I was unable to synchronise it to the host machine). As you might realise, the ntp in the command refers to Network Time Protocol and it means we're going to set the system time from a network time server. I'm using time.windows.com, but you can use any ntp server you like.

Now, whenever your clock is wrong, you need to run the following command:

sudo ntpdate time.windows.com

However, since I use Windows Terminal and I've already created a menu item to update my PowerShell Core installation, I think it's better to build this into a menu item as well.

In order to preserve the look and feel, I copied my existing Ubuntu WSL2 profile and changed the command it runs, but you don't necessarily need to do that; the only important bit is the commandline value, which needs to look like this (although you can use a different time server if you like, of course):

"commandline": "wsl.exe -u root -- ntpdate time.windows.com"

It's basically saying: run the command ntpdate time.windows.com inside my default WSL environment as the root user and then exit.

So your whole profile entry might look like this:

{
    "closeOnExit": false, // keep this if you want to see the result
    "commandline": "wsl.exe -u root -- ntpdate time.windows.com", // use any time server you want here
    "guid": "{1d4de342-38b7-51cf-b940-2309a097f518}", // needs to be unique
    "icon": "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png", // keep this to have Tux, the Linux penguin as your icon
    "name": "Update Ubuntu - WSL 2 - Update system clock",
}

Now, after you've hibernated your system, or if your clock has drifted for other reasons (which it might well do, annoyingly), you can fix it simply by selecting that menu item.

About to update the WSL2 system clock

And, since we told it not to close the window afterwards, you can see the result:

WSL2 system clock updated successfully

And now you'll be able to update your environment using sudo apt update again.

sudo apt update working again

And if you run the original Get-Date; wsl.exe date command again, you'll see everything is fixed.

WSL2 clock synchronised with host

Conclusion

Hypervisors are great, but nested virtualisation can have some unexpected challenges. If you're running WSL2 inside a virtual machine then, if you hibernate that parent virtual machine, your WSL2 system clock will not update automatically when you restart your system. This will cause all sorts of problems and can be painful to fix. By creating a simple menu command for Windows Terminal you can correct your WSL2 system clock with almost no effort and also see how far it had drifted (in my case, this shows me how long I'd had my system switched off).