Repository: docker/compose

Issue:
[BUG] watch crashes when deleting file
### Description

When deleting a file (unsure if the file existed or not but should have since it synced it) watch command crashes and cannot be restarted.

```
develop:
  watch:
    - action: sync
      path: ${LOCAL_DEPLOY_PATH}\platform
      target: c:/inetpub/wwwroot/
```

```
Syncing cm after changes were detected:
  - C:\t\docker\deploy\platform\Web.config.xdt
☺�container 061ae4ad9ec878e7a259e45aa1d7b4bd0dc56468b05497fa18cd71dd5f1c0cbe encountered an error during hcs::System::CreateProcess: failure in a Windows system call: The system cannot find the file specified. (0x2)
```
Then when trying to restart it is locked:
```
> docker compose watch --no-up
cannot take exclusive lock for project "kermit": process with PID 20836 is still running
```

Killing the 20836 process still errors out the same.

### Steps To Reproduce

It seem to be reproducable

```
watching [C:\t\docker\deploy\platform]
Syncing cm after changes were detected:
  - C:\t\docker\deploy\platform\images.jpg
☻Rtar: Removing leading drive letter from member names
x inetpub/wwwroot/images.jpg☻☻
Syncing cm after changes were detected:
  - C:\t\docker\deploy\platform\images.jpg
☺�container 7b26f6516e4c0ed000d0d71b1f01250411af2ddab0b17cd3f7f3b391a4ee97a0 encountered an error during hcs::System::CreateProcess: failure in a Windows system call: The system cannot find the file specified. (0x2)
```

### Compose Version

```Text
Docker Compose version v2.22.0
```


### Docker Environment

```Text
Client:
 Version:    24.0.6
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.10.3
    Path:     C:\Users\Administrator\.docker\cli-plugins\docker-buildx.exe
  compose: Docker Compose (Docker Inc.)
    Version:  v2.22.0
    Path:     C:\ProgramData\Docker\cli-plugins\docker-compose.exe
  scout: Command line tool for Docker Scout (Docker Inc.)
    Version:  0.17.1
    Path:     C:\Users\Administrator\.docker\cli-plugins\docker-scout.exe

Server:
 Containers: 14
  Running: 0
  Paused: 0
  Stopped: 14
 Images: 861
 Server Version: 24.0.4
 Storage Driver: windowsfilter
  Windows:
 Logging Driver: json-file
 Plugins:
  Volume: local
  Network: ics internal l2bridge l2tunnel nat null overlay private transparent
  Log: awslogs etwlogs fluentd gcplogs gelf json-file local logentries splunk syslog
 Swarm: inactive
 Default Isolation: process
 Kernel Version: 10.0 20348 (20348.1.amd64fre.fe_release.210507-1500)
 Operating System: Microsoft Windows Server Version 21H2 (OS Build 20348.1970)
 OSType: windows
 Architecture: x86_64
 CPUs: 8
 Total Memory: 31.86GiB
 Name: kermit-dev
 ID: YP3Q:GBSN:NFJ3:QQMM:DB3Z:CD3V:7RBI:V445:473L:3WU3:VOP3:5DVB
 Docker Root Dir: C:\ProgramData\docker
 Debug Mode: false
 Username: kermit
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false
 Product License: Community Engine
```


### Anything else?

The file is not removed from container.
No idea of how/where the lock is kept.
A restart of docker-engine removed the lock.

Hints from issue comments:
@pbering: Same thing happens when copying many files into a synced folder like this:

```
    develop:
      watch:
        - action: sync
          path: ./web
          target: /inetpub/wwwroot
```

only way to recover from `cannot take exclusive lock for project` is to restart the host, not even restarting Docker Desktop or dockerd helps.@mrbiggred: @pbering and @perosb a possible workaround so you don't have to restart your host can be found in issue #11069:

https://github.com/docker/compose/issues/11069#issuecomment-1769694535@ndeloof: lock is managed by https://github.com/moby/moby/blob/master/pkg/pidfile/pidfile.go#L29
According to "process with PID 20836 is still running" message, the compose process is still reported by system as "alive". If you can reproduce this issue, could you please inspect the referred process ?@pbering: When the issue happens and I see that message, then there is no process with that PID.@ndeloof: which OS are you running on ?@perosb: > which OS are you running on ?

