skip to Main Content

I’m here again, let’s see if you can give me a hand…

Normally when you install docker-desktop from 0, some configuration files are set to:

C:UsersAppDataRoamingDockersetting.json

It is almost always necessary to do manual steps: open the application, do the survey, then make changes to the settings, save and apply, then close… and log in again.

I am more into using docker-desktop from the terminal, I usually execute the commands to create the network and raise the containers based on docker-compose

The problem or rather the help I am looking for is if you can help me do the programmatic part of updating the settings and closing and reopening the desktop application: docker-desktop.

I tried doing something before coming here in C# but it turned out to be unstable:

Check/Wait for the invoked application to be open

I’m sure this could be achieved with PowerShell but I don’t know much about it, In powershell i have done this:


# Defines the configuration dictionary
$settingsToReplace = @{
    '"exposeDockerAPIOnTCP2375": false,' = '"exposeDockerAPIOnTCP2375": true,'
    '"updateHostsFile": false,' = '"updateHostsFile": true,'
    '"licenseTermsVersion": 0,' = '"licenseTermsVersion": 2,'
}

# Defines the path to the configuration file
$settingsPath = "$env:APPDATADockersettings.json"

# Read the contents of the configuration file
$settingsContent = Get-Content $settingsPath -Raw

# Replaces the values in the file content
foreach ($key in $settingsToReplace.Keys) {
    $settingsContent = $settingsContent -replace [regex]::Escape($key), $settingsToReplace[$key]
}

# Write the modified content back to the file
$settingsContent | Set-Content $settingsPath

# Close Docker Desktop
Stop-Process -Name "Docker Desktop*" -ErrorAction SilentlyContinue

# Wait until Docker Desktop has completely closed
$timeout = 60  # seconds
$processName = "Docker Desktop"
$timeoutReached = $false
$startTime = Get-Date

# Wait until Docker Desktop has completely closed or the timeout has been reached
while ((Get-Process -Name $processName -ErrorAction SilentlyContinue) -and (-not $timeoutReached)) {
    # Verifica si el proceso de Docker Desktop se ha cerrado
    if (-not (Get-Process -Name $processName -ErrorAction SilentlyContinue)) {
        Write-Host "Docker Desktop has closed before the time limit was reached."
        break
    }

    # Check if the time limit has been reached
    if ((Get-Date) - $startTime -ge [TimeSpan]::FromSeconds($timeout)) {
        $timeoutReached = $true
    }

    # Wait 1 second before checking again
    Start-Sleep -Seconds 1
}

# Check if the timeout has been reached
if ($timeoutReached) {
    Write-Host "Docker Desktop did not close properly. Please manually close the application and run the script again."
} else {
    Write-Host "Docker Desktop has closed successfully. Continuing..."
}

# Open Docker Desktop

$docker = [System.Environment]::GetEnvironmentVariable('ProgramFiles') + 'DockerDockerDocker Desktop.exe'
Start-Process -FilePath $docker

# Wait until Docker Desktop has fully opened
$processName = "Docker Desktop"
while (-not (Get-Process -Name $processName -ErrorAction SilentlyContinue)) {
    Start-Sleep -Seconds 1
}

Write-Host "continue"

But i detect that docker-desktop become unstable again… and the setting update file is applied to setting.json but is not charged to docker-desktop when is reopen..

Docker post forum related: https://forums.docker.com/t/programmatic-manipulation-and-management-of-docker-desktop/140018

Update:

What are the exact symptoms of "instability"? answer: When opening Docker-Desktop using the script, the Docker-Desktop User Interface slowly slows down to the point that it are blocked, no response to mouse clicks.

What And how do you test if the changes have been applied after restarting the Docker desktop? answer: the settings tab is opened in docker-desktop and they continue as if the changes had not been made. but the changes are reflected in the setting.json file.

Update #2

Let’s summarize what I’m doing, I’ve left C# aside to evaluate what makes docker desktop unstable, and this is the complete powershell script I’m deploying, with the changes I need in setting.json, the script works correctly, but once that docker-desktop UI opens after 30 seconds, it is completely unstable / somne flicker when try to click any UI control:

$ErrorActionPreference = 'Continue'
Set-Service -Name "com.docker.service" -StartupType Automatic -ErrorAction SilentlyContinue
Stop-Service -Name "com.docker.service" -ErrorAction SilentlyContinue
Get-Process 'Docker Desktop' -ErrorAction Ignore | Stop-Process -Force -ErrorAction Stop
Wait-Process -Name 'Docker Desktop' -ErrorAction SilentlyContinue
$settingsToUpdate = @{
    exposeDockerAPIOnTCP2375 = $true
    updateHostsFile = $true
    licenseTermsVersion = 2
    noWindowsContainers = $true
    runWinServiceInWslMode = $false
    useResourceSaver = $false
}

$settingsPath = "$env:APPDATADockersettings.json"
$settingsContent = Get-Content $settingsPath -Raw 
$settingsObject  = $settingsContent | ConvertFrom-Json

$trackUpdates = 0
foreach ($update in $settingsToUpdate.GetEnumerator()) {
    if ($target = $settingsObject.psobject.Properties.Match($update.Key)) {
        if ($target.Value -ne $update.Value) {
            Add-Member -InputObject $settingsObject -MemberType NoteProperty -Name $update.Key -Value $update.Value -Force
            $trackUpdates++
        }
    }
}

if ($trackUpdates -eq 0) {
    Write-Host "No new settings applied"
} else {
    $settingsObject | ConvertTo-Json | Set-Content $settingsPath
    Write-Host "Settings updated and saved successfully"
}

Start-Service -Name "com.docker.service" -ErrorAction SilentlyContinue

while ((Get-Service -Name "com.docker.service").Status -ne "Running") {
    Write-Host (Get-Service -Name "com.docker.service").Status
    Start-Sleep -Seconds 1
}

if((Get-Service -Name "com.docker.service").Status -eq "Running"){
    Write-Host (Get-Service -Name "com.docker.service").Status
}

$dockerDesktopFilePath = $env:ProgramFiles | Join-Path -ChildPath 'DockerDockerDocker Desktop.exe'; Start-Process -FilePath $dockerDesktopFilePath

$ipcTimeout = New-TimeSpan -Seconds 20
$waitUntil = [datetime]::Now.Add($ipcTimeout)
$pipeOpen = $false
Write-Host 'Probing docker engine I/O pipe'
do {
  Start-Sleep -Milliseconds 100
  $pipeOpen = Test-Path -LiteralPath \.pipedocker_engine
} until ($pipeOpen -or ($waitUntil -le [datetime]::Now))

if (-not $pipeOpen) {
  Write-Warning "Failed to observe named IPC pipe docker_engine within timeout"
  return
}

$responseTimeout = New-TimeSpan -Seconds 5
$waitUntil = [datetime]::Now.Add($responseTimeout)

Write-Host 'Querying docker server info'
do {
  Start-Sleep -Milliseconds 500
  $dockerInfoOutput = docker info 2>&1 
  $dockerInfoSuccess = $?
} until ($dockerInfoSuccess -or ($waitUntil -le [datetime]::Now))

if (-not $dockerInfoSuccess) {
  Write-Warning "docker info failed within timeout"
  return
}

Write-Host 'Docker Desktop Is Runing Now'

Update #3

So the problem is not how the application is opened, but how it is closed…

a friend try to run this script:

$ServiceName = "com.docker.service"
$ProcessName = "Docker Desktop"

$arrService = Get-Service -Name $ServiceName
if ($arrService.Status -ne 'Running') {
    Stop-Service -Name $ServiceName
}


if (Get-Process $ProcessName -ErrorAction SilentlyContinue) {  
  Stop-Process -Name $ProcessName
  Wait-Process -Name $ProcessName
}

In my case, the service would not be running, so the upper section of the code does not matter.
In the task manager I could see that 4 or respectively 6 processes are running in relation to Docker desktop. So that the Stop-Process command terminated the porcess “Docker Desktop” immediately.
But 2 processes “Docker Desktop Backend” and “Docker Desktop Extensions” are still running.
I started Docker Desktop by double-clicking on a shortcut and could then see that the Docker Engine was paused and the application was not running stably. I then terminated all processes and was able to open Docker Desktop again without any problems. From here on, you have to find out everything else for yourself.

it is not clear to me how many processes have to be closed when closing Docker-Desktop, it seems that the closing requires a wild-card: "Docker-Desktop*" or "Docker Desktop*" or "*Docker*" but this can cause processes that should be kept running to close.

On the other hand I found something curious last night when I did installation #64 from 0.

It turns out that if you have docker running and you go to the settings for the first time and check the option to update the hosts file, the updateHostsFile property is not the only one that changes; These other 2 also change: noWindowsContainers, runWinServiceInWslMode and screw up the general docker configuration, if you are using Windows 11/10 home, since these options should only be enabled if you are using the PRO version with Hiper-V.

