Skip to content

docs: Add info about copying image while preserving image digest#1668

Merged
HofmeisterAn merged 6 commits intodevelopfrom
feature/update-pinned-images-doc
Mar 20, 2026
Merged

docs: Add info about copying image while preserving image digest#1668
HofmeisterAn merged 6 commits intodevelopfrom
feature/update-pinned-images-doc

Conversation

@HofmeisterAn
Copy link
Collaborator

@HofmeisterAn HofmeisterAn commented Mar 19, 2026

What does this PR do?

This PR adds documentation about copying images while preserving the image index digest. I assumed the index is always part of the image and that pushing an image to a private registry preserve the digest. It turns out that is not the case, which can cause issues for users. E.g. ryuk is pinned to:

testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0
  1. If you depend on a private registry, make the image available there either through a registry proxy (pull-through cache) or by copying it from Docker Hub with a tool that preserves the manifest list and all platform variants, for example skopeo (see docs).
  2. To use a different image reference, set the TESTCONTAINERS_RYUK_CONTAINER_IMAGE environment variable, for example to an unpinned tag (see docs).

The PR updates the docs and explains how to copy images to a private registry while keeping the digest intact.

If pinning internal images, especially Ryuk, causes too many issues, I will revert the change. /cc @tisonv

Why is it important?

Support developers to store the correct image in their private registries.

Related issues

Summary by CodeRabbit

  • Documentation

    • Added guidance for using a digest-pinned Ryuk image, instructions to mirror it into private registries while preserving multi-architecture support, a warning about unsafe manual pull/tag/push, and an environment-variable override. Normalized code-fence language labels across documentation.
  • Chores

    • Updated the SSH daemon image used by the port-forwarding component.

@HofmeisterAn HofmeisterAn requested a review from a team as a code owner March 19, 2026 16:13
@HofmeisterAn HofmeisterAn added the documentation Docs, docs, docs. label Mar 19, 2026
@netlify
Copy link

netlify bot commented Mar 19, 2026

Deploy Preview for testcontainers-dotnet ready!

Name Link
🔨 Latest commit ab914ac
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-dotnet/deploys/69bce5e1ab07170008747ef3
😎 Deploy Preview https://deploy-preview-1668--testcontainers-dotnet.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Documentation pins the Ryuk image to a digest, adds mirroring instructions that preserve multi‑arch manifests and an env override; several docs normalized code‑fence language tags; and an internal PortForwarding SSHD image constant was bumped to a newer digest‑pinned tag.

Changes

Cohort / File(s) Summary
Ryuk image docs & config
docs/api/resource_reaper.md, docs/custom_configuration/index.md
Added digest‑pinned Ryuk image (testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c7...28f0), instructions for mirroring/pull‑through registries preserving full manifest lists (skopeo examples and a Windows docker run wrapper), a warning about manual pull/tag/push breaking multi‑arch support, and documented TESTCONTAINERS_RYUK_CONTAINER_IMAGE env override.
Code: Port forwarding
src/Testcontainers/Containers/PortForwarding.cs
Bumped internal OpenSSH daemon image constant from testcontainers/sshd:1.2.0@sha256:... to testcontainers/sshd:1.3.0@sha256:c50c0f59554d...13a6a.
Docs: code‑fence normalization
docs/api/create_docker_image.md, docs/api/wait_strategies.md, docs/cicd/index.md, docs/test_frameworks/xunit_net.md
Normalized fenced code block language identifiers (e.g., dockerfile, yaml, text) and added language/title attributes to some examples; no behavioral changes.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Poem

🐰 I pin the Ryuk with a careful hop,
I nudge the SSHD to the next big drop,
I tidy code fences, tidy and spry,
A nibble of docs and a happy sigh 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: adding documentation about copying images while preserving image digest, which is the main focus of the PR.
Description check ✅ Passed The PR description provides clear explanations of what was changed and why, addressing both mandatory sections with relevant context about the pinned image digest issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/update-pinned-images-doc
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
docs/api/resource_reaper.md (1)

15-15: Add language identifiers to code blocks for better rendering.

The code blocks are missing language identifiers, which would improve syntax highlighting and documentation rendering. Consider adding:

  • Line 15: text for the image reference
  • Line 21: bash for the shell command
  • Line 31: bash for the environment variable
📝 Proposed fix to add language identifiers

For line 15:

-```
+```text
 testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0

For line 21:
```diff
-```
+```bash
 skopeo copy --all docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 docker://mynexus.mydomain/testcontainers/ryuk:0.14.0

For line 31:
```diff
-```
+```bash
 TESTCONTAINERS_RYUK_CONTAINER_IMAGE=testcontainers/ryuk:0.14.0
</details>


Also applies to: 21-21, 31-31

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/api/resource_reaper.md at line 15, Add language identifiers to the
three fenced code blocks in docs/api/resource_reaper.md: change the image
reference block containing
"testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0"
to use text, change the skopeo command block starting with "skopeo copy --all docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 docker://mynexus.mydomain/testcontainers/ryuk:0.14.0" to use bash, and change
the environment variable block containing
"TESTCONTAINERS_RYUK_CONTAINER_IMAGE=testcontainers/ryuk:0.14.0" to use ```bash
so syntax highlighting renders correctly.


</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @docs/api/resource_reaper.md:

  • Line 15: Add language identifiers to the three fenced code blocks in
    docs/api/resource_reaper.md: change the image reference block containing
    "testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0"
    to use text, change the skopeo command block starting with "skopeo copy --all docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 docker://mynexus.mydomain/testcontainers/ryuk:0.14.0" to use bash, and change
    the environment variable block containing
    "TESTCONTAINERS_RYUK_CONTAINER_IMAGE=testcontainers/ryuk:0.14.0" to use ```bash
    so syntax highlighting renders correctly.

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: Repository UI

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `54bfc907-49ac-4427-a4bb-6f634bf03162`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2ca32c91eb0518c09cc184d6a89e8858a47bd1e7 and 608252e331d71e438eeb61fa68c88958ead7b061.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `docs/api/resource_reaper.md`
* `docs/custom_configuration/index.md`
* `src/Testcontainers/Containers/PortForwarding.cs`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@HofmeisterAn
Copy link
Collaborator Author

HofmeisterAn commented Mar 19, 2026

I did some local tests, and it looks like this is more of an issue than I thought. I tested Nexus and Harbor, and neither preserved the digest (I believe this is due to Docker Registry v2). It works when pulling it through Nexus from the upstream (Docker Hub). Sorry for the hassle...

@HofmeisterAn
Copy link
Collaborator Author

Looks like --preserve-digests works. I add a quick demo later.

@HofmeisterAn
Copy link
Collaborator Author

HofmeisterAn commented Mar 19, 2026

This seem to work:

1. Start Nexus

docker network `
    create nexus-test

docker run `
    --name nexus `
    --network nexus-test `
    --detach `
    --publish 5000:5000 `
    --publish 8081:8081 `
    sonatype/nexus3:3.90.1

# Prints the admin password for logging into Nexus.
docker exec `
    nexus cat /nexus-data/admin.password

2. Log in to Nexus

  1. Open http://localhost:8081.
  2. Sign in with admin and the printed password.

3. Create Hosted Docker Repository

  1. Navigate to SettingsRepositories.
  2. Click Create repositorydocker (hosted).
  3. Configure:
    • Name: testcontainers
    • HTTP port: 5000
  4. Save the repository.

4. Push Docker Image Using Skopeo

There's no Skopeo package for Windows.

docker run `
    --rm `
    --network nexus-test `
    quay.io/skopeo/stable:v1.22.0 `
    copy `
    --all `
    --dest-creds admin:<password> `
    --dest-tls-verify=false `
    --preserve-digests `
    docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 `
    docker://nexus:5000/testcontainers/ryuk:0.14.0

docker run `
    --rm `
    --network nexus-test `
    quay.io/skopeo/stable:v1.22.0 `
    inspect `
    --creds admin:<password> `
    --tls-verify=false `
    docker://nexus:5000/testcontainers/ryuk:0.14.0

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/api/resource_reaper.md`:
- Around line 15-17: Convert the top-level fenced code blocks showing
"testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0"
and the block containing
"TESTCONTAINERS_RYUK_CONTAINER_IMAGE=testcontainers/ryuk:0.14.0" into indented
code blocks (prefix each line with four spaces) instead of using triple-backtick
fences so the file no longer triggers markdownlint MD046; locate the fenced
blocks by searching for the exact strings shown and replace the ```...``` blocks
with their indented equivalents.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 93ed913d-d556-4493-825d-add0b5790c67

📥 Commits

