- CRITICAL: Change DNS zones endpoint from GET to POST to avoid leaking API token in URL query parameters - HIGH: Add sync.RWMutex to protect dnsProvider field in Server, Deployer, and proxy Manager against concurrent read/write races - HIGH: Capture old DNS provider reference synchronously before launching background cleanup goroutine - HIGH: Use getDNS()/getDNSProviderLocked() accessors instead of direct field reads in all DNS operations
5.4 KiB
Feature: Docker Diagnostic Hints on Disconnection
Problem: When Docker is unreachable, the UI shows a generic "Docker disconnected" label with no actionable guidance. Users (especially on Windows/macOS where Docker Desktop must be running) have no idea what's wrong or how to fix it.
Goal: Enrich the health-check response with a structured diagnostic object so the frontend can display platform-aware, actionable hints.
Backend Changes
1. Enhance GET /api/health response (health.go)
Currently returns { "docker": true|false }. Change to:
{
"docker": {
"connected": false,
"error": "dial unix /var/run/docker.sock: connect: no such file or directory",
"category": "socket_not_found",
"hints": [
"Docker Desktop does not appear to be running.",
"Start Docker Desktop and wait for it to finish initializing.",
"If using a custom socket path, check DOCKER_HOST env variable."
],
"platform": "windows",
"checked_at": "2026-03-30T12:34:56Z"
}
}
2. Create a Docker diagnostics module (new file, e.g. internal/docker/diagnostics.go)
Classify the Ping error into a diagnostic category and generate platform-specific hints. Follow the pattern already established in hints.go for proxy validation.
Error categories to handle:
| Category | Error signature | Windows hints | Linux hints | macOS hints |
|---|---|---|---|---|
socket_not_found |
no such file or directory, The system cannot find the file specified |
Docker Desktop not running; start it from Start Menu or system tray | Docker daemon not running; sudo systemctl start docker |
Docker Desktop not running; start from Applications or open -a Docker |
connection_refused |
connection refused |
Docker Desktop is starting up — wait ~30s and retry | Docker daemon is starting; sudo systemctl status docker |
Docker Desktop is starting; check the whale icon in the menu bar |
permission_denied |
permission denied |
Run the application as Administrator, or add your user to the docker-users group |
Add your user to the docker group: sudo usermod -aG docker $USER then re-login |
Check Docker Desktop settings -> Resources -> File Sharing |
timeout |
context deadline exceeded, i/o timeout |
Docker Desktop may be overloaded or hanging — restart it | Docker daemon may be overloaded; check journalctl -u docker |
Docker Desktop may be unresponsive; restart from menu bar |
tls_error |
tls:, certificate |
Check Docker TLS cert configuration and DOCKER_TLS_VERIFY |
Verify certs in ~/.docker/ match daemon config |
Check ~/.docker/ TLS configuration |
unknown |
(fallback) | Show raw error with link to Docker Desktop troubleshooting docs | Show raw error with dockerd docs link |
Show raw error with Docker Desktop docs link |
Detect the platform via runtime.GOOS in the diagnostics module (the binary runs on the host, so this is accurate).
3. Expose runtime.GOOS once in diagnostics, don't scatter it through handlers.
4. Preserve backward compat — if any external consumer depends on the old "docker": bool shape, consider a migration path or version the health endpoint. Internal-only API can break freely.
Frontend Changes
5. Update the API type (api.ts)
interface DockerHealth {
connected: boolean;
error?: string;
category?: string;
hints?: string[];
platform?: string;
checked_at?: string;
}
export function getHealth(): Promise<{ docker: DockerHealth }> {
return get<{ docker: DockerHealth }>('/api/health');
}
6. Enhance the health indicator (+layout.svelte)
When dockerConnected === false:
- Show a clickable/expandable area (tooltip, popover, or collapsible panel) below the red dot.
- Display the
hintsarray as a bulleted list. - Optionally show the raw
errorin a<details>collapse for advanced users. - Show
checked_atas relative time ("last checked 15s ago"). - Add a manual "Retry now" button that triggers an immediate health check instead of waiting for the 30s poll.
7. Add i18n keys (en.json, ru.json)
Add keys for each hint category so hints can be translated. The backend should return category + platform identifiers; the frontend can use them to look up localized hint text instead of displaying raw English strings from the backend. This keeps i18n centralized in the frontend.
Architecture Notes
- The proxy validator (validator.go, hints.go) already implements a similar pattern: classifying errors by substring match and returning human-readable hints. Reuse that approach for consistency.
- Keep diagnostics pure — a function that takes an
errorandruntime.GOOSand returns(category string, hints []string). No side effects, easy to unit-test. - Consider caching the diagnostic result for a few seconds to avoid spamming Docker if the frontend retries rapidly.
Testing
- Unit-test the diagnostics function with synthetic errors for each category x platform combination.
- Integration-test the health endpoint with a mock Docker client that returns each error type.
- Frontend: test that the hint UI renders correctly for each category and collapses/expands properly.