Self-hosted service
GoogleContainerTools/container-structure-test avatar
GoogleContainerTools/container-structure-test

container-structure-test: verifying image contents before they reach a registry

validate the structure of your container images

2,497 stars212 forksGoApache-2.0

At a glance

What is it?
A Go CLI from GoogleContainerTools that asserts on commands, files and image metadata using a YAML or JSON config. It is in maintenance mode, so the question is whether its checks still fit your pipeline.
Who is it for?
Adopt it if you already build images in CI and want assertions on binaries, config files or labels without writing a Dockerfile test harness. Do not adopt it if you need runtime behaviour checks, multi-service interaction or a project with active feature development: the README states it is in maintenance mode and the last push was on 2026-07-20.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 72 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap between a successful build and a correct image

A Docker build can exit zero while the image is wrong. A package installs to a different path, a config file keeps a placeholder value, a binary is missing from PATH, or a label that your deployment tooling reads never got set. None of that fails the build. It fails later, in a cluster, usually at 3am.

container-structure-test targets exactly that gap. It takes a built image plus a config file describing what should be true about it, then reports pass or fail. The README describes four test types: command tests, file existence tests, file content tests, and a single metadata test. That set is deliberately narrow. It is not a runtime test framework and does not start your service to see if it responds.

The audience is platform and release engineers who already have an image-building step and want a gate between build and push. If your images are built with Bazel, the repository ships defs.bzl and a BUILD.bazel at the top level, which suggests Bazel integration is a first-class path rather than an afterthought.

How the driver, the config and the image interact

The CLI is a single binary built from cmd/container-structure-test, using cobra for commands and go-dockerclient plus go-containerregistry for image access. The test command takes an image reference and one or more config files.

Config loading is ordered. The README states that multiple config files may be specified in a single run and that the driver executes the tests in order. That matters if you split a shared baseline config from per-service config: the order you list them on the command line is the order they run.

Command tests are the interesting part mechanically. The README says each command in the setup section runs in a separate container and then commits a modified image to be the new base image for the test run. So a setup step like installing a package produces an intermediate image that the next command runs against. These intermediate artifacts are deleted after the run unless you pass --save, which the README describes as being for debugging.

One behaviour to internalise: entrypoints are overwritten by default. The README is explicit that this is to avoid unexpected output, and that if your entrypoint matters you should call it yourself through the setup field. Teams that skip this line in the docs usually conclude the tool is broken when their command test runs against the wrong environment.

Installing container-structure-test and running a first config

On macOS the README gives a Homebrew formula. On Linux, the documented path is a direct download of the release binary from GitHub, followed by a chmod and a move onto PATH. The Linux example below avoids sudo by putting the binary in $HOME/bin.

bash
curl -LO https://github.com/GoogleContainerTools/container-structure-test/releases/latest/download/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && mkdir -p $HOME/bin && export PATH=$PATH:$HOME/bin && mv container-structure-test-linux-amd64 $HOME/bin/container-structure-test

After that, container-structure-test should resolve on PATH. The README notes the framework looks for the image in the local Docker daemon unless you supply a tar, and that --pull forces a remote pull first.

The README's own example run, using a config file named config.yaml, looks like this:

bash
container-structure-test test --image gcr.io/registry/image:latest \
--config config.yaml

A config needs schemaVersion set to 2.0.0, which the README calls out as required in all container-structure-test YAML files. The README gives this command test example, showing setup, args and expectedOutput:

yaml
commandTests:
  - name: "gunicorn flask"
    setup: [["virtualenv", "/env"], ["pip", "install", "gunicorn", "flask"]]
    command: "which"
    args: ["gunicorn"]
    expectedOutput: ["/env/bin/gunicorn"]

Expected output is a per-test pass or fail report. If a command test fails, the expected or excluded output regexes are the first thing to check, since a regex that matches nothing looks identical to a command that produced nothing.

Where the model breaks down

Command tests are stateful in a way that surprises people. Because setup commands commit intermediate images and each command runs in its own container, a setup step that writes to a path outside the committed layers may not be visible to later steps. The README does not document what is and is not preserved across those commits, so treat setup as best-effort and verify against your own image.

