## TL;DR

Docker scout streams the image and builds its SBOM index through Docker's backend, and on a fresh CI runner every layer pull, index build, and advisory fetch starts from zero. Pre-pull the image, cache scout's data between runs, cap the job with an explicit timeout, and split the work: a fast critical-only scan on PRs, the full scan on a nightly schedule. A scan that hangs for 6 hours is a pipeline design problem, not a scout bug.

## The query

```text
docker scout scan never finished on the agent's CI runner - the job hit the 6-hour GitHub Actions timeout
```

## Use this when

- A `docker scout` scan runs for hours on a CI runner and gets killed by the job timeout.
- The agent cannot tell whether scout is progressing or stuck.
- Scout scans pass locally but hang on fresh CI runners.

## Not for

- `docker scout: unauthorized: authentication required` (that is a Docker Hub session problem).
- Scout finishing but reporting unexpected results (that is a coverage question).
- Scans that fail in seconds with an error (that is a config problem, not a hang).

## Steps

### Step 1: Cap the job so it fails loudly instead of burning 6 hours

```yaml
jobs:
  scan:
    timeout-minutes: 30
```

Expected output: the job now fails at 30 minutes with a timeout error the agent can detect and act on, instead of sitting at the 6-hour default until GitHub kills it.

### Step 2: Pre-pull the image before scanning

```bash
docker pull "[image]"
docker scout cves "[image]"
```

Expected output: the pull progress is visible separately from the scan, so the agent can see whether the time is going to the registry or to scout's analysis. On a cache-cold runner the pull is often most of the wall time.

### Step 3: Cache scout's data directory between runs

```yaml
- uses: actions/cache@v4
  with:
    path: ~/.docker/scout
    key: scout-[runner-os]-[week]
```

Expected output: repeat scans reuse the SBOM index and advisory data instead of rebuilding them from zero. First run on a new base image is still slow, but every run after it is fast.

### Step 4: Split quick PR scans from full nightly scans

```bash
docker scout cves --only-severity critical,high "[image]"
```

Expected output: the PR scan finishes in minutes because it filters before the expensive policy evaluation. Move the unfiltered full scan to a nightly workflow where a 30-minute run is acceptable.

### Step 5: Pin the image digest

```bash
docker scout cves "[image]@[digest]"
```

Expected output: scout analyzes one immutable artifact. Scanning a floating tag while it moves can force re-indexing mid-run and produce the never-finishing behavior.

### Step 6: Verify scout can reach its backend from the runner

```bash
docker scout version
docker scout quickview "[image]"
```

Expected output: `quickview` returns the layer overview quickly. If even `quickview` hangs, the problem is network egress from the runner to Docker's backend (proxy, allowlist), not the image size. Fix egress before tuning the scan.

## Variant phrasings

### docker scout hangs in CI but works locally
The local Docker Desktop has a warm cache and fast network. Steps 2-3 replicate that warmth on the runner.

### docker scout cves takes hours on a large image
Large images mean large SBOM indexes. Step 4 limits the per-PR cost; step 3 amortizes the index build.

### github actions scout step never completes
Step 1 bounds it, step 6 checks whether the runner can even reach scout's backend.

## Why it happens

Scout does three expensive things per scan: pull the image layers, build an SBOM index, and evaluate vulnerabilities and policy against Docker's advisory data. A developer laptop amortizes all three across days of warm cache. A fresh GitHub Actions runner pays all three from zero on every run, and scout streams progress in a way that looks idle in CI logs. The agent reads "no output for an hour" as a hang, when it is usually just a cold cache doing honest work very slowly.

## Edge cases

- Scout requires a Docker Hub login for full functionality. An expired session degrades to limited scans that can behave differently from local runs.
- The cache key must rotate when the base image changes, or scout serves a stale index. Include the base image digest in the key for correctness.
- Multi-arch images double the index work. If the runner only needs one architecture, scan the single-arch digest.
- `timeout-minutes` on the job is a backstop, not a fix. If the scan always hits 30 minutes, the split in step 4 is still required.
- Self-hosted runners behind an egress proxy need the proxy configured for the Docker daemon too, not just the scout CLI.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_SVqNkuAgjrzKc2Yq_2Jv_A