```
Kernel Version: 10.0 20348 (20348.1.amd64fre.fe_release.210507-1500)
Operating System: Microsoft Windows Server Version 21H2 (OS Build 20348.1970)
OSType: windows
Architecture: x86_64

```@AlexeyPlodenko: Same on
```
OS Name:                   Microsoft Windows 10 Pro
OS Version:                10.0.19045 N/A Build 19045
```@mac-hel: > @pbering and @perosb a possible workaround so you don't have to restart your host can be found in issue #11069:
> 
> [#11069 (comment)](https://github.com/docker/compose/issues/11069#issuecomment-1769694535)

Another workarround (Linux) is to stop containers before exiting watch.
`Ctrl+z` to suspend watch
`docker-compose down` to stop and remove containers
`fg` to bring watch process into foreground
`Ctrl+c` to exit watch@wclr: > According to "process with PID 20836 is still running" message, the compose process is still reported by system as "alive". If you can reproduce this issue, could you please inspect the referred process ?

I've been implementing small script that watches `docker-compose.yaml` and restarts `compose watch` on its changes (to handle actual state), and run into this issue.

The question is why it reports that `process with PID XXXX is still running` when no process with this pid is running? I could net get its logic. What is it checking for when start `compose watch`, does it check the process?@ndeloof: @wclr process detection is implemented by https://github.com/moby/moby/blob/master/pkg/process/process_windows.go

@wclr: > @wclr process detection is implemented by https://github.com/moby/moby/blob/master/pkg/process/process_windows.go

Well, here I believe it checks the process. But the fact is that when launching `docker compose watch` it reports that `process with PID XXXX is still running` while there is no `XXXX` in the tasklist (for example, in my case this PID was killed by the aforementioned script that spawned `docker compose watch`). [Рeople above mention this too,](https://github.com/docker/compose/issues/11066#issuecomment-1785755676), so you probably need to check the logic behind this report and check to ensure that it can not be the case.@At4m4n: > When the issue happens and I see that message, then there is no process with that PID.

Same. No such process at Windows host itself, neither at the container I watch (Ubuntu based app image). Seems to exist somewhere within the "docker engine space"

UPD. Managed to resolve this by updating to latest docker version. The installation itself said there's an assistance service process running and suggested to kill it. `watch` command works again after update, no host reboot needed. @wclr: > UPD. Managed to resolve this by updating to latest docker version. The installation itself said there's an assistance service process running and suggested to kill it. watch command works again after update, no host reboot needed.

It is not fixed, I used the latest version when run into this. And you don't need to reboot, you can just delete `%LOCALAPPDATA%/docker-compose.[YOUR_COMPOSE_PROJECT_NAME].pid`.

I eventually ended up writing my own custom watch script to fully replace `docker compose watch` functionality (for my case). It watches need file changes in the project and executes (inside the container) copying from the attached host volume to docker volumes to keep them in sync, this script as well initially runs rsync to make the initial sync with the host. 

The problem mentioned in this issue is not the only one for current compose watch implementation, for example it also ignores /skips some file changes if they are made in a batch, so the custom solution can solve all this.@AlexeyPlodenko: I am deleting the image and the container and then run the `docker-compose watch` in one PowerShell script, to sync the files, before the containers are started:

```
Powershell.exe -noexit -command "cd ../..;  docker rm --force backoffice-php; docker rmi $(docker images --format '{{.Repository}}:{{.Tag}}'|findstr 'backoffice-php'); docker-compose watch"
```@torrinworx: Can confirm that I'm seeing this issue on docker version 24.0.7, and a temporary work around for it is to delete the .pid file in:
C:\Users\<your-user-name>\AppData\Local\docker-compose\<your-project-name>.pid@princemaple: Encountered this on Desktop 4.27.1 (136059), Engine: 25.0.2, Compose: v2.24.3-desktop.1@glours: Hey @perosb 
I'm not able to reproduce with the latest Docker Desktop release `4.27.2`, can you give it a try?
If you still have the issue, can you give us a minimal & complete reproduction case?

For all the other, if you don't have the same issue as the original one, please:
- Check the latest version of Docker Desktop
- If you can reproduce your issue, please open a new one with a full repo case

Thanks all 🙏 @xerosta: Errors are still occurring (I've updated to the latest Docker version `4.27.2`) :

> $ docker compose watch
> cannot take exclusive lock for project "<project name>": process with PID 43396 is still running

My OS :
> OS Name:              Microsoft Windows 11 Famille
> OS Version:            10.0.22631 N/A build 22631

If necessary, I can open a repository.@borgez: for me manual delete file in AppData\Local\docker-compose\ *.pid solove problem but very annoying

```
docker version
Client:
 Cloud integration: v1.0.35+desktop.10
 Version:           25.0.3
 API version:       1.44
 Go version:        go1.21.6
 Git commit:        4debf41
 Built:             Tue Feb  6 21:13:02 2024
 OS/Arch:           windows/amd64
 Context:           default

Server: Docker Desktop 4.27.2 (137060)
 Engine:
  Version:          25.0.3
  API version:      1.44 (minimum version 1.24)
  Go version:       go1.21.6
  Git commit:       f417435e5f6216828dec57958c490c4f8bae4f98
  Built:            Wed Feb  7 00:39:16 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.28
  GitCommit:        ae07eda36dd25f8a1b98dfbf587313b99c0190bb
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```@torrinworx: > Hey @perosb I'm not able to reproduce with the latest Docker Desktop release `4.27.2`, can you give it a try? If you still have the issue, can you give us a minimal & complete reproduction case?
> 
> For all the other, if you don't have the same issue as the original one, please:
> 
> * Check the latest version of Docker Desktop
> * If you can reproduce your issue, please open a new one with a full repo case
> 
> Thanks all 🙏

@glours I'm actually facing this issue with my repo here: https://github.com/torrinworx/Bitorch

To reproduce:
1. `$ docker compose -f .\dev.docker-compose.yml build --no-cache`
2. `$docker compose -f .\dev.docker-compose.yml watch`
3. `$ ctrl+c`, and delete/remove running container/compose stacks
4.  `$ docker compose -f .\dev.docker-compose.yml watch`

Result:
```
PS C:\Users\torri\Desktop\Repositories\Personal\Bitorch> docker compose -f .\dev.docker-compose.yml watch

cannot take exclusive lock for project "bitorch-development": process with PID 47156 is still running
PS C:\Users\torri\Desktop\Repositories\Personal\Bitorch> tasklist /fi "PID eq 47156" 
INFO: No tasks are running which match the specified criteria.
PS C:\Users\torri\Desktop\Repositories\Personal\Bitorch>
```

This is with docker desktop 4.27.2.@FibreFoX: Maybe this is related, but for me it looks like the `%LOCALAPPDATA%\docker-compose\PROJECTNAME.pid`-file is not getting removed properly. When exiting via `CRTL + C`, the exit code is `130` (`$LastExitCode` when using PowerShell), maybe thats the reason that watch-command is not working as intended?

I always need to delete that file, no other problems so far (but haven't played around with this new feature yet).

Running Docker Desktop 4.27.2 on Windows 10 Pro 22H2 using HyperV.@ndeloof: This file is not expected to be removed after command completion, but when executed compose command check the registered pid is alive (see https://github.com/moby/moby/blob/master/pkg/process/process_windows.go).@glours: @torrinworx 👋 
I used your repository but wasn't able to reproduce on my side.
I don't know what happens to be honest, can you share me a recording so I'll be able to check if I'm not missing a step?
Are you using WSL2 or HyperV as Docker Desktop Virtutal machine?
@torrinworx: > @torrinworx 👋 I used your repository but wasn't able to reproduce on my side. I don't know what happens to be honest, can you share me a recording so I'll be able to check if I'm not missing a step? Are you using WSL2 or HyperV as Docker Desktop Virtutal machine?

@glours I'm using WSL2 on Windows 11 Home 22H2 22621.3155. Here is a video with this issue happening with the Bitorch repository I linked above:

https://www.youtube.com/watch?v=PzrfWC825Rc@glours: @torrinworx thank you very much!
Can I ask you an another question, I want to check if you don't have a old version of Compose in your path that could take the priority in favor of the embedded version of Desktop
Can you share the result of `docker compose version` please?@torrinworx: np!

Huh yeah it looks like it's still taking the old desktop version: Docker Compose version v2.24.5-desktop.1

Even though my docker desktop client is saying v4.27.2@glours: Unfortunately no 😞 , Compose `v2.24.5-desktop.1` is the version shipped in Docker Desktop `4.27.2` @glours: @torrinworx can you try something else please, instead of doing `docker compose watch` directly can you try the following steps:
* `docker compose -f .\dev.docker-compose.yml up -d`
* `docker compose -f .\dev.docker-compose.yml watch`
* Then do the CTRL+C 
* `docker compose -f .\dev.docker-compose.yml watch` again

And a 2nd test:
* `docker compose -f .\dev.docker-compose.yml up -d`
* `docker compose -f .\dev.docker-compose.yml watch`
* Then do the CTRL+C 
* Don't remove the containers in Docker Desktop
* `docker compose -f .\dev.docker-compose.yml watch` again@ndeloof: Can you please check the status of the process listed in the lock file ?
```powershell
Get-Process -Id 146328
```
@torrinworx: > @torrinworx can you try something else please, instead of doing `docker compose watch` directly can you try the following steps:
> 
> * `docker compose -f .\dev.docker-compose.yml up -d`
> * `docker compose -f .\dev.docker-compose.yml watch`
> * Then do the CTRL+C
> * `docker compose -f .\dev.docker-compose.yml watch` again
> 
> And a 2nd test:
> 
> * `docker compose -f .\dev.docker-compose.yml up -d`
> * `docker compose -f .\dev.docker-compose.yml watch`
> * Then do the CTRL+C
> * Don't remove the containers in Docker Desktop
> * `docker compose -f .\dev.docker-compose.yml watch` again

So both tests result in the same error:
```
PS C:\Users\torri\Desktop\Repositories\Personal\Bitorch> docker compose -f .\dev.docker-compose.yml watch
cannot take exclusive lock for project "bitorch-development": process with PID 165984 is still running
PS C:\Users\torri\Desktop\Repositories\Personal\Bitorch>
```
However when I delete the .pid file from the directory they both work just fine.

@ndeloof After I CTRL+C the watch command and delete the containers this is the result:
```
C:\Users\torri>tasklist /FI "PID eq 162632"
INFO: No tasks are running which match the specified criteria.

C:\Users\torri>
```

It only shows `No tasks are running...` after you CTRL+C the watch command, when it's still running it will show this, even when the containers are deleted:
```
C:\Users\torri>tasklist /FI "PID eq 162632"

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
docker-compose.exe          162632 Console                    1     47,308 K

C:\Users\torri>
```
@ndeloof: Looking at the [pidfile code](https://github.com/moby/moby/blob/master/pkg/process/process_windows.go#L10-L32) which should detect process is done, I wonder `GetExitCodeProcess` could return without an error, and then code would `return true`@glours: @ndeloof agree@glours: @torrinworx can you try [this specific version of Compose (choose the right binary for you)](https://github.com/docker/compose/actions/runs/7933388152/artifacts/1251775185) and install it with the name `docker-compose(.exe)` under your `~/.docker/cli-plugins` directory@torrinworx: @glours That worked! I was able to re-watch the compose file and build the containers without seeing the PID error.@milas: This fix has been included in Compose [2.24.7+](https://github.com/docker/compose/releases/latest). If you continue to have problems after upgrading, comment here and we can re-open the issue or create a new one as appropriate. Thanks for the report!@julielerman: I will be documenting all of these experiments in a blog post but just wanted to leave it here in case someone gets stuck.

Some data for you @milas , as I'm getting this on mac Monterey v 12.5
Docker Desktop  Docker & verified Compose version matches: v2.27.1-desktop.1
Visual Studio Code  1.90.2
Docker extension v1.29.1

docker compose -f "docker-compose.yml" up -d --build --watch
docker compose down
(containers are gone)
docker compose watch

_cannot take exclusive lock for project "xxx": process with PID 88950 is still running_

_I THINK IT's the  detached flag that is causing this..._ ??? (sorry for the brain dump. I keep playing wtih it and understanding more and more and maybe its useful to you...maybe not LOL)

Note that --watch results in multiple terminal windows (as stated in the docs somewhere) : docker terminal and my zsh terminal window. *
*After compose down, the docker terminal is still there with the last message from the UP command. If I delete that terminal manually (using the trashcan icon) then it releases and I can run docker compose watch

Update: I feel like I'm going a little crazy. After manually clsoing the docker terminal, then running with up --watch again, there is no docker terminal. Just my main zsh. I hate inconsistent outcomes. I've done this many times and this is a new behavior.

With docker compose watch, docker compose down properly releases and gets me back to 
"Terminal will be reused by tasks, press any key to close it." in the docker terminal. However after pressing any key , I still have to Ctrl-C (no message suggests that) to truly release the docker terminal and go back to zsh.

*Side question; I haven't been able to find any other guidance about using up --watch vs watch. Does it exist somewhere?
@AlexeyPlodenko: Unfortunately faced a similar bug again on Windows 11 Home 24H2 26100.2605 laptop with the latest Docker and WSL.

I cannot give the exact replication steps. But I guess it is related to the laptop getting low battery and the OS shutting down or something similar.

After restart trying to do "docker compose watch" results in the mentioned earlier error - **cannot take exclusive lock for project "...": process with PID 19904 is still running**

Checking the running process PID - tasklist /FI "PID eq 19904" - gives:
```
Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
TextInputHost.exe            19904 Console                    1    121,816 K
```

Seems Docker does not check the name of the process, but only the PID.

Docker version:
```
Client:
 Version:           27.4.0
 API version:       1.47
 Go version:        go1.22.10
 Git commit:        bde2b89
 Built:             Sat Dec  7 10:40:21 2024
 OS/Arch:           windows/amd64
 Context:           desktop-linux

Server: Docker Desktop 4.37.1 (178610)
 Engine:
  Version:          27.4.0
  API version:      1.47 (minimum version 1.24)
  Go version:       go1.22.10
  Git commit:       92a8393
  Built:            Sat Dec  7 10:38:57 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.7.21
  GitCommit:        472731909fa34bd7bc9c087e4c27943f9835f111
 runc:
  Version:          1.1.13
  GitCommit:        v1.1.13-0-g58aa920
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

---

A temp. fix mentioned [here](https://github.com/docker/compose/issues/11069#issuecomment-1769694535) helped.

For Windows, the files were located in C:\Users\MY_USER_NAME\AppData\Local\docker-compose\*.pid
I have stopped Docker. Deleted the files. Started Docker. And now the build works again.@PingvinB: Still experiencing this on Windows 11.
`Docker version 29.1.2, build 890dcca`

@ndeloof: @PingvinB  what does `docker compose version` say?@PingvinB: ❯ docker compose version
Docker Compose version v2.40.3-desktop.1@ndeloof: any log when compose command "crashes"?@PingvinB: @ndeloof Apologies. I should've made it clearer than I'm referring to experiencing the .pid-lock files becoming stale and pointing to the wrong process id when using `docker compose watch`. There is no crash to be the logged. 

Solution patch:
diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go
index 55fe7168..fc29014c 100644
--- a/cmd/compose/compose.go
+++ b/cmd/compose/compose.go
@@ -209,8 +209,6 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
 		po = append(po, cli.WithResourceLoader(r))
 	}
 
-	po = append(po, cli.WithContext(ctx))
-
 	options, err := o.toProjectOptions(po...)
 	if err != nil {
 		return nil, metrics, compose.WrapComposeError(err)
@@ -243,7 +241,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
 		api.Separator = "_"
 	}
 
-	project, err := cli.ProjectFromOptions(options)
+	project, err := cli.ProjectFromOptions(ctx, options)
 	if err != nil {
 		return nil, metrics, compose.WrapComposeError(err)
 	}
diff --git a/go.mod b/go.mod
index dd28fa48..05fa0fbf 100644
--- a/go.mod
+++ b/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/AlecAivazis/survey/v2 v2.3.7
 	github.com/Microsoft/go-winio v0.6.1
 	github.com/buger/goterm v1.0.4
-	github.com/compose-spec/compose-go/v2 v2.0.0-rc.7
+	github.com/compose-spec/compose-go/v2 v2.0.0-rc.8
 	github.com/containerd/console v1.0.3
 	github.com/containerd/containerd v1.7.12
 	github.com/davecgh/go-spew v1.1.1
@@ -23,6 +23,7 @@ require (
 	github.com/hashicorp/go-version v1.6.0
 	github.com/jonboulle/clockwork v0.4.0
 	github.com/mattn/go-shellwords v1.0.12
+	github.com/mitchellh/go-ps v1.0.0
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/moby/buildkit v0.13.0-beta1.0.20231219135447-957cb50df991
 	github.com/moby/patternmatcher v0.6.0
diff --git a/go.sum b/go.sum
index 19dd1afd..22d96bb9 100644
--- a/go.sum
+++ b/go.sum
@@ -86,8 +86,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
 github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
 github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
-github.com/compose-spec/compose-go/v2 v2.0.0-rc.7 h1:koFIK+JwplWu1m/DscSO6MJw7hodaEHOaKQZPUSL4OY=
-github.com/compose-spec/compose-go/v2 v2.0.0-rc.7/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
+github.com/compose-spec/compose-go/v2 v2.0.0-rc.8 h1:b7l+GqFF+2W4M4kLQUDRTGhqmTiRwT3bYd9X7xrxp5Q=
+github.com/compose-spec/compose-go/v2 v2.0.0-rc.8/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
 github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
 github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
 github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
@@ -331,6 +331,8 @@ github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
 github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
 github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
+github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
 github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
diff --git a/internal/locker/pidfile.go b/internal/locker/pidfile.go
index ea688fb6..08dcea1f 100644
--- a/internal/locker/pidfile.go
+++ b/internal/locker/pidfile.go
@@ -18,10 +18,7 @@ package locker
 
 import (
 	"fmt"
-	"os"
 	"path/filepath"
-
-	"github.com/docker/docker/pkg/pidfile"
 )
 
 type Pidfile struct {
@@ -36,7 +33,3 @@ func NewPidfile(projectName string) (*Pidfile, error) {
 	path := filepath.Join(run, fmt.Sprintf("%s.pid", projectName))
 	return &Pidfile{path: path}, nil
 }
-
-func (f *Pidfile) Lock() error {
-	return pidfile.Write(f.path, os.Getpid())
-}
diff --git a/internal/locker/pidfile_unix.go b/internal/locker/pidfile_unix.go
new file mode 100644
index 00000000..484b65d8
--- /dev/null
+++ b/internal/locker/pidfile_unix.go
@@ -0,0 +1,29 @@
+//go:build !windows
+
+/*
+   Copyright 2023 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package locker
+
+import (
+	"os"
+
+	"github.com/docker/docker/pkg/pidfile"
+)
+
+func (f *Pidfile) Lock() error {
+	return pidfile.Write(f.path, os.Getpid())
+}
diff --git a/internal/locker/pidfile_windows.go b/internal/locker/pidfile_windows.go
new file mode 100644
index 00000000..9f8d4c3e
--- /dev/null
+++ b/internal/locker/pidfile_windows.go
@@ -0,0 +1,47 @@
+//go:build windows
+
+/*
+   Copyright 2023 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package locker
+
+import (
+	"github.com/docker/docker/pkg/pidfile"
+	"github.com/mitchellh/go-ps"
+	"os"
+)
+
+func (f *Pidfile) Lock() error {
+	newPID := os.Getpid()
+	err := pidfile.Write(f.path, newPID)
+	if err != nil {
+		// Get PID registered in the file
+		pid, errPid := pidfile.Read(f.path)
+		if errPid != nil {
+			return err
+		}
+		// Some users faced issues on Windows where the process written in the pidfile was identified as still existing
+		// So we used a 2nd process library to verify if this not a false positive feedback
+		// Check if the process exists
+		process, errPid := ps.FindProcess(pid)
+		if process == nil && errPid == nil {
+			// If the process does not exist, remove the pidfile and try to lock again
+			_ = os.Remove(f.path)
+			return pidfile.Write(f.path, newPID)
+		}
+	}
+	return err
+}



Test patch:
diff --git a/pkg/e2e/compose_run_test.go b/pkg/e2e/compose_run_test.go
index e552e465..ceec152a 100644
--- a/pkg/e2e/compose_run_test.go
+++ b/pkg/e2e/compose_run_test.go
@@ -162,10 +162,10 @@ func TestLocalComposeRun(t *testing.T) {
 	})
 
 	t.Run("--quiet-pull", func(t *testing.T) {
-		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "down", "--rmi", "all")
+		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/quiet-pull.yaml", "down", "--rmi", "all")
 		res.Assert(t, icmd.Success)
 
-		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/compose.yaml", "run", "--quiet-pull", "back")
+		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/quiet-pull.yaml", "run", "--quiet-pull", "backend")
 		assert.Assert(t, !strings.Contains(res.Combined(), "Pull complete"), res.Combined())
 		assert.Assert(t, strings.Contains(res.Combined(), "Pulled"), res.Combined())
 	})
diff --git a/pkg/e2e/fixtures/run-test/quiet-pull.yaml b/pkg/e2e/fixtures/run-test/quiet-pull.yaml
new file mode 100644
index 00000000..92267636
--- /dev/null
+++ b/pkg/e2e/fixtures/run-test/quiet-pull.yaml
@@ -0,0 +1,3 @@
+services:
+  backend:
+    image: hello-world
\ No newline at end of file