So for this reason, if you programmatically activate updateHostsFile it will have no effect… unless you activate the other two… which will not have positive results either…

The thing is complicated and complex… it should be simpler to manage this through PowerShell…

2

Answers


  1. Chosen as BEST ANSWER

    The final script to programaticaly:

    • Stop docker service and process.
    • Update setting.json.
    • Reopen docker desktop are here:

    https://gist.github.com/arcanisgk/d78acd5d51ab263d9467fb2da97781ca

    The problem of instability occurred because not all the processes that doccker incorporates were closed, apparently there are a series of additional processes (not services) that must be closed equally or it will cause instability.

    $ErrorActionPreference = 'Continue'
    Stop-Service -Name "com.docker.*" -ErrorAction SilentlyContinue
    Set-Service -Name "com.docker.service" -StartupType Automatic -ErrorAction SilentlyContinue
    
    $processesToStop = @('Docker Desktop', 'com.docker.backend', 'com.docker.extensions')
    $processesToStop | ForEach-Object {
        Get-Process -Name $_ -ErrorAction Ignore | Stop-Process -Force -ErrorAction Ignore
    }
    
    $settingsToUpdate = @{
        exposeDockerAPIOnTCP2375 = $true
        updateHostsFile = $true
        licenseTermsVersion = 2
        noWindowsContainers = $false        # Required to enable read updateHostsFile property
        runWinServiceInWslMode = $true      # Required to enable read updateHostsFile property
        useResourceSaver = $false
    }
    
    $settingsPath = "$env:APPDATADockersettings.json"
    $settingsContent = Get-Content $settingsPath -Raw 
    $settingsObject  = $settingsContent | ConvertFrom-Json
    
    $trackUpdates = 0
    foreach ($update in $settingsToUpdate.GetEnumerator()) {
        if ($target = $settingsObject.psobject.Properties.Match($update.Key)) {
            if ($target.Value -ne $update.Value) {
                Write-Host $update.Key
                Add-Member -InputObject $settingsObject -MemberType NoteProperty -Name $update.Key -Value $update.Value -Force
                $trackUpdates++
            }
        }
    }
    
    if ($trackUpdates -eq 0) {
        Write-Host "No new settings applied"
    } else {
        $settingsObject | ConvertTo-Json | Set-Content $settingsPath
        Write-Host "Settings updated and saved successfully"
    }
    
    Start-Service -Name "com.docker.service" -ErrorAction SilentlyContinue
    
    while ((Get-Service -Name "com.docker.service").Status -ne "Running") {
        Write-Host (Get-Service -Name "com.docker.service").Status
        Start-Sleep -Seconds 1
    }
    
    if((Get-Service -Name "com.docker.service").Status -eq "Running"){
        Write-Host "Service: com.docker.service is now:"
        Write-Host (Get-Service -Name "com.docker.service").Status
    }
    
    $dockerDesktopFilePath = $env:ProgramFiles | Join-Path -ChildPath 'DockerDockerDocker Desktop.exe'; Start-Process -FilePath $dockerDesktopFilePath
    
    $ipcTimeout = New-TimeSpan -Seconds 20
    $waitUntil = [datetime]::Now.Add($ipcTimeout)
    $pipeOpen = $false
    Write-Host 'Probing docker engine I/O pipe'
    do {
      Start-Sleep -Milliseconds 100
      $pipeOpen = Test-Path -LiteralPath \.pipedocker_engine
    } until ($pipeOpen -or ($waitUntil -le [datetime]::Now))
    
    if (-not $pipeOpen) {
      Write-Warning "Failed to observe named IPC pipe docker_engine within timeout"
      return
    }
    
    $responseTimeout = New-TimeSpan -Seconds 5
    $waitUntil = [datetime]::Now.Add($responseTimeout)
    
    Write-Host 'Querying docker server info'
    do {
      Start-Sleep -Milliseconds 500
      $dockerInfoOutput = docker info 2>&1
      $dockerInfoSuccess = $?
    } until ($dockerInfoSuccess -or ($waitUntil -le [datetime]::Now))
    
    if (-not $dockerInfoSuccess) {
      Write-Warning "docker info failed within timeout"
      return
    }
    
    Write-Host 'Docker Desktop Is Runing Now'
    

  2. The following sequence of steps work for me with Docker Desktop 4.3 and Docker Engine 20.10 on Windows:

    1. Stop Docker Desktop
    2. Update settings.json
    3. Restart Docker engine service
    4. Start Docker Desktop
    5. Wait for
      • creation of docker_engine IPC pipe, and for
      • service to respond to docker info queries

    For stopping the process, make life easier by using Get-Process ... |Stop-Process – this allows you to ignore failure to resolve existing running processes but still stop on failure to kill any existing processes:

    $ErrorActionPreference = 'Continue'
    
    Get-Process 'Docker Desktop' -ErrorAction Ignore |Stop-Process -Force -ErrorAction Stop
    

    Now you can remove all that ugly code to detect whether the processes stopped – it either does exactly what you expect, or it fails conclusively!

    Next, let’s look at updating the settings file. Your regex-replace strategy probably works most of the time, but a more robust approach would be:

    • parse the document as JSON
    • modify the resulting object
    • convert it back to JSON and save
    # define settings to update
    $settingsToUpdate = @{
        exposeDockerAPIOnTCP2375 = $true
        updateHostsFile = $true
        licenseTermsVersion = 2
    }
    
    # read settings file from disk (save for later), and then parse with ConvertFrom-Json
    $settingsPath = "$env:APPDATADockersettings.json"
    $settingsContent = Get-Content $settingsPath -Raw 
    $settingsObject  = $settingsContent |ConvertFrom-Json
    
    # update relevant settings
    $trackUpdates = 0
    foreach ($update in $settingsToUpdate.GetEnumerator()) {
      if ($target = $settingsObject.psobject.Properties.Match($update.Key)) {
        if ($target.Value -ne $update.Value) {
          # update existing entry
          $target.Value = $newValue.Value
    
          $trackUpdates++
        }
      }
      else {
        # add new entry
        #
        # this is a good place to emit or log warnings if 
        # you expect the file to always be pre-populated
        # 
        Add-Member -InputObject $settingsObject -Name $newValue.Key -Value $new
    
        $trackUpdates++
      }
    }
    
    if ($trackUpdates -eq 0) {
      # no updates applied? nothing more to be done
      Write-Host "No new settings applied"
      return
    }
    
    # serialize and write settings to disk
    $settingsObject |ConvertTo-Json |Set-Content $settingsPath
    

    Step 3 and 4 are the simplest – Restart-Service to restart the engine service, Start-Process to launch Docker Desktop:

    # restart engine service
    Restart-Service -Name com.docker.service
    
    # start docker desktop
    $dockerDesktopFilePath = $env:ProgramFiles |Join-Path -ChildPath 'DockerDockerDocker Desktop.exe'
    Start-Process -FilePath $dockerDesktopFilePath
    

    If you want to revert the settings changes on failure to restart docker engine at this point, consider using a simple try/catch to re-apply the original document contents:

    try {
      Restart-Service -Name com.docker.service
    }
    catch {
      # roll-back settings changes and quit
      $settingsContent |Set-Content $settingsPath -Force
      # replace `throw` with `return` if you want to fail silently here
      throw
    }
    

    Now we just need to wait for docker desktop to become responsive – the following appears to work consistently for me:

    # let's give it 20 seconds 
    $ipcTimeout = New-TimeSpan -Seconds 20
    $waitUntil = [datetime]::Now.Add($ipcTimeout)
    $pipeOpen = $false
    Write-Host 'Probing docker engine I/O pipe'
    do {
      # stat for a named pipe is pretty 
      # low-overhead, retry every 100ms
      Start-Sleep -Milliseconds 100
      $pipeOpen = Test-Path -LiteralPath \.pipedocker_engine
    } until ($pipeOpen -or ($waitUntil -le [datetime]::Now))
    
    if (-not $pipeOpen) {
      # not responding, time to quit
      Write-Warning "Failed to observe named IPC pipe docker_engine within timeout"
      return
    }
    
    # let's give it another 10 seconds to respond to client requests
    $responseTimeout = New-TimeSpan -Seconds 5
    $waitUntil = [datetime]::Now.Add($responseTimeout)
    
    Write-Host 'Querying docker server info'
    do {
      # much more expensive probe than 
      # the pipe test, retry only every 500ms
      Start-Sleep -Milliseconds 500
      $dockerInfoOutput = docker info 2>&1 
      $dockerInfoSuccess = $?
    } until ($dockerInfoSuccess -or ($waitUntil -le [datetime]::Now))
    
    if (-not $dockerInfoSuccess) {
      Write-Warning "docker info failed within timeout"
      return
    }
    

    Hope this helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search