From 3c11abc68abdd34ea794e62c1d7505e18469435e Mon Sep 17 00:00:00 2001 From: Dvir Arad Date: Mon, 11 May 2026 10:49:47 +0300 Subject: [PATCH 1/2] fix(reaper): read RYUK_CONTAINER_IMAGE lazily so dotenv/runtime overrides work --- packages/testcontainers/src/reaper/reaper.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/testcontainers/src/reaper/reaper.ts b/packages/testcontainers/src/reaper/reaper.ts index 44284f21d..97714e9c8 100644 --- a/packages/testcontainers/src/reaper/reaper.ts +++ b/packages/testcontainers/src/reaper/reaper.ts @@ -7,9 +7,18 @@ import { GenericContainer } from "../generic-container/generic-container"; import { LABEL_TESTCONTAINERS_RYUK, LABEL_TESTCONTAINERS_SESSION_ID } from "../utils/labels"; import { Wait } from "../wait-strategies/wait"; -export const REAPER_IMAGE = process.env["RYUK_CONTAINER_IMAGE"] - ? ImageName.fromString(process.env["RYUK_CONTAINER_IMAGE"]).string - : ImageName.fromString("testcontainers/ryuk:0.14.0").string; +/** + * Resolve the Ryuk reaper image name. Read lazily so that callers (and tests) + * can set `process.env.RYUK_CONTAINER_IMAGE` _after_ this module is imported — + * including via `.env` files loaded by `dotenv` at runtime. + * + * See https://github.com/testcontainers/testcontainers-node/issues/1310. + */ +export function getReaperImage(): string { + return process.env["RYUK_CONTAINER_IMAGE"] + ? ImageName.fromString(process.env["RYUK_CONTAINER_IMAGE"]).string + : ImageName.fromString("testcontainers/ryuk:0.14.0").string; +} export interface Reaper { sessionId: string; @@ -87,7 +96,7 @@ async function useExistingReaper(reaperContainer: ContainerInfo, sessionId: stri async function createNewReaper(sessionId: string, remoteSocketPath: string): Promise { log.debug(`Creating new Reaper for session "${sessionId}" with socket path "${remoteSocketPath}"...`); - const container = new GenericContainer(REAPER_IMAGE) + const container = new GenericContainer(getReaperImage()) .withName(`testcontainers-ryuk-${sessionId}`) .withExposedPorts( process.env["TESTCONTAINERS_RYUK_PORT"] From 4e193cdefe68afe5e03e6900b5058a87dc80d8d0 Mon Sep 17 00:00:00 2001 From: Dvir Arad Date: Mon, 11 May 2026 10:49:47 +0300 Subject: [PATCH 2/2] refactor(generic-container): use getReaperImage() instead of REAPER_IMAGE const --- .../src/generic-container/generic-container.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/testcontainers/src/generic-container/generic-container.ts b/packages/testcontainers/src/generic-container/generic-container.ts index 313818c58..2d5e02bd1 100644 --- a/packages/testcontainers/src/generic-container/generic-container.ts +++ b/packages/testcontainers/src/generic-container/generic-container.ts @@ -8,7 +8,7 @@ import { ContainerRuntimeClient, getContainerRuntimeClient, ImageName } from ".. import { CONTAINER_STATUSES } from "../container-runtime/clients/container/types"; import { StartedNetwork } from "../network/network"; import { PortForwarderInstance, SSHD_IMAGE } from "../port-forwarder/port-forwarder"; -import { getReaper, REAPER_IMAGE } from "../reaper/reaper"; +import { getReaper, getReaperImage } from "../reaper/reaper"; import { StartedTestContainer, TestContainer } from "../test-container"; import { ArchiveToCopy, @@ -70,7 +70,7 @@ export class GenericContainer implements TestContainer { constructor(image: string) { this.imageName = ImageName.fromString(image); this.createOpts = { Image: this.imageName.string }; - this.hostConfig = { AutoRemove: this.imageName.string === REAPER_IMAGE }; + this.hostConfig = { AutoRemove: this.imageName.string === getReaperImage() }; } private isHelperContainer() { @@ -78,7 +78,7 @@ export class GenericContainer implements TestContainer { } private isReaper() { - return this.imageName.string === REAPER_IMAGE; + return this.imageName.string === getReaperImage(); } protected beforeContainerCreated?(): Promise;