How to Run testssl.sh Within Docker Containers: Complete Guide

You can run testssl.sh within Docker containers using the official scratch-based image from GitHub Container Registry or Docker Hub, passing scanner arguments directly to docker run while bind-mounting host directories to capture output files.

The drwetter/testssl.sh repository provides a containerized version of the SSL/TLS scanner that eliminates host-side dependencies. This guide explains how to execute scans using pre-built images and how to build custom variants from source.

Understanding the testssl.sh Docker Image Architecture

The container image follows a multi-stage build pattern designed for minimal footprint and security.

Multi-Stage Build Process

The Dockerfile defines two distinct stages. First, a builder stage uses an openSUSE Leap base to pull required binaries—bash, openssl, curl, and dependencies—into a custom root-fs at /rootfs (lines 6-20).

Second, a runtime stage copies this stripped root-fs into a scratch image (lines 33-60). This approach ensures the final image contains only the essential runtime components, reducing attack surface and image size.

Security-First Runtime Configuration

The runtime image creates a non-root user named testssl with UID 1000 and sets this as the default execution context. The entrypoint is hardcoded to testssl.sh, meaning any arguments passed to docker run are interpreted directly as scanner options.

Running testssl.sh in Docker Containers

The official images are published to both Docker Hub and GitHub Container Registry under ghcr.io/testssl/testssl.sh.

Quick Start with Pre-Built Images

To verify the installation or view available options, run the container without arguments to display the built-in help:

docker run --rm -it ghcr.io/testssl/testssl.sh:3.2

This executes the default entrypoint and shows the scanner's --help output.

Handling Output Files and Permissions

When generating reports with flags like --html, --json, or --log, the scanner creates files with ownership testssl:1000 and mode 644 inside the container. To retrieve these files on your host system, you must bind-mount a host directory and set the container's working directory accordingly.

Scan a target and write an HTML report to your host:

docker run --rm -it \
  -v /tmp:/data \
  --workdir /data \
  ghcr.io/testssl/testssl.sh:3.2 \
  --htmlfile ./ example.com

The HTML file appears as /tmp/example.com_p443-<date>-<time>.html on your host machine. According to Dockerfile.md (lines 13-18), this pattern ensures proper file permissions and accessibility.

Building Custom testssl.sh Images

You can build container images locally from specific branches or tags when you need custom modifications or the Alpine-based variant.

Building from Source

Clone the repository and build using the local Dockerfile:

git clone --branch 3.2 --depth 1 https://github.com/testssl/testssl.sh .
docker build -t localhost/testssl.sh:3.2 .

Alternatively, build directly from the remote repository without local checkout:

docker build -t localhost/testssl.sh:3.2 \
  https://github.com/testssl/testssl.sh.git#3.2

These methods reference the multi-stage build defined in Dockerfile (lines 35-43), which handles the builder and runtime stages automatically.

Alpine-Based Variant

For a smaller image footprint, use the Alpine-based Dockerfile:

docker build -t localhost/testssl.sh:3.2-alpine \
  --file https://raw.githubusercontent.com/testssl/testssl.sh/3.2/Dockerfile.alpine \
  https://github.com/testssl/testssl.sh.git#3.2

As documented in Dockerfile.md (lines 66-73), this variant uses Alpine Linux instead of the scratch-based approach, trading minimalism for broader compatibility with certain network debugging tools.

Summary

  • The official testssl.sh image uses a multi-stage build (openSUSE builder → scratch runtime) to minimize size and dependencies.
  • The container runs as non-root user testssl (UID 1000) with the entrypoint already set to the scanner script.
  • Use bind mounts and --workdir to capture HTML, JSON, or log files to your host filesystem with correct permissions.
  • Build custom images from any branch using the remote repository URL or locally cloned source.
  • Choose between the scratch-based image (smallest, most secure) or the Alpine-based variant for broader tooling support.

Frequently Asked Questions

How do I save scan results when running testssl.sh in Docker?

Bind-mount a host directory to the container and set it as the working directory. The scanner writes files with mode 644 and ownership testssl:1000, so mounting a host path like -v /tmp:/data combined with --workdir /data allows the output files to persist on your host after the container exits.

What is the difference between the standard and Alpine testssl.sh Docker images?

The standard image copies a stripped root-fs into a scratch container, resulting in a minimal runtime with no package manager or shell beyond the essentials. The Alpine variant uses Dockerfile.alpine and builds on Alpine Linux, offering a slightly larger image but including additional utilities that may help with network debugging or custom scripting.

Can I run testssl.sh in Docker without installing anything on my host?

Yes. The image contains all runtime dependencies including bash, openssl, and curl. You only need Docker installed—no other host-side packages are required. Pull the image from ghcr.io/testssl/testssl.sh or Docker Hub and run it immediately.

Which user does the testssl.sh Docker container run as?

The container executes as the non-root user testssl with UID 1000, as defined in Dockerfile (lines 33-60). This security measure ensures the scanner operates with minimal privileges inside the container environment.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →