Guidance for AI coding agents working in this repository. Human contributors should also read CONTRIBUTING.md and the documentation site which are the source of truth.
The NVIDIA Container Toolkit is a collection of utilities that allow you to run GPU-accelerated containers. It is designed to work with any container runtime, e.g. docker, containerd, cri-o, podman. It is written primarily in Go.
Historically, NVIDIA GPUs were injected into containers via an OCI
preStart hook.
The preStart hook would invoke the nvidia-container-cli which wraps the
NVIDIA Container Runtime library, libnvidia-container, written in C.
The preStart hook would discover GPUs on the host (during every container
startup) and perform a number of operations to make the requested GPU(s)
available in the container. Most of these operations, e.g. setting up cgroups
for devices, bind mounting files, are what low-level runtimes, like runc,
specialize in. This is the primary motivation for the development of the
Container Device Interface (CDI),
which provides a standard specification for defining "what" access to a device
means. Device vendors no longer need to write and maintain their own hooks to
enable device access. Instead, they define a CDI spec for their devices, and
runtime like runc take care of "injecting" devices based on the spec.
The NVIDIA Container Toolkit defaults to using CDI for enabling GPU
support in containers. Current and future development is focused on the
CDI-based implementation. The preStart hook-based implementation is not
leveraged by default, but users can opt-in to using it by configuring
NVIDIA Container Toolkit to run in legacy mode.
cmd/— binary entrypoints.internal/— private modules shared across commands.pkg/— public modules intended for broader reuse.api/— versioned config schema types.tests/— integration and e2e tests; this is a separate Go module (tests/go.mod) from the root module.packaging/—.deb/.rpmpackaging metadata.deployments/— container/systemd/udev artifacts and thedevelbuild image used bymake docker-*targets.hack/,scripts/— release and CI helper scripts.third_party/— vendored submodules; treat as upstream, not project code.testdata/— data used for unit tests.
All standard tasks go through the Makefile. Prefer make targets over invoking tools directly so CI and local runs stay consistent.
make build # go build ./...
make cmds # build all cmd/ binaries
make test # run unit tests
make lint # golangci-lint run ./... (config is in .golangci.yml)
make fmt # gofmt -s -l -w
make goimports # goimports -local github.com/NVIDIA/nvidia-container-toolkitAlways run make build, make fmt and make test before considering Go
changes complete.
Any make <target> can be run inside the project's build container instead
of on the host with make docker-<target> (e.g. make docker-test,
make docker-lint) — this matches the environment CI uses, so prefer it if
local Go/golangci-lint versions are in doubt.
- Comments explain why, not what. Identifier names should carry the "what."
- Keep changes scoped to the task. No drive-by refactors, speculative abstractions, or unrelated formatting churn — one concern per PR.
- When adding new files, add the Apache 2.0 license header to the top of the file. Match the header used in other files exactly rather than inventing a variant.
- Follow existing patterns in
cmds/,pkg, andinternal/for logging and error wrapping rather than introducing new libraries. - Vendor directory (
vendor/) is checked in; rungo mod tidy/go mod vendorafter dependency changes and do not hand-edit vendored code. - Mocks are generated (files end in
_mock.go); don't hand-edit generated mocks — regenerate viamake generate(go generate ./...) instead. - Standard Go formatting (
gofmt -s -l -w) and import grouping viagoimports -local github.com/NVIDIA/nvidia-container-toolkit.
- Unit tests are co-located
*_test.gofiles usingtestify(require/assert), typically table-driven with amap[string]struct{...}of cases andt.Run(name, ...). Run viamake test. New behavior needs appropriate test coverage. - When fixing a bug, add a regression test that fails without the fix.
- For any significant change (architectural change, new feature, breaking change, non-trivial bug fix), an issue should exist describing the problem/proposal before implementation begins — check for or ask about a linked issue rather than assuming a PR alone is sufficient.
- All commits must be signed off (DCO):
git commit -s, producing a trailingSigned-off-by: Name <email>line. Do not fabricate a sign-off identity — use the configured git user's identity. - Do not open, push to, or comment on GitHub issues/PRs without explicit user confirmation.
- Keep PR titles short and imperative; the body should explain motivation ("why"), not just restate the diff.
- Never commit credentials, API keys, tokens, passwords, kubeconfigs, or private keys.
- Do not hand-edit generated files. Run
make generateto regenerate Go mocks. Rungo mod tidy/go mod vendorafter dependency changes and do not hand-edit vendored code. - Do not modify anything in
third_party. These are vendored Git submodules that track upstream repos, namelylibnvidia-container. If a change is required in a Git submodule, a separate PR needs to be raised against that repository. - Do not commit built binaries,
coverage.out, or anything the .gitignore already excludes. - Do not modify CODEOWNERS or GOVERNANCE.md unless the task is explicitly about that.