Reviewing files that changed from the base of the PR and between df32e7f and d1107be.

📒 Files selected for processing (1)
  • docs/api/resource_reaper.md

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
docs/api/resource_reaper.md (1)

15-17: ⚠️ Potential issue | 🟡 Minor

Convert top-level fenced blocks to indented blocks (MD046 still failing).

This repeats an earlier finding and still applies in the current revision: the top-level code blocks at Line 15 and Line 50 are fenced, but lint expects indented code blocks.

Suggested fix
-```text
-testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0
-```
+    testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0

...
-```shell
-TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0
-```
+    TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0

Also applies to: 50-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/api/resource_reaper.md` around lines 15 - 17, The markdown contains
top-level fenced code blocks that violate MD046; locate the two fenced blocks
containing the lines starting with
"testcontainers/ryuk:0.14.0@sha256:7c1a8a9..." and
"TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0"
and convert each fenced triple-backtick block into an indented code block by
removing the backticks and prefixing each line of the code with four spaces
(ensuring a blank line before and after the indented block to preserve
rendering).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/api/resource_reaper.md`:
- Around line 15-17: The markdown contains top-level fenced code blocks that
violate MD046; locate the two fenced blocks containing the lines starting with
"testcontainers/ryuk:0.14.0@sha256:7c1a8a9..." and
"TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0"
and convert each fenced triple-backtick block into an indented code block by
removing the backticks and prefixing each line of the code with four spaces
(ensuring a blank line before and after the indented block to preserve
rendering).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 82b5bcb6-fb00-4859-9cc2-6fcda0ae1c45

📥 Commits

Reviewing files that changed from the base of the PR and between d1107be and 12b0e0a.

📒 Files selected for processing (1)
  • docs/api/resource_reaper.md

@HofmeisterAn HofmeisterAn changed the title docs: Add info about copying an image while preserving the OCI index docs: Add info about copying image while preserving image index digest Mar 19, 2026
@HofmeisterAn HofmeisterAn changed the title docs: Add info about copying image while preserving image index digest docs: Add info about copying image while preserving image digest Mar 19, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
docs/api/resource_reaper.md (1)

15-17: ⚠️ Potential issue | 🟡 Minor

Use indented code blocks for top-level snippets to satisfy MD046.

These top-level fenced blocks still trigger markdownlint (MD046). Convert them to indented code blocks (4 spaces), consistent with the file’s configured style.

Suggested fix
-```text
-testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0
-```
+    testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0
...
-```shell
-TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0
-```
+    TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0

Also applies to: 50-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/api/resource_reaper.md` around lines 15 - 17, Replace the top-level
fenced code blocks with indented (4-space) code blocks to satisfy MD046: locate
the fenced blocks containing the snippets like
"testcontainers/ryuk:0.14.0@sha256:..." and the
"TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0"
example and remove the surrounding ``` fences, prefix each line of those
snippets with four spaces so they become indented code blocks; apply the same
change for the other occurrences noted (also around the lines showing the shell
variable example) to keep style consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@docs/api/resource_reaper.md`:
- Around line 15-17: Replace the top-level fenced code blocks with indented
(4-space) code blocks to satisfy MD046: locate the fenced blocks containing the
snippets like "testcontainers/ryuk:0.14.0@sha256:..." and the
"TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0"
example and remove the surrounding ``` fences, prefix each line of those
snippets with four spaces so they become indented code blocks; apply the same
change for the other occurrences noted (also around the lines showing the shell
variable example) to keep style consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f490e090-ae1f-4a22-a719-d3df8169eae2

📥 Commits

Reviewing files that changed from the base of the PR and between 12b0e0a and c43909e.

📒 Files selected for processing (1)
  • docs/api/resource_reaper.md

@HofmeisterAn
Copy link
Collaborator Author

Copying to Harbor works fine too:

docker run `
    --rm `
    quay.io/skopeo/stable:v1.22.0 `
    copy `
    --all `
    --dest-creds '<robot>:<pat>' `
    --dest-tls-verify=false `
    --preserve-digests `
    docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 `
    docker://harbor:5000/<project>/testcontainers/ryuk:0.14.0

@HofmeisterAn HofmeisterAn merged commit 598f961 into develop Mar 20, 2026
211 of 227 checks passed
@HofmeisterAn HofmeisterAn deleted the feature/update-pinned-images-doc branch March 20, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Docs, docs, docs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant