Skip to content

Conversation

mcuste
Copy link

@mcuste mcuste commented Aug 9, 2025

Summary

  • Relevant feature request: [feature request] Support for Host Port Exposure (host.testcontainers.internal) #821
  • Add with_exposed_host_port() API to enable containers to connect back to host services
  • Implement automatic host mapping detection using native Docker features:
    • host-gateway on Linux (Docker 20.10+)
    • host.docker.internal on Docker Desktop (macOS/Windows)
  • Add comprehensive tests covering various scenarios including parallel connections
    • Unit tests for version detection and mapping logic
    • Integration tests for host service connectivity
    • Parallel connection tests to ensure thread safety
    • All existing tests continue to pass
  • Include example demonstrating the feature usage

Background

This feature, requested on #821, enables containers to communicate with services running on the host machine by exposing host ports through a standardized hostname host.testcontainers.internal. This is particularly useful for integration tests where containers need to call back to test services or mock servers running on the host.

The implementation leverages native Docker features that have been stable for years:

  • host-gateway support was added in Docker 20.10 (released October 2020, ~5 years ago)
  • host.docker.internal has been available on Docker Desktop since early versions

Any Docker installation older than 20.10 would be significantly outdated, so the current implementation focuses on
these native approaches rather than complex fallback mechanisms.

As for the DinD case, I will look into it in the future if I can find the time.

Copy link

netlify bot commented Aug 9, 2025

Deploy Preview for testcontainers-rust ready!

Name Link
🔨 Latest commit 666129b
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-rust/deploys/6897875c1a3a3e0008de25d5
😎 Deploy Preview https://deploy-preview-830--testcontainers-rust.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.

@mcuste
Copy link
Author

mcuste commented Aug 10, 2025

Currently one of the tests fail on CI: cargo test --no-default-features --features http_wait,properties-config

Tried it locally but can't really reproduce, and the changes I made shouldn't effect those tests. The error is IOError { err: Custom { kind: Other, error: "bytes remaining on stream" } }, so probably it failed due to the Docker in CI environment, which will probably pass on retry.

@DDtKey
Copy link
Contributor

DDtKey commented Aug 10, 2025

Yes, it seems to be flakiness.

Thanks for the contribution! 🚀

I will try to take a look soon!
cc @mervyn-mccreight

Copy link
Contributor

@DDtKey DDtKey left a comment

Choose a reason for hiding this comment

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

I already started reviewing piece by piece and then realized conceptual issue: #830 (comment) - the implementation relies only on target_os which isn't enough (It won't work for remote docker, testcontainers cloud and etc)

Tbh I think that's the blocker for the whole implementation we need to address 🤔

See details in #821 and references in comments - that's generally why sshd is used in other languages as a fallback mechanism

❓We could consider adding initial support for non-remote hosts only (with explicit documentation) to split this into several steps, but we have to ensure it can be extended without breaking things (#830 (comment))

Comment on lines +33 to +65
async fn supports_host_gateway(client: &Client) -> bool {
if !cfg!(target_os = "linux") {
log::trace!("Platform: not Linux, host-gateway unsupported");
return false;
}

log::trace!("Platform: Linux, checking Docker version for host-gateway");

// Check Docker version to see if it supports host-gateway
match client.docker_version().await {
Ok(Some(version_str)) => {
let supported = is_docker_version_at_least(&version_str, 20, 10);
if !supported {
log::warn!("Docker version {} detected on Linux - host-gateway may not be supported (requires 20.10+)", version_str);
}
supported
}
Ok(None) => {
// No version info available, assume it's supported but warn
log::warn!(
"Docker version not available - assuming host-gateway is supported on Linux"
);
true
}
Err(err) => {
// If we can't get version info, assume it's supported but warn
log::warn!(
"Failed to detect Docker version ({}), assuming host-gateway is supported on Linux",
err
);
true
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like good candidate to be a method of client as main argument is &Client

But in order to split the logic and decouple from the Client I'd suggest to define some trait and impl it for the Client (e.g impl HostMapping for Client { ...})

Comment on lines +20 to +26
let parts: Vec<&str> = version_str.split('.').collect();
if parts.len() < 2 {
return false;
}

let major: u32 = parts[0].parse().unwrap_or(0);
let minor: u32 = parts[1].parse().unwrap_or(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's rely on https://crates.io/crates/semver for that?

/// Checks if we're running on Docker Desktop (macOS or Windows).
/// Docker Desktop provides `host.docker.internal` by default.
fn is_docker_desktop() -> bool {
let is_desktop = cfg!(any(target_os = "macos", target_os = "windows"));
Copy link
Contributor

@DDtKey DDtKey Aug 10, 2025

Choose a reason for hiding this comment

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

This won’t actually work as is because we can’t rely solely on the operating system of the user for that.

Testcontainers support remote Docker hosts.

@DDtKey
Copy link
Contributor

DDtKey commented Aug 10, 2025

So, I'm generally ok to split work into 2-3 steps:

  • introduce initial support limited to local docker installations (+ have flexible enough interface)
  • support fallback mechanisms, remote docker hosts
  • ensure DinD works

But let's discuss what can be achieved in the first iteration

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants