Skip to content

Conversation

@CarlosNihelton
Copy link
Collaborator

@CarlosNihelton CarlosNihelton commented Sep 5, 2025

Since questing we no longer have systemd-timesyncd.service, but chrony.service instead.

But it comes disabled by default because a WSL instance is basically a container instance.

ubuntu@DESKTOP-551PQ9O:/mnt/c/Users/User$ systemctl status chrony
○ chrony.service - chrony, an NTP client/server
     Loaded: loaded (/usr/lib/systemd/system/chrony.service; enabled; preset: enabled)
     Active: inactive (dead)
  Condition: start condition unmet at Fri 2025-09-05 16:06:48 -03; 26min ago
             └─ ConditionVirtualization=!container was not met
       Docs: man:chronyd(8)
             man:chronyc(1)
             man:chrony.conf(5)

Sep 05 16:06:19 DESKTOP-551PQ9O systemd[1]: chrony.service - chrony, an NTP client/server was skipped because of an unmet condition check (ConditionVirtualization=!container).
Sep 05 16:06:48 DESKTOP-551PQ9O systemd[1]: chrony.service - chrony, an NTP client/server was skipped because of an unmet condition check (ConditionVirtualization=!container).
ubuntu@DESKTOP-551PQ9O:/mnt/c/Users/User$

I can think of three ways to fix this:

  1. Rename the systemd-timesyncd.service.d directory in this source tree into chrony.service.d (plus any minor corrections needed)

  2. Ship overrides for both systemd-timesyncd and chrony

  3. Write the override file at install time via a postinst hook, introspecting the system to determine which service is present.

None of the options please me for the following reasons:

  1. This breaks older releases. A future SRU targetting plucky and before needs to have this patch undone if taken from main. Alternatively we could branch off, but thats add maintenance costs.

  2. Ships unnecessary stuff. A system will either have chrony or systemd-timesyncd but not both.

  3. Adds complexity. One of the main reasons why I don't quite like resorting to postinst hooks is that we take to ourselves the burden of keeping track of which files need to be deleted by a postrm hook, job that should otherwise belong to the package manager.

I prefer option 2. It not only seems the least worse of the three options, but in the long term it looks like option 1 being applied in two steps. We ship now both overrides and in a future where systemd-timesyncd is no longer relevant we remove the matching directory, effectively becoming a rename if integrated over time ;p

$$ Engineering = \int_{0}^{T} Programming \cdot d t $$

Here's one example of failure in CI caused by this mismatch (before PR #27 was rebased on top of this one):

https://github.com/ubuntu/wsl-setup/actions/runs/17502925193/job/49720222983#step:7:64

And here's a successful run of that same PR after rebasing on top of this one 🫤 :

https://github.com/ubuntu/wsl-setup/actions/runs/17503092367/job/49720720404?pr=27

After deploying this patch we get:

ubuntu@DESKTOP-551PQ9O:/mnt/c/Users/User$ systemctl status chrony
● chrony.service - chrony, an NTP client/server
     Loaded: loaded (/usr/lib/systemd/system/chrony.service; enabled; preset: enabled)
    Drop-In: /usr/lib/systemd/system/chrony.service.d
             └─wsl.conf
     Active: active (running) since Fri 2025-09-05 16:49:45 -03; 4s ago
 Invocation: 791cb90924cc42a9a845db7ff8dd874b
       Docs: man:chronyd(8)
             man:chronyc(1)
             man:chrony.conf(5)
   Main PID: 148 (chronyd-starter)
      Tasks: 3 (limit: 7087)
     Memory: 10.1M (peak: 10.9M)
        CPU: 90ms
     CGroup: /system.slice/chrony.service
             ├─148 /bin/sh /usr/lib/systemd/scripts/chronyd-starter.sh -n -F 1
             ├─215 /usr/sbin/chronyd -n -F 1 -x
             └─224 /usr/sbin/chronyd -n -F 1 -x

Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Disabled control of system clock
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Loaded 0 symmetric keys
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Using leap second list /usr/share/zoneinfo/leap-seconds.list
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Loaded seccomp filter (level 1)
Sep 05 16:49:45 DESKTOP-551PQ9O systemd[1]: Started chrony.service - chrony, an NTP client/server.
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Added pool 1.ntp.ubuntu.com
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Added pool 2.ntp.ubuntu.com
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Added pool 3.ntp.ubuntu.com
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Added pool 4.ntp.ubuntu.com
Sep 05 16:49:45 DESKTOP-551PQ9O chronyd[215]: Added pool ntp-bootstrap.ubuntu.com

Since questing we no longer have systemd-timesyncd.service, but
chrony.service instead.

But it comes disabled by default because a WSL instance is basically a
container instance.

```
ubuntu@DESKTOP-551PQ9O:/mnt/c/Users/User$ systemctl status chrony
○ chrony.service - chrony, an NTP client/server
     Loaded: loaded (/usr/lib/systemd/system/chrony.service; enabled; preset: enabled)
     Active: inactive (dead)
  Condition: start condition unmet at Fri 2025-09-05 16:06:48 -03; 26min ago
             └─ ConditionVirtualization=!container was not met
       Docs: man:chronyd(8)
             man:chronyc(1)
             man:chrony.conf(5)

Sep 05 16:06:19 DESKTOP-551PQ9O systemd[1]: chrony.service - chrony, an NTP client/server was skipped because of an unmet condition check (ConditionVirtualization=!container).
Sep 05 16:06:48 DESKTOP-551PQ9O systemd[1]: chrony.service - chrony, an NTP client/server was skipped because of an unmet condition check (ConditionVirtualization=!container).
ubuntu@DESKTOP-551PQ9O:/mnt/c/Users/User$
```

I can think of three ways to fix this:

