Run CAST Profiler with Docker
Overview
CAST Profiler is published as a container image, which is the least intrusive way to run it on a build agent: no download step, no unzip, no executable to keep up to date. This page covers the image, the invocation, and the one constraint that catches most first attempts - the container runs as a non-root user.
The image
| Property | Value |
|---|---|
| Repository | castimaging/profiler-cli |
| Tags | latest, v2, and exact versions such as 2.0.3 |
| Architectures | linux/amd64 and linux/arm64 |
| Base image | Red Hat Universal Base Image 9 minimal |
| Working directory | /app |
| Runs as | UID 10001 |
The entrypoint passes every argument straight through to the CLI, so command-line options go directly after the image name and behave exactly as documented in the CLI reference.
There is a second repository, castbuild/profiler, whose tags include -SNAPSHOT builds. That is the internal build repository. Use castimaging/profiler-cli.
Because the image is published for both linux/amd64 and linux/arm64, this is also the supported way to run CAST Profiler on macOS, where no native build is available. Docker pulls the architecture matching your Mac, so Apple Silicon runs the arm64 image and Intel the amd64 one, with no emulation layer in either case.
Verify Docker is available
$ docker --version
Scan a source folder
Mount the source read-only and mount an output folder to collect the results:
$ docker run --rm \
-v /src/my-app:/source:ro \
-v "$PWD/profiler-out:/out" \
castimaging/profiler-cli:2.0.3 \
--offline --clean-log --output /out --name my-app /source
--name here is the CAST Profiler CLI option, not docker run --name. It works because everything after the image name is passed to the CLI. If you move it before the image name, Docker consumes it as the container name and the CLI never sees it.
To publish the full results page instead of keeping everything local, swap the mode and suppress the browser launch, which cannot work on an agent anyway:
$ docker run --rm \
-v /src/my-app:/source:ro \
castimaging/profiler-cli:2.0.3 \
--complete-insight --no-browser --clean-log --name my-app /source
File ownership
The container runs as UID 10001, not root. On a Linux host, where bind mounts expose the host’s own ownership straight through, two things follow:
- The mounted source must be readable by UID
10001. World-readable source is fine; source owned by another user with restrictive permissions is not. - The mounted output folder must be writable by UID
10001, otherwise the run fails when it writes the report files. Creating the folder on the host before the run and making it group- or world-writable is the simplest fix.
If your build agent’s permissions make that awkward, override the user. The entrypoint handles an overridden user by setting HOME to a writable location when it is unset, so this is a supported path rather than a workaround:
$ docker run --rm --user "$(id -u):$(id -g)" \
-v /src/my-app:/source:ro \
-v "$PWD/profiler-out:/out" \
castimaging/profiler-cli:2.0.3 \
--offline --clean-log --output /out --name my-app /source
On macOS and Windows this section rarely applies: Docker Desktop’s file sharing maps ownership across the virtual machine boundary for you, so mounted folders are readable and writable without any of the above. Expect bind-mount I/O to be slower than on Linux, which is noticeable when scanning a large tree.
Paths in the results
Every path in the result files is the path as seen inside the container. Scanning /source produces results whose root path is /source, not /src/my-app. This matters when you correlate an alert against a working copy on the host, and when you compare two scans of the same application that were mounted at different paths - the reported paths differ even though the content is identical. Mount at a consistent path across runs.
Pinning a version
Pin an exact tag in a pipeline:
$ docker run --rm ... castimaging/profiler-cli:2.0.3 ...
The reference knowledge base that decides which languages and technologies can be recognised is bundled in the image, so the tag determines the detection results as much as it determines the code. Two consequences:
- Pinning a tag makes results comparable across runs, which is what you want when you are tracking an application over time.
- Moving to a newer tag can change the inventory without the source code changing, because the reference data moves with it. Each evaluation records the reference data version and its content hashes under
dataIntegrity, so you can tell the two causes apart - see Result files.
Using --pull always with a moving tag such as latest gives you the opposite trade-off: current detection, no comparability. Prefer an exact tag and upgrade deliberately.
Multiple source folders
Mount each one and list them all as positional arguments:
$ docker run --rm \
-v /src/backend:/source/backend:ro \
-v /src/frontend:/source/frontend:ro \
-v "$PWD/profiler-out:/out" \
castimaging/profiler-cli:2.0.3 \
--offline --clean-log --output /out --name my-app /source/backend /source/frontend