airlock/docker.rs
1//! Docker daemon connection with non-standard socket discovery.
2
3use std::{env, path::PathBuf};
4
5use bollard::{Docker, API_DEFAULT_VERSION};
6use home::home_dir;
7use saluki_error::{ErrorContext as _, GenericError};
8use tracing::debug;
9
10/// Default connection timeout, in seconds, matching bollard.
11const DEFAULT_TIMEOUT: u64 = 120;
12
13/// The standard Docker socket path on Linux.
14const STANDARD_SOCKET: &str = "/var/run/docker.sock";
15
16/// Connect to the Docker daemon, searching non-standard socket locations on macOS.
17///
18/// Resolution order:
19///
20/// 1. If `DOCKER_HOST` is set **or** `/var/run/docker.sock` exists on disk, defer to bollard's
21/// default connection logic — no extra work needed.
22/// 2. Otherwise, try each candidate socket path in order and use the first that exists.
23/// 3. If no candidate is found, fall back to `Docker::connect_with_defaults()` so the error
24/// message comes from bollard rather than from us.
25///
26/// # Candidate paths
27///
28/// These are checked (in order) when the standard socket is absent:
29///
30/// | Path | Environment |
31/// |------|-------------|
32/// | `$HOME/.docker/run/docker.sock` | Docker Desktop |
33/// | `$HOME/.orbstack/run/docker.sock` | OrbStack |
34/// | `$HOME/.rd/docker.sock` | Rancher Desktop |
35/// | `$HOME/.lima/default/sock/docker.sock` | Lima (default instance) |
36/// | `$HOME/.lima/docker/sock/docker.sock` | Lima (docker instance) |
37/// | `$HOME/.colima/default/docker.sock` | Colima |
38/// | `$HOME/.colima/docker/docker.sock` | Colima (docker profile) |
39/// | `$HOME/.local/share/containers/podman/machine/podman.sock` | Podman Desktop (macOS) |
40///
41/// # Errors
42///
43/// Returns an error if no reachable Docker daemon can be found. When connecting through a
44/// discovered non-standard path, the error includes the path that was attempted.
45pub fn connect() -> Result<Docker, GenericError> {
46 // Fast path: DOCKER_HOST is explicitly configured, or the standard socket exists.
47 if env::var("DOCKER_HOST").is_ok() || PathBuf::from(STANDARD_SOCKET).exists() {
48 return Ok(Docker::connect_with_defaults()?);
49 }
50
51 // Build candidate list from well-known non-standard locations.
52 if let Some(home) = home_dir() {
53 let candidates = [
54 home.join(".docker/run/docker.sock"),
55 home.join(".orbstack/run/docker.sock"),
56 home.join(".rd/docker.sock"),
57 home.join(".lima/default/sock/docker.sock"),
58 home.join(".lima/docker/sock/docker.sock"),
59 home.join(".colima/default/docker.sock"),
60 home.join(".colima/docker/docker.sock"),
61 home.join(".local/share/containers/podman/machine/podman.sock"),
62 ];
63
64 for path in &candidates {
65 if path.exists() {
66 let path = path.display();
67 debug!("using non-standard Docker socket: {path}");
68 return Docker::connect_with_unix(&path.to_string(), DEFAULT_TIMEOUT, API_DEFAULT_VERSION)
69 .with_error_context(|| format!("Failed to connect to Docker using socket discovered at {path}"));
70 }
71 }
72 }
73
74 // Nothing found — let bollard try (and fail with its own error message).
75 Ok(Docker::connect_with_defaults()?)
76}