1. Rename the `systemd-timesyncd.service.d` directory in this source
   tree into `chrony.service.d` (plus any minor corrections needed)

2. Ship overrides for both `systemd-timesyncd` and `chrony`

3. Write the override file at install time via a postinst hook,
   introspecting the system to determine which service is present.

None of the options please me for the following reasons:

1. This breaks older releases. A future SRU targetting plucky and before
   needs to have this patch undone if taken from main. Alternatively we
   could branch off, but thats add maintenance costs.

2. Ships unnecessary stuff. A system will either have chrony or
   systemd-timesyncd but not both.

3. Adds complexity. One of the main reasons why I don't quite like
   resorting to postinst hooks is that we take to ourselves the burden
   of keeping track of which files need to be deleted by a postrm hook,
   job that should otherwise belong to the package manager.

I prefer option 2. It not only seems the least worse of the three
options, but in the long term it looks like option 1 being applied in
two steps. We ship now both overrides and in a future where
systemd-timesyncd is no longer relevant we remove the matching
directory, effectively becoming a rename if integrated over time ;p

$ Engineering = \int_{0}^{T} Programming d t $
@CarlosNihelton CarlosNihelton changed the title Enable chrony on WSL fix: Enable chrony on WSL Sep 5, 2025
@didrocks
Copy link
Member

didrocks commented Sep 8, 2025

Honest question: what do you think about shipping those ConditionVirtualization directly upstream and propose a patch there? Don't you think it’s something upstream would like and will apply to everyone?

@CarlosNihelton
Copy link
Collaborator Author

CarlosNihelton commented Sep 8, 2025

Honest question: what do you think about shipping those ConditionVirtualization directly upstream and propose a patch there? Don't you think it’s something upstream would like and will apply to everyone?

If by upstream you mean the chrony-project, the root element in the upstream tree, their source is pretty agnostic to packaging or daemon management, they don't ship systemd units, for example.

Specifically for Ubuntu, I had discussed this with the Foundations team before the switch happened, warning about how systemd-timesyncd handled containers and requesting to preserve WSL out of that. Back in the day we looked in a different approach, though, instead of systemd containers we discussed the main /etc/chrony/chrony.conf file. So their recommendation was to ship an override for WSL.

Looking now how the systemd unit ended up being implemented (in the very same way as systemd-timesyncd) and noticing that just the unit override is enough (without further touches in /etc/chrony directory tree) , it should be rather trivial to make the WSL line part of the default configuration. So I looked again for which of the upstreams I should patch and I think Debian Salsa is the right target.

- ConditionVirtualization=!container
+ ConditionVirtualization=|!container
+ ConditionVirtualization=|wsl

So I restarted this discussion for WSL: https://bugs.launchpad.net/ubuntu/+source/chrony/+bug/2122337

Thanks for the question. I was too excited to get the CI job proposed in #27 passing that I didn't consider the proper approach. :)

@CarlosNihelton
Copy link
Collaborator Author

To be completely fair, recent releases of WSL seem to have a good time sinc in place. I tested a few times putting a device to sleep while having a WSL instance running (without any NTS available) and the time always came correct after resumption.

We could still pursue this aiming to prevent future regression or inconsistency in the WSL behavior.

We could also consider using time.windows.com as the primary source instead of ntp.ubuntu.com to prevent the possibility of drifting from the Windows time. Then, for this piece of configuration we'd need to ship an override in wsl-setup.

@didrocks WDYT?

@didrocks
Copy link
Member

didrocks commented Sep 8, 2025

We could also consider using time.windows.com as the primary source instead of ntp.ubuntu.com to prevent the possibility of drifting from the Windows time. Then, for this piece of configuration we'd need to ship an override in wsl-setup.

I would not change that personnally for now until we have evidence/complains about time drifting from the Windows time. Does it make sense?

On the other topic, indeed, by upstream, I meant where the systemd unit is implemented, so here, apparently, debian salsa. Would that be doable?

@CarlosNihelton
Copy link
Collaborator Author

On the other topic, indeed, by upstream, I meant where the systemd unit is implemented, so here, apparently, debian salsa. Would that be doable?

Yeap. I checked with the Server team, they are also fine with the approach going into Salsa.

@CarlosNihelton
Copy link
Collaborator Author

I'm now having mixed feelings about this. As I keep running tests to provoke the time to go out of sync between host and guest, the less I'm convinced this is still necessary. The WSL Kernel team has this out of source patch for the Hyper-V driver in the kernel that forces the guest time to be adjusted if it goes out of sync even when the hypervisor didn't intend to do so (they claim Hyper-V sometimes should force the time sync but doesn't, reason why the patch was implemented in the first place):

microsoft/WSL2-Linux-Kernel@a34090e.

It seems robust enough, except for the allowed lag of 5s, which to me seems a too big lag. In practice though, by the time my device wakes up from sleep or hibernation and I check the clock inside WSL with powershell.exe -Command Get-Date && timedatectl the outputs from both commands match.

I'm afraid maintaining a NTS client inside WSL enabled by default might end up being just unnecessary resource consumption for most of the users.

Also, besides chrony.service, there is a shell script used to start the daemon that would still cause it to not touch the system clock on containers (including WSL). Perhaps we should just patch that part to allow the daemon to run in its full power if invoked, but not touch the systemd unit, documenting instead what to do for those running custom kernels that don't contain that patch (or those who have other reasons to enable NTS manually inside WSL).

WDYT?

@didrocks
Copy link
Member

didrocks commented Sep 9, 2025

I think the fix for chrony.service in the base package makes sense anyway as it’s something widespread we would like (as we can rapproach WSL to a VM system "mentually" more than a container one).

I’m unsure about the conclusion you got to as the reference to "this" is lacking context to me ;) Let’s discuss that over our next 1o1, that would be easier! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants