Summary
When adding two containers to a wrangler.jsonc file and running wrangler dev, only the last container image in the file is created. In the below provided wrangler configuration only the EgressTest1Container image is created. However, the image build logs show both images are created but when we run docker images only one image is shown.
Here is the github repo for reproducible code - https://github.com/RahiReja/egress-tests
{
"name": "egress-tests",
"main": "src/index.ts",
"compatibility_date": "2026-02-06",
"compatibility_flags": ["nodejs_compat"],
"observability": {
"enabled": true
},
"containers": [
{
"image": "./Dockerfile",
"class_name": "EgressTestContainer",
"name": "egress-test-container",
"max_instances": 2
},
{
"image": "./Dockerfile",
"class_name": "EgressTest1Container",
"name": "egress-test1-container",
"max_instances": 2
}
],
"durable_objects": {
"bindings": [
{
"class_name": "EgressTestContainer",
"name": "CONTAINER"
},
{
"class_name": "EgressTest1Container",
"name": "CONTAINER1"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["EgressTestContainer", "EgressTest1Container"]
}
]
}
Expected Behaviour
When we do wrangler dev both container image has to be created. Not only the last one.
Environment
Package: @cloudflare/containers@0.3.4
Wrangler: 4.81.0
Worker compatibility date: 2026-04-18
enableInternet = true
interceptHttps = true
Minimal shape of the setup
import { Container, getContainer, ContainerProxy } from "@cloudflare/containers";
export { ContainerProxy };
export class EgressTestContainer extends Container {
defaultPort = 8080;
sleepAfter = "3m";
enableInternet = false;
interceptHttps = true;
// allowedHosts gates everything: only these hosts can reach outbound/internet.
// 'by-host.com' is included so the outboundByHost handler can run.
allowedHosts = ["allowed.com", "by-host.com", "*.globtest.com"];
deniedHosts = ["denied.com"];
constructor(ctx: any, env: any) {
super(ctx, env);
this.entrypoint = ["node", "server.js"];
this.envVars = {
NODE_EXTRA_CA_CERTS: "/etc/cloudflare/certs/cloudflare-containers-ca.crt"
};
}
}
export class EgressTest1Container extends Container {
defaultPort = 8080;
sleepAfter = "3m";
enableInternet = false;
interceptHttps = true;
// allowedHosts gates everything: only these hosts can reach outbound/internet.
// 'by-host.com' is included so the outboundByHost handler can run.
allowedHosts = ["allowed.com", "by-host.com", "*.globtest.com"];
deniedHosts = ["denied.com"];
constructor(ctx: any, env: any) {
super(ctx, env);
this.entrypoint = ["node", "server.js"];
this.envVars = {
NODE_EXTRA_CA_CERTS: "/etc/cloudflare/certs/cloudflare-containers-ca.crt"
};
}
}
EgressTestContainer.outboundByHost = {
"by-host.com": (_req: Request) => {
const res = new Response("outboundByHost: by-host.com");
// res.headers.append("Set-Cookie", "cookie3=value3; Path=/");
// res.headers.append("Set-Cookie", "cookie4=value4; Path=/");
res.headers.append("Set-Cookie", "cookie3=value3; Path=/");
//console.log(res); // Logs both cookies
return res;
},
"*.globtest.com": (req: Request) => {
return new Response("outboundByHost glob: " + new URL(req.url).hostname);
}
};
EgressTestContainer.outbound = (req: Request) => {
return new Response("catch-all: " + new URL(req.url).hostname);
};
export default {
async fetch(request: Request, env: { CONTAINER: DurableObjectNamespace<EgressTestContainer> }): Promise<Response> {
try {
const url = new URL(request.url);
const id = url.searchParams.get("id") || "singleton";
const container = getContainer(env.CONTAINER, id);
if (url.pathname === "/proxy") {
return await container.containerFetch(request);
}
if (url.pathname === "/proxy_https") {
const res = await container.containerFetch(request);
// return await container.containerFetch(request);
console.log("Handler result:", res); // Logs both cookies if present
// for (const key of res.headers.keys()) {
// console.log(`Header: ${key} = ${res.headers.get(key)}`);
// }
return res;
}
if (url.pathname === "/config/deny-host") {
const hostname = url.searchParams.get("hostname");
if (!hostname) {
return new Response("hostname is required", { status: 400 });
}
await container.denyHost(hostname);
return new Response("OK");
}
if (url.pathname === "/destroy") {
await container.destroy();
return new Response("Container killed");
}
if (url.pathname === "/status") {
const state = await container.getState();
return new Response(JSON.stringify(state, null, 2));
}
return new Response("Not Found");
} catch (e) {
console.error("Worker fetch error:", e);
return new Response(`Worker error: ${e instanceof Error ? e.message : String(e)}`, {
status: 500
});
}
}
};
This is the same code as in the Cloudflare/Containers package example egress-test with some modifications. I have added one more container EgressTest1Container and runs wrangler dev. It only creates the last container image not all container images.
Summary
When adding two containers to a wrangler.jsonc file and running wrangler dev, only the last container image in the file is created. In the below provided wrangler configuration only the EgressTest1Container image is created. However, the image build logs show both images are created but when we run docker images only one image is shown.
Here is the github repo for reproducible code - https://github.com/RahiReja/egress-tests
Expected Behaviour
When we do wrangler dev both container image has to be created. Not only the last one.
Environment
Package: @cloudflare/containers@0.3.4
Wrangler: 4.81.0
Worker compatibility date: 2026-04-18
enableInternet = true
interceptHttps = true
Minimal shape of the setup
This is the same code as in the Cloudflare/Containers package example egress-test with some modifications. I have added one more container EgressTest1Container and runs wrangler dev. It only creates the last container image not all container images.