The tool also assumes a Docker daemon. The README describes the docker and tar drivers and says the framework looks for the image in the local Docker daemon if it is not provided as a tar. Nothing in the README describes a Podman driver, so anyone on a Podman-only host should plan to export a tar first rather than expect a drop-in swap.

Finally, the project's own framing is a constraint. The README states that container-structure-test is not an officially supported Google project and is currently in maintenance mode. Contributions are welcome, but maintenance mode means you should not expect new test types to appear. If your requirement is behavioural testing, this is the wrong tool and always will be.

How it differs from Skaffold's test stage and from Testcontainers

Skaffold has a test stage, and people often reach for it because Skaffold already orchestrates their build and deploy loop. The difference is scope: Skaffold's test stage exists inside a Skaffold workflow, whereas container-structure-test is a standalone binary you can call from any pipeline. If you do not use Skaffold, adopting it just to get image assertions is a large dependency for a small job.

Testcontainers sits on the other side of the line. It starts containers so your test code can talk to a real dependency, which is about runtime behaviour and integration. container-structure-test never runs your application's request path. It inspects the image: which commands exist, what they print, which files are present, what those files contain, and what the metadata says. The two are complementary rather than competing, and choosing between them is really a question of whether your failure mode is a wrong image or a wrong interaction.

Maintenance, release cadence and the Apache-2.0 licence

The last push to the default branch was on 2026-07-20, and the most recent release in the list is v1.22.1 from 2025-12-16. The repository is not archived. The README's own statement that the project is in maintenance mode is the more useful signal than the push date: it tells you the maintainers are not adding capability, whatever the commit graph looks like.

Upgrade cost is low in normal use. The config schema is versioned through schemaVersion, currently 2.0.0, so a schema bump is the event that would force edits to your YAML. The README does not document a migration path between schema versions, which means you should pin the binary version in CI rather than tracking latest, and read CHANGELOG.md before moving.

On licensing: the repository carries Apache-2.0, and the Makefile headers repeat the Apache 2.0 notice. That is a permissive licence with a patent grant, and it is the same licence as much of the surrounding container tooling. This is a description of what the repository states, not legal advice; if you redistribute a modified binary, read the LICENSE file rather than this paragraph.

Editorial conclusion

Adopt it if you already build images in CI and want assertions on binaries, config files or labels without writing a Dockerfile test harness. Do not adopt it if you need runtime behaviour checks, multi-service interaction or a project with active feature development: the README states it is in maintenance mode and the last push was on 2026-07-20. Before wiring it in, verify that your driver works in your environment, since the README warns that container builds are not updated with new releases, and confirm the gcr.io/gcp-runtimes/container-structure-test:latest tag you plan to use is current.

Frequently asked questions

What is a structure test in container-structure-test?

It is a check written in a YAML or JSON config that asserts something about a built image rather than about a running service. The README lists four kinds: command tests, file existence tests, file content tests, and one metadata test. The driver loads the config, runs the tests in order, and reports pass or fail.

How do I install container-structure-test on Linux or macOS?

On macOS the README gives a Homebrew formula, brew install container-structure-test. On Linux and on macOS without Homebrew it documents downloading the release binary from GitHub, running chmod +x on it, and moving it onto your PATH. The README also notes that a container image exists at gcr.io/gcp-runtimes/container-structure-test:latest for running tests through Google Cloud Builder.

Does container-structure-test work with Podman?

The README only describes the docker and tar drivers, and says the framework looks for the image in the local Docker daemon if it is not provided as a tar. No Podman driver is documented. On a Podman-only host, the tar path is the documented route.

Why does my command test fail even though the command works when I run the image manually?

The README states that all entrypoints are overwritten by default to avoid unexpected output. If your entrypoint sets up the environment the command depends on, you need to invoke it yourself through the setup field. Setup commands each run in a separate container and commit a modified image used as the base for the test run.

Is container-structure-test an officially supported Google project?

No. The README states that container-structure-test is not an officially supported Google project and is currently in maintenance mode, while noting that contributions are still welcome. The repository is not archived and the last push was on 2026-07-20.

Official sources

  1. GoogleContainerTools/container-structure-test on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/googlecontainertools-container-structure-test.svg)](https://hysenlabs.com/projects/googlecontainertools-container-structure-test)