diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 301c59f0..9f613c4a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,4 +24,4 @@ jobs:
       - name: Build
         run: dotnet build -c Release --framework ${{ matrix.framework }}
       - name: Test
-        run: dotnet test -c Release --framework ${{ matrix.framework }} --no-build
+        run: dotnet test -c Release --framework ${{ matrix.framework }} --no-build --logger console
diff --git a/src/Docker.DotNet.BasicAuth/BasicAuthCredentials.cs b/src/Docker.DotNet.BasicAuth/BasicAuthCredentials.cs
index f1ba77f4..b51bfc85 100644
--- a/src/Docker.DotNet.BasicAuth/BasicAuthCredentials.cs
+++ b/src/Docker.DotNet.BasicAuth/BasicAuthCredentials.cs
@@ -5,12 +5,10 @@ public class BasicAuthCredentials : Credentials
     private readonly bool _isTls;
 
     private readonly MaybeSecureString _username;
+
     private readonly MaybeSecureString _password;
 
-    public override HttpMessageHandler GetHandler(HttpMessageHandler innerHandler)
-    {
-        return new BasicAuthHandler(_username, _password, innerHandler);
-    }
+    private bool _disposed;
 
     public BasicAuthCredentials(SecureString username, SecureString password, bool isTls = false)
         : this(new MaybeSecureString(username), new MaybeSecureString(password), isTls)
@@ -29,14 +27,35 @@ private BasicAuthCredentials(MaybeSecureString username, MaybeSecureString passw
         _password = password;
     }
 
+    public override void Dispose()
+    {
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }
+
     public override bool IsTlsCredentials()
     {
         return _isTls;
     }
 
-    public override void Dispose()
+    public override HttpMessageHandler GetHandler(HttpMessageHandler handler)
+    {
+        return new BasicAuthHandler(_username, _password, handler);
+    }
+
+    protected virtual void Dispose(bool disposing)
     {
-        _username.Dispose();
-        _password.Dispose();
+        if (_disposed)
+        {
+            return;
+        }
+
+        if (disposing)
+        {
+            _username.Dispose();
+            _password.Dispose();
+        }
+
+        _disposed = true;
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet.X509/CertificateCredentials.cs b/src/Docker.DotNet.X509/CertificateCredentials.cs
index e7e56ce0..3404616b 100644
--- a/src/Docker.DotNet.X509/CertificateCredentials.cs
+++ b/src/Docker.DotNet.X509/CertificateCredentials.cs
@@ -4,37 +4,47 @@ public class CertificateCredentials : Credentials
 {
     private readonly X509Certificate2 _certificate;
 
-    public CertificateCredentials(X509Certificate2 clientCertificate)
+    private bool _disposed;
+
+    public CertificateCredentials(X509Certificate2 certificate)
     {
-        _certificate = clientCertificate;
+        _certificate = certificate;
     }
 
-    public RemoteCertificateValidationCallback ServerCertificateValidationCallback { get; set; }
-
-    public override HttpMessageHandler GetHandler(HttpMessageHandler innerHandler)
+    public override void Dispose()
     {
-        var handler = (ManagedHandler)innerHandler;
-        handler.ClientCertificates = new X509CertificateCollection
-        {
-            _certificate
-        };
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }
 
-        handler.ServerCertificateValidationCallback = ServerCertificateValidationCallback;
+    public override bool IsTlsCredentials()
+    {
+        return true;
+    }
 
-        if (handler.ServerCertificateValidationCallback == null)
+    public override HttpMessageHandler GetHandler(HttpMessageHandler handler)
+    {
+        if (handler is HttpClientHandler httpClientHandler)
         {
-            handler.ServerCertificateValidationCallback = ServicePointManager.ServerCertificateValidationCallback;
+            httpClientHandler.ClientCertificates.Add(_certificate);
+            httpClientHandler.ServerCertificateCustomValidationCallback = (_, _, _, _) => true;
         }
 
         return handler;
     }
 
-    public override bool IsTlsCredentials()
+    protected virtual void Dispose(bool disposing)
     {
-        return true;
-    }
+        if (_disposed)
+        {
+            return;
+        }
 
-    public override void Dispose()
-    {
+        if (disposing)
+        {
+            _certificate.Dispose();
+        }
+
+        _disposed = true;
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet.X509/Docker.DotNet.X509.csproj b/src/Docker.DotNet.X509/Docker.DotNet.X509.csproj
index 017ee021..8c2dc7c0 100644
--- a/src/Docker.DotNet.X509/Docker.DotNet.X509.csproj
+++ b/src/Docker.DotNet.X509/Docker.DotNet.X509.csproj
@@ -4,6 +4,9 @@
     <PackageId>Docker.DotNet.Enhanced.X509</PackageId>
     <Description>Docker.DotNet.X509 is a library that allows you to use certificate authentication with a remote Docker engine programmatically in your .NET applications.</Description>
   </PropertyGroup>
+  <ItemGroup Condition="$(TargetFrameworkIdentifier) == '.NETStandard'">
+    <PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
+  </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Docker.DotNet\Docker.DotNet.csproj" />
   </ItemGroup>
diff --git a/src/Docker.DotNet.X509/RSAUtil.cs b/src/Docker.DotNet.X509/RSAUtil.cs
index 274e3ef7..9f9ceda9 100644
--- a/src/Docker.DotNet.X509/RSAUtil.cs
+++ b/src/Docker.DotNet.X509/RSAUtil.cs
@@ -2,179 +2,25 @@ namespace Docker.DotNet.X509;
 
 public static class RSAUtil
 {
-    private const byte Padding = 0x00;
-
     public static X509Certificate2 GetCertFromPFX(string pfxFilePath, string password)
     {
+#if NET9_0_OR_GREATER
+        return X509CertificateLoader.LoadPkcs12FromFile(pfxFilePath, password);
+#else
         return new X509Certificate2(pfxFilePath, password);
+#endif
     }
 
-    public static X509Certificate2 GetCertFromPFXSecure(string pfxFilePath, SecureString password)
-    {
-        return new X509Certificate2(pfxFilePath, password);
-    }
-
-    public static X509Certificate2 GetCertFromPEMFiles(string certFilePath, string keyFilePath)
-    {
-        var cert = new X509Certificate2(certFilePath);
-        cert.PrivateKey = ReadFromPemFile(keyFilePath);
-        return cert;
-    }
-
-    private static RSACryptoServiceProvider ReadFromPemFile(string pemFilePath)
-    {
-        var allBytes = File.ReadAllBytes(pemFilePath);
-        var mem = new MemoryStream(allBytes);
-        var startIndex = 0;
-        var endIndex = 0;
-
-        using (var rdr = new BinaryReader(mem))
-        {
-            if (!TryReadUntil(rdr, "-----BEGIN RSA PRIVATE KEY-----"))
-            {
-                throw new Exception("Invalid file format expected. No begin tag.");
-            }
-
-            startIndex = (int)(mem.Position);
-
-            const string endTag = "-----END RSA PRIVATE KEY-----";
-            if (!TryReadUntil(rdr, endTag))
-            {
-                throw new Exception("Invalid file format expected. No end tag.");
-            }
-
-            endIndex = (int)(mem.Position - endTag.Length - 2);
-        }
-
-        // Convert the bytes from base64;
-        var convertedBytes = Convert.FromBase64String(Encoding.UTF8.GetString(allBytes, startIndex, endIndex - startIndex));
-        mem = new MemoryStream(convertedBytes);
-        using (var rdr = new BinaryReader(mem))
-        {
-            var val = rdr.ReadUInt16();
-            if (val != 0x8230)
-            {
-                throw new Exception("Invalid byte ordering.");
-            }
-
-            // Discard the next bits of the version.
-            rdr.ReadUInt32();
-            if (rdr.ReadByte() != Padding)
-            {
-                throw new InvalidDataException("Invalid ASN.1 format.");
-            }
-
-            var rsa = new RSAParameters()
-            {
-                Modulus = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                Exponent = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                D = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                P = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                Q = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                DP = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                DQ = rdr.ReadBytes(ReadIntegerCount(rdr)),
-                InverseQ = rdr.ReadBytes(ReadIntegerCount(rdr))
-            };
-
-            // Use "1" to indicate RSA.
-            var csp = new CspParameters(1)
-            {
-
-                // Set the KeyContainerName so that native code that looks up the private key
-                // can find it. This produces a keyset file on disk as a side effect.
-                KeyContainerName = pemFilePath
-            };
-            var rsaProvider = new RSACryptoServiceProvider(csp)
-            {
-
-                // Setting to false makes sure the keystore file will be cleaned up
-                // when the current process exits.
-                PersistKeyInCsp = false
-            };
-
-            // Import the private key into the keyset.
-            rsaProvider.ImportParameters(rsa);
-
-            return rsaProvider;
-        }
-    }
-
-    /// <summary>
-    /// Reads an integer count encoding in DER ASN.1 format.
-    /// <summary>
-    private static int ReadIntegerCount(BinaryReader rdr)
+    public static X509Certificate2 GetCertFromPEM(string certFilePath, string keyFilePath)
     {
-        const byte highBitOctet = 0x80;
-        const byte ASN1_INTEGER = 0x02;
-
-        if (rdr.ReadByte() != ASN1_INTEGER)
-        {
-            throw new Exception("Integer tag expected.");
-        }
-
-        int count = 0;
-        var val = rdr.ReadByte();
-        if ((val & highBitOctet) == highBitOctet)
-        {
-            byte numOfOctets = (byte)(val - highBitOctet);
-            if (numOfOctets > 4)
-            {
-                throw new InvalidDataException("Too many octets.");
-            }
-
-            for (var i = 0; i < numOfOctets; i++)
-            {
-                count <<= 8;
-                count += rdr.ReadByte();
-            }
-        }
-        else
-        {
-            count = val;
-        }
-
-        while (rdr.ReadByte() == Padding)
-        {
-            count--;
-        }
-
-        // The last read was a valid byte. Go back here.
-        rdr.BaseStream.Seek(-1, SeekOrigin.Current);
-
-        return count;
-    }
-
-    /// <summary>
-    /// Reads until the matching PEM tag is found.
-    /// <summary>
-    private static bool TryReadUntil(BinaryReader rdr, string tag)
-    {
-        char delim = '\n';
-        char c;
-        char[] line = new char[64];
-        int index;
-
-        try
-        {
-            do
-            {
-                index = 0;
-                while ((c = rdr.ReadChar()) != delim)
-                {
-                    if(c == '\r')
-                    {
-                        continue;
-                    }
-                    line[index] = c;
-                    index++;
-                }
-            } while (new string(line, 0, index) != tag);
-
-            return true;
-        }
-        catch (EndOfStreamException)
-        {
-            return false;
-        }
+#if NETSTANDARD
+        return Polyfills.X509Certificate2.CreateFromPemFile(certFilePath, keyFilePath);
+#elif NET9_0_OR_GREATER
+        var certificate = X509Certificate2.CreateFromPemFile(certFilePath, keyFilePath);
+        return OperatingSystem.IsWindows() ? X509CertificateLoader.LoadPkcs12(certificate.Export(X509ContentType.Pfx), null) : certificate;
+#elif NET6_0_OR_GREATER
+        var certificate = X509Certificate2.CreateFromPemFile(certFilePath, keyFilePath);
+        return OperatingSystem.IsWindows() ? new X509Certificate2(certificate.Export(X509ContentType.Pfx)) : certificate;
+#endif
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet.X509/X509Certificate2.cs b/src/Docker.DotNet.X509/X509Certificate2.cs
new file mode 100644
index 00000000..f3a9ff91
--- /dev/null
+++ b/src/Docker.DotNet.X509/X509Certificate2.cs
@@ -0,0 +1,64 @@
+#if NETSTANDARD
+namespace Docker.DotNet.X509.Polyfills;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.OpenSsl;
+using Org.BouncyCastle.Pkcs;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.X509;
+
+public static class X509Certificate2
+{
+    private static readonly X509CertificateParser CertificateParser = new X509CertificateParser();
+
+    public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath)
+    {
+        if (!File.Exists(certPemFilePath))
+        {
+            throw new FileNotFoundException(certPemFilePath);
+        }
+
+        if (!File.Exists(keyPemFilePath))
+        {
+            throw new FileNotFoundException(keyPemFilePath);
+        }
+
+        using var keyPairStream = new StreamReader(keyPemFilePath);
+
+        using var certificateStream = new MemoryStream();
+
+        var store = new Pkcs12StoreBuilder().Build();
+
+        var certificate = CertificateParser.ReadCertificate(File.ReadAllBytes(certPemFilePath));
+
+        var password = Guid.NewGuid().ToString("D");
+
+        var keyObject = new PemReader(keyPairStream).ReadObject();
+
+        var certificateEntry = new X509CertificateEntry(certificate);
+
+        var keyParameter = ResolveKeyParameter(keyObject);
+
+        var keyEntry = new AsymmetricKeyEntry(keyParameter);
+
+        store.SetKeyEntry(certificate.SubjectDN + "_key", keyEntry, new[] { certificateEntry });
+        store.Save(certificateStream, password.ToCharArray(), new SecureRandom());
+
+        return new System.Security.Cryptography.X509Certificates.X509Certificate2(Pkcs12Utilities.ConvertToDefiniteLength(certificateStream.ToArray()), password);
+    }
+
+    private static AsymmetricKeyParameter ResolveKeyParameter(object keyObject)
+    {
+        switch (keyObject)
+        {
+            case AsymmetricCipherKeyPair ackp:
+                return ackp.Private;
+            case RsaPrivateCrtKeyParameters rpckp:
+                return rpckp;
+            default:
+                throw new ArgumentOutOfRangeException(nameof(keyObject), $"Unsupported asymmetric key entry encountered while trying to resolve key from input object '{keyObject.GetType()}'.");
+        }
+    }
+}
+#endif
\ No newline at end of file
diff --git a/src/Docker.DotNet/AnonymousCredentials.cs b/src/Docker.DotNet/AnonymousCredentials.cs
index 6844c5f4..0375ca01 100644
--- a/src/Docker.DotNet/AnonymousCredentials.cs
+++ b/src/Docker.DotNet/AnonymousCredentials.cs
@@ -2,17 +2,17 @@ namespace Docker.DotNet;
 
 public class AnonymousCredentials : Credentials
 {
-    public override bool IsTlsCredentials()
+    public override void Dispose()
     {
-        return false;
     }
 
-    public override void Dispose()
+    public override bool IsTlsCredentials()
     {
+        return false;
     }
 
-    public override HttpMessageHandler GetHandler(HttpMessageHandler innerHandler)
+    public override HttpMessageHandler GetHandler(HttpMessageHandler handler)
     {
-        return innerHandler;
+        return handler;
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/Credentials.cs b/src/Docker.DotNet/Credentials.cs
index d971a21b..a20428a6 100644
--- a/src/Docker.DotNet/Credentials.cs
+++ b/src/Docker.DotNet/Credentials.cs
@@ -2,11 +2,9 @@ namespace Docker.DotNet;
 
 public abstract class Credentials : IDisposable
 {
-    public abstract bool IsTlsCredentials();
+    public abstract void Dispose();
 
-    public abstract HttpMessageHandler GetHandler(HttpMessageHandler innerHandler);
+    public abstract bool IsTlsCredentials();
 
-    public virtual void Dispose()
-    {
-    }
+    public abstract HttpMessageHandler GetHandler(HttpMessageHandler handler);
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/Docker.DotNet.csproj b/src/Docker.DotNet/Docker.DotNet.csproj
index ea1aa6ec..e58096c1 100644
--- a/src/Docker.DotNet/Docker.DotNet.csproj
+++ b/src/Docker.DotNet/Docker.DotNet.csproj
@@ -4,6 +4,9 @@
     <PackageId>Docker.DotNet.Enhanced</PackageId>
     <Description>Docker.DotNet is a library that allows you to interact with the Docker Remote API programmatically with fully asynchronous, non-blocking and object-oriented code in your .NET applications.</Description>
   </PropertyGroup>
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
+  </ItemGroup>
   <ItemGroup Condition="$(TargetFramework) == 'net8.0'">
     <PackageReference Include="System.IO.Pipelines" Version="8.0.0" />
   </ItemGroup>
@@ -44,6 +47,8 @@
     <Using Include="System.Threading.Tasks" />
     <Using Include="Docker.DotNet" />
     <Using Include="Docker.DotNet.Models" />
+    <Using Include="Microsoft.Extensions.Logging" />
+    <Using Include="Microsoft.Extensions.Logging.Abstractions" />
     <Using Include="Microsoft.Net.Http.Client" />
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/src/Docker.DotNet/DockerClient.cs b/src/Docker.DotNet/DockerClient.cs
index 821b1369..f0e67b6c 100644
--- a/src/Docker.DotNet/DockerClient.cs
+++ b/src/Docker.DotNet/DockerClient.cs
@@ -17,9 +17,12 @@ public sealed class DockerClient : IDockerClient
 
     private readonly Version _requestedApiVersion;
 
-    internal DockerClient(DockerClientConfiguration configuration, Version requestedApiVersion)
+    private readonly ILogger _logger;
+
+    internal DockerClient(DockerClientConfiguration configuration, Version requestedApiVersion, ILogger logger = null)
     {
         _requestedApiVersion = requestedApiVersion;
+        _logger = logger ?? NullLogger.Instance;
 
         Configuration = configuration;
         DefaultTimeout = configuration.DefaultTimeout;
@@ -72,7 +75,7 @@ await stream.ConnectAsync(timeout, cancellationToken)
                         .ConfigureAwait(false);
 
                     return dockerStream;
-                });
+                }, _logger);
                 break;
 
             case "tcp":
@@ -82,11 +85,11 @@ await stream.ConnectAsync(timeout, cancellationToken)
                     Scheme = configuration.Credentials.IsTlsCredentials() ? "https" : "http"
                 };
                 uri = builder.Uri;
-                handler = new ManagedHandler();
+                handler = new ManagedHandler(_logger);
                 break;
 
             case "https":
-                handler = new ManagedHandler();
+                handler = new ManagedHandler(_logger);
                 break;
 
             case "unix":
@@ -99,7 +102,7 @@ await sock.ConnectAsync(new Microsoft.Net.Http.Client.UnixDomainSocketEndPoint(p
                         .ConfigureAwait(false);
 
                     return sock;
-                });
+                }, _logger);
                 uri = new UriBuilder("http", uri.Segments.Last()).Uri;
                 break;
 
@@ -388,10 +391,7 @@ await HandleIfErrorResponseAsync(response.StatusCode, response, errorHandlers)
             throw new NotSupportedException("message handler does not support hijacked streams");
         }
 
-        var stream = await content.ReadAsStreamAsync()
-            .ConfigureAwait(false);
-
-        return (WriteClosableStream)stream;
+        return content.HijackStream();
     }
 
     private async Task<HttpResponseMessage> PrivateMakeRequestAsync(
diff --git a/src/Docker.DotNet/DockerClientConfiguration.cs b/src/Docker.DotNet/DockerClientConfiguration.cs
index d92e4ef7..046359a0 100644
--- a/src/Docker.DotNet/DockerClientConfiguration.cs
+++ b/src/Docker.DotNet/DockerClientConfiguration.cs
@@ -50,9 +50,9 @@ public DockerClientConfiguration(
 
     public TimeSpan NamedPipeConnectTimeout { get; }
 
-    public DockerClient CreateClient(Version requestedApiVersion = null)
+    public DockerClient CreateClient(Version requestedApiVersion = null, ILogger logger = null)
     {
-        return new DockerClient(this, requestedApiVersion);
+        return new DockerClient(this, requestedApiVersion, logger);
     }
 
     public void Dispose()
diff --git a/src/Docker.DotNet/Endpoints/ContainerOperations.cs b/src/Docker.DotNet/Endpoints/ContainerOperations.cs
index 9fad6cad..226f2c76 100644
--- a/src/Docker.DotNet/Endpoints/ContainerOperations.cs
+++ b/src/Docker.DotNet/Endpoints/ContainerOperations.cs
@@ -297,14 +297,8 @@ public Task RenameContainerAsync(string id, ContainerRenameParameters parameters
         }
 
         var queryParameters = new QueryString<ContainerAttachParameters>(parameters);
-        var stream = await _client.MakeRequestForHijackedStreamAsync(new[] { NoSuchContainerHandler }, HttpMethod.Post, $"containers/{id}/attach", queryParameters, null, null, cancellationToken).ConfigureAwait(false);
-        if (!stream.CanCloseWrite)
-        {
-            stream.Dispose();
-            throw new NotSupportedException("Cannot shutdown write on this transport");
-        }
-
-        return new MultiplexedStream(stream, !tty);
+        var result = await _client.MakeRequestForStreamAsync(new[] { NoSuchContainerHandler }, HttpMethod.Post, $"containers/{id}/attach", queryParameters, null, null, cancellationToken).ConfigureAwait(false);
+        return new MultiplexedStream(result, !tty);
     }
 
     public async Task<ContainerWaitResponse> WaitContainerAsync(string id, CancellationToken cancellationToken = default(CancellationToken))
diff --git a/src/Docker.DotNet/Endpoints/ExecOperations.cs b/src/Docker.DotNet/Endpoints/ExecOperations.cs
index 8b6e87b6..884be06b 100644
--- a/src/Docker.DotNet/Endpoints/ExecOperations.cs
+++ b/src/Docker.DotNet/Endpoints/ExecOperations.cs
@@ -83,21 +83,15 @@ public async Task<MultiplexedStream> StartAndAttachContainerExecAsync(string id,
         return await StartWithConfigContainerExecAsync(id, new ContainerExecStartParameters() { AttachStdin = true, AttachStderr = true, AttachStdout = true, Tty = tty }, cancellationToken);
     }
 
-    public async Task<MultiplexedStream> StartWithConfigContainerExecAsync(string id, ContainerExecStartParameters eConfig, CancellationToken cancellationToken)
+    public async Task<MultiplexedStream> StartWithConfigContainerExecAsync(string id, ContainerExecStartParameters parameters, CancellationToken cancellationToken)
     {
         if (string.IsNullOrEmpty(id))
         {
             throw new ArgumentNullException(nameof(id));
         }
 
-        var data = new JsonRequestContent<ContainerExecStartParameters>(eConfig, DockerClient.JsonSerializer);
-        var stream = await _client.MakeRequestForHijackedStreamAsync(new[] { NoSuchContainerHandler }, HttpMethod.Post, $"exec/{id}/start", null, data, null, cancellationToken).ConfigureAwait(false);
-        if (!stream.CanCloseWrite)
-        {
-            stream.Dispose();
-            throw new NotSupportedException("Cannot shutdown write on this transport");
-        }
-
-        return new MultiplexedStream(stream, !eConfig.Tty);
+        var data = new JsonRequestContent<ContainerExecStartParameters>(parameters, DockerClient.JsonSerializer);
+        var result = await _client.MakeRequestForStreamAsync(new[] { NoSuchContainerHandler }, HttpMethod.Post, $"exec/{id}/start", null, data, null, cancellationToken).ConfigureAwait(false);
+        return new MultiplexedStream(result, !parameters.Tty);
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/JsonNullableDateTimeConverter.cs b/src/Docker.DotNet/JsonNullableDateTimeConverter.cs
index 52b1fffe..fa92a113 100644
--- a/src/Docker.DotNet/JsonNullableDateTimeConverter.cs
+++ b/src/Docker.DotNet/JsonNullableDateTimeConverter.cs
@@ -1,4 +1,4 @@
-namespace Docker.DotNet;
+namespace Docker.DotNet;
 
 internal sealed class JsonNullableDateTimeConverter : JsonConverter<DateTime?>
 {
diff --git a/src/Docker.DotNet/Microsoft.Net.Http.Client/BufferedReadStream.cs b/src/Docker.DotNet/Microsoft.Net.Http.Client/BufferedReadStream.cs
index 94ba4246..2a216647 100644
--- a/src/Docker.DotNet/Microsoft.Net.Http.Client/BufferedReadStream.cs
+++ b/src/Docker.DotNet/Microsoft.Net.Http.Client/BufferedReadStream.cs
@@ -1,39 +1,27 @@
-#if !NET45
-#endif
-
 namespace Microsoft.Net.Http.Client;
 
-internal class BufferedReadStream : WriteClosableStream, IPeekableStream
+internal sealed class BufferedReadStream : WriteClosableStream, IPeekableStream
 {
-    private const char CR = '\r';
-    private const char LF = '\n';
-
     private readonly Stream _inner;
     private readonly Socket _socket;
     private readonly byte[] _buffer;
-    private volatile int _bufferRefCount;
-    private int _bufferOffset = 0;
-    private int _bufferCount = 0;
-    private bool _disposed;
+    private readonly ILogger _logger;
+    private int _bufferRefCount;
+    private int _bufferOffset;
+    private int _bufferCount;
 
-    public BufferedReadStream(Stream inner, Socket socket)
-        : this(inner, socket, 1024)
-    { }
+    public BufferedReadStream(Stream inner, Socket socket, ILogger logger)
+        : this(inner, socket, 8192, logger)
+    {
+    }
 
-    public BufferedReadStream(Stream inner, Socket socket, int bufferLength)
+    public BufferedReadStream(Stream inner, Socket socket, int bufferLength, ILogger logger)
     {
-        if (inner == null)
-        {
-            throw new ArgumentNullException(nameof(inner));
-        }
-        _inner = inner;
+        _inner = inner ?? throw new ArgumentNullException(nameof(inner));
         _socket = socket;
-#if !NET45
-        _bufferRefCount = 1;
         _buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
-#else
-            _buffer = new byte[bufferLength];
-#endif
+        _logger = logger;
+        _bufferRefCount = 1;
     }
 
     public override bool CanRead
@@ -67,32 +55,32 @@ public override long Position
         set { throw new NotSupportedException(); }
     }
 
-    public override long Seek(long offset, SeekOrigin origin)
+    public override bool CanCloseWrite => _socket != null || _inner is WriteClosableStream;
+
+    protected override void Dispose(bool disposing)
     {
-        throw new NotSupportedException();
+        // TODO: Why does disposing break the implementation, see the other chunked streams too.
+        // base.Dispose(disposing);
+
+        if (disposing)
+        {
+            _inner.Dispose();
+
+            if (Interlocked.Decrement(ref _bufferRefCount) == 0)
+            {
+                ArrayPool<byte>.Shared.Return(_buffer);
+            }
+        }
     }
 
-    public override void SetLength(long value)
+    public override long Seek(long offset, SeekOrigin origin)
     {
         throw new NotSupportedException();
     }
 
-    protected override void Dispose(bool disposing)
+    public override void SetLength(long value)
     {
-        if (!_disposed)
-        {
-            _disposed = true;
-            if (disposing)
-            {
-                _inner.Dispose();
-#if !NET45
-                if (Interlocked.Decrement(ref _bufferRefCount) == 0)
-                {
-                    ArrayPool<byte>.Shared.Return(_buffer);
-                }
-#endif
-            }
-        }
+        throw new NotSupportedException();
     }
 
     public override void Flush()
@@ -137,6 +125,23 @@ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, Cancel
         return _inner.ReadAsync(buffer, offset, count, cancellationToken);
     }
 
+    public override void CloseWrite()
+    {
+        if (_socket != null)
+        {
+            _socket.Shutdown(SocketShutdown.Send);
+            return;
+        }
+
+        if (_inner is WriteClosableStream writeClosableStream)
+        {
+            writeClosableStream.CloseWrite();
+            return;
+        }
+
+        throw new NotSupportedException("Cannot shutdown write on this transport");
+    }
+
     public bool Peek(byte[] buffer, uint toPeek, out uint peeked, out uint available, out uint remaining)
     {
         int read = PeekBuffer(buffer, toPeek, out peeked, out available, out remaining);
@@ -153,132 +158,124 @@ public bool Peek(byte[] buffer, uint toPeek, out uint peeked, out uint available
         throw new NotSupportedException("_inner stream isn't a peekable stream");
     }
 
-    private int ReadBuffer(byte[] buffer, int offset, int count)
+    public async Task<string> ReadLineAsync(CancellationToken cancellationToken)
     {
-        if (_bufferCount > 0)
-        {
-            int toCopy = Math.Min(_bufferCount, count);
-            Buffer.BlockCopy(_buffer, _bufferOffset, buffer, offset, toCopy);
-            _bufferOffset += toCopy;
-            _bufferCount -= toCopy;
-            return toCopy;
-        }
+        const char nullChar = '\0';
 
-        return 0;
-    }
+        const char cr = '\r';
 
-    private int PeekBuffer(byte[] buffer, uint toPeek, out uint peeked, out uint available, out uint remaining)
-    {
-        if (_bufferCount > 0)
-        {
-            int toCopy = Math.Min(_bufferCount, (int)toPeek);
-            Buffer.BlockCopy(_buffer, _bufferOffset, buffer, 0, toCopy);
-            peeked = (uint) toCopy;
-            available = (uint)_bufferCount;
-            remaining = available - peeked;
-            return toCopy;
-        }
+        const char lf = '\n';
 
-        peeked = 0;
-        available = 0;
-        remaining = 0;
-        return 0;
-    }
-
-    private async Task EnsureBufferedAsync(CancellationToken cancel)
-    {
         if (_bufferCount == 0)
         {
-            _bufferOffset = 0;
-#if !NET45
-            bool validBuffer = Interlocked.Increment(ref _bufferRefCount) > 1;
+            var bufferInUse = Interlocked.Increment(ref _bufferRefCount) > 1;
+
             try
             {
-                if (validBuffer)
+                if (bufferInUse)
                 {
-                    _bufferCount = await _inner.ReadAsync(_buffer, _bufferOffset, _buffer.Length, cancel).ConfigureAwait(false);
+                    _bufferOffset = 0;
+
+                    _bufferCount = await _inner.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken)
+                        .ConfigureAwait(false);
                 }
             }
+            catch (Exception e)
+            {
+                _logger.LogCritical(e, "Failed to read from buffer.");
+                throw;
+            }
             finally
             {
-                if ((Interlocked.Decrement(ref _bufferRefCount) == 0) && validBuffer)
+                var bufferReleased = Interlocked.Decrement(ref _bufferRefCount) == 0;
+
+                if (bufferInUse && bufferReleased)
                 {
                     ArrayPool<byte>.Shared.Return(_buffer);
                 }
             }
-#else
-                _bufferCount = await _inner.ReadAsync(_buffer, _bufferOffset, _buffer.Length, cancel).ConfigureAwait(false);
-#endif
-            if (_bufferCount == 0)
-            {
-                throw new IOException("Unexpected end of stream");
-            }
         }
-    }
 
-    // TODO: Line length limits?
-    public async Task<string> ReadLineAsync(CancellationToken cancel)
-    {
-        ThrowIfDisposed();
-        StringBuilder builder = new StringBuilder();
-        bool foundCR = false, foundCRLF = false;
-        do
+        if (_logger.IsEnabled(LogLevel.Debug))
         {
-            if (_bufferCount == 0)
-            {
-                await EnsureBufferedAsync(cancel).ConfigureAwait(false);
-            }
+            var content = Encoding.ASCII.GetString(_buffer.TakeWhile(value => value != nullChar).ToArray());
+            content = content.Replace("\r", "<CR>");
+            content = content.Replace("\n", "<LF>");
+            _logger.LogDebug("Raw buffer content: '{Content}'.", content);
+        }
+
+        var start = _bufferOffset;
 
-            char ch = (char)_buffer[_bufferOffset]; // TODO: Encoding enforcement
-            builder.Append(ch);
-            _bufferOffset++;
-            _bufferCount--;
-            if (ch == CR)
+        var end = -1;
+
+        for (var i = _bufferOffset; i < _buffer.Length; i++)
+        {
+            // If a null terminator is found, skip the rest of the buffer.
+            if (_buffer[i] == nullChar)
             {
-                foundCR = true;
+                _logger.LogDebug("Null terminator found at position: {Position}.", i);
+                end = i;
+                break;
             }
-            else if (ch == LF)
+
+            // Check if current byte is CR and the next byte is LF.
+            if (_buffer[i] == cr && i + 1 < _buffer.Length && _buffer[i + 1] == lf)
             {
-                if (foundCR)
-                {
-                    foundCRLF = true;
-                }
-                else
-                {
-                    foundCR = false;
-                }
+                _logger.LogDebug("CRLF found at positions {CR} and {LF}.", i, i + 1);
+                end = i;
+                break;
             }
         }
-        while (!foundCRLF);
-
-        return builder.ToString(0, builder.Length - 2); // Drop the CRLF
-    }
 
-    private void ThrowIfDisposed()
-    {
-        if (_disposed)
+        // No CRLF found, process the entire remaining buffer.
+        if (end == -1)
         {
-            throw new ObjectDisposedException(nameof(BufferedReadStream));
+            end = _buffer.Length;
+            _logger.LogDebug("No CRLF found. Setting end position to buffer length: {End}.", end);
+        }
+        else
+        {
+            _bufferCount -= end - start + 2;
+            _bufferOffset = end + 2;
+            _logger.LogDebug("CRLF found. Consumed {Consumed} bytes. New offset: {Offset}, Remaining count: {RemainingBytes}.", end - start + 2, _bufferOffset, _bufferCount);
         }
-    }
 
-    public override bool CanCloseWrite => _socket != null || _inner is WriteClosableStream;
+        var length = end - start;
+        var line = Encoding.ASCII.GetString(_buffer, start, length);
 
-    public override void CloseWrite()
+        _logger.LogDebug("String from positions {Start} to {End} (length {Length}): '{Line}'.", start, end, length, line);
+        return line;
+    }
+
+    private int ReadBuffer(byte[] buffer, int offset, int count)
     {
-        if (_socket != null)
+        if (_bufferCount > 0)
         {
-            _socket.Shutdown(SocketShutdown.Send);
-            return;
+            int toCopy = Math.Min(_bufferCount, count);
+            Buffer.BlockCopy(_buffer, _bufferOffset, buffer, offset, toCopy);
+            _bufferOffset += toCopy;
+            _bufferCount -= toCopy;
+            return toCopy;
         }
 
-        var s = _inner as WriteClosableStream;
-        if (s != null)
+        return 0;
+    }
+
+    private int PeekBuffer(byte[] buffer, uint toPeek, out uint peeked, out uint available, out uint remaining)
+    {
+        if (_bufferCount > 0)
         {
-            s.CloseWrite();
-            return;
+            int toCopy = Math.Min(_bufferCount, (int)toPeek);
+            Buffer.BlockCopy(_buffer, _bufferOffset, buffer, 0, toCopy);
+            peeked = (uint) toCopy;
+            available = (uint)_bufferCount;
+            remaining = available - peeked;
+            return toCopy;
         }
 
-        throw new NotSupportedException("Cannot shutdown write on this transport");
+        peeked = 0;
+        available = 0;
+        remaining = 0;
+        return 0;
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedReadStream.cs b/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedReadStream.cs
index 99496512..3bcdc582 100644
--- a/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedReadStream.cs
+++ b/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedReadStream.cs
@@ -1,20 +1,19 @@
 namespace Microsoft.Net.Http.Client;
 
-internal class ChunkedReadStream : WriteClosableStream
+internal sealed class ChunkedReadStream : Stream
 {
     private readonly BufferedReadStream _inner;
-    private long _chunkBytesRemaining;
-    private bool _disposed;
+    private int _chunkBytesRemaining;
     private bool _done;
 
-    public ChunkedReadStream(BufferedReadStream inner)
+    public ChunkedReadStream(BufferedReadStream stream)
     {
-        _inner = inner;
+        _inner = stream ?? throw new ArgumentNullException(nameof(stream));
     }
 
     public override bool CanRead
     {
-        get { return !_disposed; }
+        get { return _inner.CanRead; }
     }
 
     public override bool CanSeek
@@ -32,11 +31,6 @@ public override bool CanWrite
         get { return false; }
     }
 
-    public override bool CanCloseWrite
-    {
-        get { return _inner.CanCloseWrite; }
-    }
-
     public override long Length
     {
         get { throw new NotSupportedException(); }
@@ -52,12 +46,10 @@ public override int ReadTimeout
     {
         get
         {
-            ThrowIfDisposed();
             return _inner.ReadTimeout;
         }
         set
         {
-            ThrowIfDisposed();
             _inner.ReadTimeout = value;
         }
     }
@@ -66,89 +58,78 @@ public override int WriteTimeout
     {
         get
         {
-            ThrowIfDisposed();
             return _inner.WriteTimeout;
         }
         set
         {
-            ThrowIfDisposed();
             _inner.WriteTimeout = value;
         }
     }
 
-    public override int Read(byte[] buffer, int offset, int count)
+    protected override void Dispose(bool disposing)
     {
-        return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
+        // base.Dispose(disposing);
+
+        if (disposing)
+        {
+            // _inner.Dispose();
+        }
     }
 
-    public async override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+    public override int Read(byte[] buffer, int offset, int count)
     {
-        // TODO: Validate buffer
-        ThrowIfDisposed();
+        throw new NotSupportedException();
+    }
 
+    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+    {
         if (_done)
         {
             return 0;
         }
 
-        cancellationToken.ThrowIfCancellationRequested();
-
         if (_chunkBytesRemaining == 0)
         {
-            string headerLine = await _inner.ReadLineAsync(cancellationToken).ConfigureAwait(false);
-            if (!long.TryParse(headerLine, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _chunkBytesRemaining))
+            var headerLine = await _inner.ReadLineAsync(cancellationToken)
+                .ConfigureAwait(false);
+
+            if (!int.TryParse(headerLine, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _chunkBytesRemaining))
             {
-                throw new IOException("Invalid chunk header: " + headerLine);
+                throw new IOException($"Invalid chunk header encountered: '{headerLine}'.");
             }
         }
 
-        int read = 0;
+        var readBytesCount = 0;
+
         if (_chunkBytesRemaining > 0)
         {
-            int toRead = (int)Math.Min(count, _chunkBytesRemaining);
-            read = await _inner.ReadAsync(buffer, offset, toRead, cancellationToken).ConfigureAwait(false);
-            if (read == 0)
+            var remainingBytesCount = Math.Min(_chunkBytesRemaining, count);
+
+            readBytesCount = await _inner.ReadAsync(buffer, offset, remainingBytesCount, cancellationToken)
+                .ConfigureAwait(false);
+
+            if (readBytesCount == 0)
             {
                 throw new EndOfStreamException();
             }
 
-            _chunkBytesRemaining -= read;
+            _chunkBytesRemaining -= readBytesCount;
         }
 
         if (_chunkBytesRemaining == 0)
         {
-            // End of chunk, read the terminator CRLF
-            var trailer = await _inner.ReadLineAsync(cancellationToken).ConfigureAwait(false);
-            if (trailer.Length > 0)
-            {
-                throw new IOException("Invalid chunk trailer");
-            }
+            var emptyLine = await _inner.ReadLineAsync(cancellationToken)
+                .ConfigureAwait(false);
 
-            if (read == 0)
+            if (!string.IsNullOrEmpty(emptyLine))
             {
-                _done = true;
+                throw new IOException($"Expected an empty line, but received: '{emptyLine}'.");
             }
-        }
 
-        return read;
-    }
-
-    protected override void Dispose(bool disposing)
-    {
-        if (disposing)
-        {
-            // TODO: Sync drain with timeout if small number of bytes remaining?  This will let us re-use the connection.
-            _inner.Dispose();
+            _done = readBytesCount == 0;
         }
-        _disposed = true;
-    }
 
-    private void ThrowIfDisposed()
-    {
-        if (_disposed)
-        {
-            throw new ObjectDisposedException(typeof(ContentLengthReadStream).FullName);
-        }
+        return readBytesCount;
     }
 
     public override void Write(byte[] buffer, int offset, int count)
@@ -175,9 +156,4 @@ public override void Flush()
     {
         _inner.Flush();
     }
-
-    public override void CloseWrite()
-    {
-        _inner.CloseWrite();
-    }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedWriteStream.cs b/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedWriteStream.cs
index 65e74185..e66174a3 100644
--- a/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedWriteStream.cs
+++ b/src/Docker.DotNet/Microsoft.Net.Http.Client/ChunkedWriteStream.cs
@@ -1,17 +1,14 @@
 namespace Microsoft.Net.Http.Client;
 
-internal class ChunkedWriteStream : Stream
+internal sealed class ChunkedWriteStream : Stream
 {
     private static readonly byte[] s_EndContentBytes = Encoding.ASCII.GetBytes("0\r\n\r\n");
 
-    private Stream _innerStream;
+    private readonly Stream _inner;
 
     public ChunkedWriteStream(Stream stream)
     {
-        if (stream == null)
-            throw new ArgumentNullException(nameof(stream));
-
-        _innerStream = stream;
+        _inner = stream ?? throw new ArgumentNullException(nameof(stream));
     }
 
     public override bool CanRead => false;
@@ -31,14 +28,24 @@ public override long Position
         set { throw new NotImplementedException(); }
     }
 
+    protected override void Dispose(bool disposing)
+    {
+        // base.Dispose(disposing);
+
+        if (disposing)
+        {
+            // _inner.Dispose();
+        }
+    }
+
     public override void Flush()
     {
-        _innerStream.Flush();
+        _inner.Flush();
     }
 
     public override Task FlushAsync(CancellationToken cancellationToken)
     {
-        return _innerStream.FlushAsync(cancellationToken);
+        return _inner.FlushAsync(cancellationToken);
     }
 
     public override int Read(byte[] buffer, int offset, int count)
@@ -58,7 +65,7 @@ public override void SetLength(long value)
 
     public override void Write(byte[] buffer, int offset, int count)
     {
-        WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
+        throw new NotSupportedException();
     }
 
     public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
@@ -69,13 +76,13 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
         }
 
         var chunkSize = Encoding.ASCII.GetBytes(count.ToString("x") + "\r\n");
-        await _innerStream.WriteAsync(chunkSize, 0, chunkSize.Length, cancellationToken).ConfigureAwait(false);
-        await _innerStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
-        await _innerStream.WriteAsync(chunkSize, chunkSize.Length - 2, 2, cancellationToken).ConfigureAwait(false);
+        await _inner.WriteAsync(chunkSize, 0, chunkSize.Length, cancellationToken).ConfigureAwait(false);
+        await _inner.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
+        await _inner.WriteAsync(chunkSize, chunkSize.Length - 2, 2, cancellationToken).ConfigureAwait(false);
     }
 
     public Task EndContentAsync(CancellationToken cancellationToken)
     {
-        return _innerStream.WriteAsync(s_EndContentBytes, 0, s_EndContentBytes.Length, cancellationToken);
+        return _inner.WriteAsync(s_EndContentBytes, 0, s_EndContentBytes.Length, cancellationToken);
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/Microsoft.Net.Http.Client/ContentLengthReadStream.cs b/src/Docker.DotNet/Microsoft.Net.Http.Client/ContentLengthReadStream.cs
index a46a736f..2d38d85c 100644
--- a/src/Docker.DotNet/Microsoft.Net.Http.Client/ContentLengthReadStream.cs
+++ b/src/Docker.DotNet/Microsoft.Net.Http.Client/ContentLengthReadStream.cs
@@ -100,7 +100,7 @@ public override int Read(byte[] buffer, int offset, int count)
         return read;
     }
 
-    public async override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
         // TODO: Validate args
         if (_disposed)
diff --git a/src/Docker.DotNet/Microsoft.Net.Http.Client/HttpConnection.cs b/src/Docker.DotNet/Microsoft.Net.Http.Client/HttpConnection.cs
index 666cc8f2..aba3d8fc 100644
--- a/src/Docker.DotNet/Microsoft.Net.Http.Client/HttpConnection.cs
+++ b/src/Docker.DotNet/Microsoft.Net.Http.Client/HttpConnection.cs
@@ -1,13 +1,15 @@
 namespace Microsoft.Net.Http.Client;
 
-internal class HttpConnection : IDisposable
+internal sealed class HttpConnection : IDisposable
 {
+    // private static readonly ISet<string> DockerStreamHeaders = new HashSet<string>{ "application/vnd.docker.raw-stream", "application/vnd.docker.multiplexed-stream" };
+
     public HttpConnection(BufferedReadStream transport)
     {
         Transport = transport;
     }
 
-    public BufferedReadStream Transport { get; private set; }
+    public BufferedReadStream Transport { get; }
 
     public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
     {
@@ -37,8 +39,8 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, Can
 
             // Receive headers
             List<string> responseLines = await ReadResponseLinesAsync(cancellationToken);
-            // Determine response type (Chunked, Content-Length, opaque, none...)
-            // Receive body
+
+            // Receive body and determine the response type (Content-Length, Transfer-Encoding, Opaque)
             return CreateResponseMessage(responseLines);
         }
         catch (Exception ex)
@@ -83,13 +85,22 @@ private string SerializeRequest(HttpRequestMessage request)
 
     private async Task<List<string>> ReadResponseLinesAsync(CancellationToken cancellationToken)
     {
-        List<string> lines = new List<string>();
-        string line = await Transport.ReadLineAsync(cancellationToken);
-        while (line.Length > 0)
+        var lines = new List<string>(12);
+
+        do
         {
+            var line = await Transport.ReadLineAsync(cancellationToken)
+                .ConfigureAwait(false);
+
+            if (string.IsNullOrEmpty(line))
+            {
+                break;
+            }
+
             lines.Add(line);
-            line = await Transport.ReadLineAsync(cancellationToken);
         }
+        while (true);
+
         return lines;
     }
 
@@ -103,8 +114,8 @@ private HttpResponseMessage CreateResponseMessage(List<string> responseLines)
         {
             throw new HttpRequestException("Invalid response line: " + responseLine);
         }
-        int statusCode = 0;
-        if (int.TryParse(responseLineParts[1], NumberStyles.None, CultureInfo.InvariantCulture, out statusCode))
+
+        if (int.TryParse(responseLineParts[1], NumberStyles.None, CultureInfo.InvariantCulture, out var statusCode))
         {
             // TODO: Validate range
         }
@@ -135,22 +146,16 @@ private HttpResponseMessage CreateResponseMessage(List<string> responseLines)
                 System.Diagnostics.Debug.Assert(success, "Failed to add response header: " + rawHeader);
             }
         }
-        // After headers have been set
-        content.ResolveResponseStream(chunked: response.Headers.TransferEncodingChunked.HasValue && response.Headers.TransferEncodingChunked.Value);
 
+        // var isStream = content.Headers.TryGetValues("Content-Type", out var headerValues)
+        //     && headerValues.Any(header => DockerStreamHeaders.Contains(header));
+
+        content.ResolveResponseStream(chunked: response.Headers.TransferEncodingChunked.HasValue && response.Headers.TransferEncodingChunked.Value);
         return response;
     }
 
     public void Dispose()
     {
-        Dispose(true);
-    }
-
-    protected virtual void Dispose(bool disposing)
-    {
-        if (disposing)
-        {
-            Transport.Dispose();
-        }
+        Transport.Dispose();
     }
 }
\ No newline at end of file
diff --git a/src/Docker.DotNet/Microsoft.Net.Http.Client/ManagedHandler.cs b/src/Docker.DotNet/Microsoft.Net.Http.Client/ManagedHandler.cs
index f9fcef56..a34d0ac7 100644
--- a/src/Docker.DotNet/Microsoft.Net.Http.Client/ManagedHandler.cs
+++ b/src/Docker.DotNet/Microsoft.Net.Http.Client/ManagedHandler.cs
@@ -4,21 +4,27 @@ namespace Microsoft.Net.Http.Client;
 
 public class ManagedHandler : HttpMessageHandler
 {
+    private readonly ILogger _logger;
+
     public delegate Task<Stream> StreamOpener(string host, int port, CancellationToken cancellationToken);
+
     public delegate Task<Socket> SocketOpener(string host, int port, CancellationToken cancellationToken);
 
-    public ManagedHandler()
+    public ManagedHandler(ILogger logger)
     {
+        _logger = logger;
         _socketOpener = TCPSocketOpenerAsync;
     }
 
-    public ManagedHandler(StreamOpener opener)
+    public ManagedHandler(StreamOpener opener, ILogger logger)
     {
+        _logger = logger;
         _streamOpener = opener ?? throw new ArgumentNullException(nameof(opener));
     }
 
-    public ManagedHandler(SocketOpener opener)
+    public ManagedHandler(SocketOpener opener, ILogger logger)
     {
+        _logger = logger;
         _socketOpener = opener ?? throw new ArgumentNullException(nameof(opener));
     }
 
@@ -52,7 +58,7 @@ public IWebProxy Proxy
     private SocketOpener _socketOpener;
     private IWebProxy _proxy;
 
-    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
     {
         if (request == null)
         {
@@ -172,7 +178,7 @@ private async Task<HttpResponseMessage> ProcessRequestAsync(HttpRequestMessage r
             transport = sslStream;
         }
 
-        var bufferedReadStream = new BufferedReadStream(transport, socket);
+        var bufferedReadStream = new BufferedReadStream(transport, socket, _logger);
         var connection = new HttpConnection(bufferedReadStream);
         return await connection.SendAsync(request, cancellationToken);
     }
@@ -308,36 +314,35 @@ private ProxyMode DetermineProxyModeAndAddressLine(HttpRequestMessage request)
 
     private static async Task<Socket> TCPSocketOpenerAsync(string host, int port, CancellationToken cancellationToken)
     {
-        var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
+        var addresses = await Dns.GetHostAddressesAsync(host)
+            .ConfigureAwait(false);
+
         if (addresses.Length == 0)
         {
-            throw new Exception($"could not resolve address for {host}");
+            throw new Exception($"Unable to resolve any IP addresses for the host '{host}'.");
         }
 
-        Socket connectedSocket = null;
-        Exception lastException = null;
+        var exceptions = new List<Exception>();
+
         foreach (var address in addresses)
         {
-            var s = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+            var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+
             try
             {
-                await s.ConnectAsync(address, port).ConfigureAwait(false);
-                connectedSocket = s;
-                break;
+                await socket.ConnectAsync(address, port)
+                    .ConfigureAwait(false);
+
+                return socket;
             }
             catch (Exception e)
             {
-                s.Dispose();
-                lastException = e;
+                socket.Dispose();
+                exceptions.Add(e);
             }
         }
 
-        if (connectedSocket == null)
-        {
-            throw lastException;
-        }
-
-        return connectedSocket;
+        throw new AggregateException(exceptions);
     }
 
     private async Task TunnelThroughProxyAsync(HttpRequestMessage request, Stream transport, CancellationToken cancellationToken)
@@ -355,7 +360,7 @@ private async Task TunnelThroughProxyAsync(HttpRequestMessage request, Stream tr
         connectRequest.SetAddressLineProperty(authority);
         connectRequest.Headers.Host = authority;
 
-        HttpConnection connection = new HttpConnection(new BufferedReadStream(transport, null));
+        HttpConnection connection = new HttpConnection(new BufferedReadStream(transport, null, _logger));
         HttpResponseMessage connectResponse;
         try
         {
diff --git a/src/Docker.DotNet/MultiplexedStream.cs b/src/Docker.DotNet/MultiplexedStream.cs
index 48d5727e..e35db4d1 100644
--- a/src/Docker.DotNet/MultiplexedStream.cs
+++ b/src/Docker.DotNet/MultiplexedStream.cs
@@ -1,51 +1,55 @@
-#if !NET45
-#endif
-
 namespace Docker.DotNet;
 
-public class MultiplexedStream : IDisposable, IPeekableStream
+public sealed class MultiplexedStream : IDisposable, IPeekableStream
 {
+    private const int BufferSize = 16384;
     private readonly Stream _stream;
     private TargetStream _target;
     private int _remaining;
     private readonly byte[] _header = new byte[8];
     private readonly bool _multiplexed;
 
-    const int BufferSize = 81920;
-
     public MultiplexedStream(Stream stream, bool multiplexed)
     {
         _stream = stream;
         _multiplexed = multiplexed;
     }
 
-    public enum TargetStream
+    public void Dispose()
+    {
+        _stream.Dispose();
+    }
+
+    public enum TargetStream : byte
     {
         StandardIn = 0,
         StandardOut = 1,
         StandardError = 2
     }
 
-    public struct ReadResult
+    public readonly struct ReadResult
     {
-        public int Count { get; set; }
-        public TargetStream Target { get; set; }
+        public ReadResult(TargetStream target, int count)
+        {
+            Target = target;
+            Count = count;
+        }
+
+        public TargetStream Target { get; }
+
+        public int Count { get; }
+
         public bool EOF => Count == 0;
     }
 
     public void CloseWrite()
     {
-        if (_stream is WriteClosableStream closable)
+        if (_stream is WriteClosableStream writeClosableStream)
         {
-            closable.CloseWrite();
+            writeClosableStream.CloseWrite();
         }
     }
 
-    public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
-    {
-        return _stream.WriteAsync(buffer, offset, count, cancellationToken);
-    }
-
     public bool Peek(byte[] buffer, uint toPeek, out uint peeked, out uint available, out uint remaining)
     {
         if (_stream is IPeekableStream peekableStream)
@@ -56,46 +60,50 @@ public bool Peek(byte[] buffer, uint toPeek, out uint peeked, out uint available
         throw new NotSupportedException("_stream isn't a peekable stream");
     }
 
+    public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+    {
+        return _stream.WriteAsync(buffer, offset, count, cancellationToken);
+    }
+
     public async Task<ReadResult> ReadOutputAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
     {
+        int readBytesCount;
+
         if (!_multiplexed)
         {
-            return new ReadResult
-            {
-                Count = await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false),
-                Target = TargetStream.StandardOut
-            };
+            readBytesCount = await _stream.ReadAsync(buffer, offset, count, cancellationToken)
+                .ConfigureAwait(false);
+
+            return new ReadResult(TargetStream.StandardOut, readBytesCount);
         }
 
         while (_remaining == 0)
         {
             for (var i = 0; i < _header.Length;)
             {
-                var n = await _stream.ReadAsync(_header, i, _header.Length - i, cancellationToken).ConfigureAwait(false);
-                if (n == 0)
+                readBytesCount = await _stream.ReadAsync(_header, i, _header.Length - i, cancellationToken)
+                    .ConfigureAwait(false);
+
+                if (readBytesCount == 0)
                 {
                     if (i == 0)
                     {
-                        // End of the stream.
                         return new ReadResult();
                     }
 
                     throw new EndOfStreamException();
                 }
 
-                i += n;
+                i += readBytesCount;
             }
 
-            switch ((TargetStream)_header[0])
+            if (Enum.IsDefined(typeof(TargetStream), _header[0]))
             {
-                case TargetStream.StandardIn:
-                case TargetStream.StandardOut:
-                case TargetStream.StandardError:
-                    _target = (TargetStream)_header[0];
-                    break;
-
-                default:
-                    throw new IOException("unknown stream type");
+                _target = (TargetStream)_header[0];
+            }
+            else
+            {
+                throw new IOException($"Unknown stream type: '{_header[0]}'.");
             }
 
             _remaining = (_header[4] << 24) |
@@ -104,87 +112,84 @@ public async Task<ReadResult> ReadOutputAsync(byte[] buffer, int offset, int cou
                          _header[7];
         }
 
-        var toRead = Math.Min(count, _remaining);
-        var read = await _stream.ReadAsync(buffer, offset, toRead, cancellationToken).ConfigureAwait(false);
-        if (read == 0)
+        var remainingBytesCount = Math.Min(count, _remaining);
+
+        readBytesCount = await _stream.ReadAsync(buffer, offset, remainingBytesCount, cancellationToken)
+            .ConfigureAwait(false);
+
+        if (readBytesCount == 0)
         {
             throw new EndOfStreamException();
         }
 
-        _remaining -= read;
-        return new ReadResult
-        {
-            Count = read,
-            Target = _target
-        };
+        _remaining -= readBytesCount;
+        return new ReadResult(_target, readBytesCount);
     }
 
     public async Task<(string stdout, string stderr)> ReadOutputToEndAsync(CancellationToken cancellationToken)
     {
-        using (MemoryStream outMem = new MemoryStream(), outErr = new MemoryStream())
-        {
-            await CopyOutputToAsync(Stream.Null, outMem, outErr, cancellationToken);
+        using MemoryStream stdoutMemoryStream = new MemoryStream(), stderrMemoryStream = new MemoryStream();
 
-            outMem.Seek(0, SeekOrigin.Begin);
-            outErr.Seek(0, SeekOrigin.Begin);
+        using StreamReader stdoutStreamReader = new StreamReader(stdoutMemoryStream), stderrStreamReader = new StreamReader(stderrMemoryStream);
 
-            using (StreamReader outRdr = new StreamReader(outMem), errRdr = new StreamReader(outErr))
-            {
-                var stdout = outRdr.ReadToEnd();
-                var stderr = errRdr.ReadToEnd();
-                return (stdout, stderr);
-            }
-        }
+        await CopyOutputToAsync(Stream.Null, stdoutMemoryStream, stderrMemoryStream, cancellationToken)
+            .ConfigureAwait(false);
+
+        stdoutMemoryStream.Seek(0, SeekOrigin.Begin);
+        stderrMemoryStream.Seek(0, SeekOrigin.Begin);
+
+        var stdoutReadTask = stdoutStreamReader.ReadToEndAsync();
+        var stderrReadTask = stderrStreamReader.ReadToEndAsync();
+        await Task.WhenAll(stdoutReadTask, stderrReadTask)
+            .ConfigureAwait(false);
+
+        return (stdoutReadTask.Result, stderrReadTask.Result);
     }
 
     public async Task CopyFromAsync(Stream input, CancellationToken cancellationToken)
     {
-#if !NET45
         var buffer = ArrayPool<byte>.Shared.Rent(BufferSize);
-#else
-            var buffer = new byte[BufferSize];
-#endif
 
         try
         {
             for (;;)
             {
-                var count = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
-                if (count == 0)
+                var readBytesCount = await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken)
+                    .ConfigureAwait(false);
+
+                if (readBytesCount == 0)
                 {
                     break;
                 }
 
-                await WriteAsync(buffer, 0, count, cancellationToken).ConfigureAwait(false);
+                await WriteAsync(buffer, 0, readBytesCount, cancellationToken)
+                    .ConfigureAwait(false);
             }
         }
         finally
         {
-#if !NET45
             ArrayPool<byte>.Shared.Return(buffer);
-#endif
         }
     }
 
     public async Task CopyOutputToAsync(Stream stdin, Stream stdout, Stream stderr, CancellationToken cancellationToken)
     {
-#if !NET45
         var buffer = ArrayPool<byte>.Shared.Rent(BufferSize);
-#else
-            var buffer = new byte[BufferSize];
-#endif
 
         try
         {
             for (;;)
             {
-                var result = await ReadOutputAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
+                var result = await ReadOutputAsync(buffer, 0, buffer.Length, cancellationToken)
+                    .ConfigureAwait(false);
+
                 if (result.EOF)
                 {
                     return;
                 }
 
                 Stream stream;
+
                 switch (result.Target)
                 {
                     case TargetStream.StandardIn:
@@ -197,22 +202,16 @@ public async Task CopyOutputToAsync(Stream stdin, Stream stdout, Stream stderr,
                         stream = stderr;
                         break;
                     default:
-                        throw new InvalidOperationException($"Unknown TargetStream: '{result.Target}'.");
+                        throw new IOException($"Unknown stream type: '{result.Target}'.");
                 }
 
-                await stream.WriteAsync(buffer, 0, result.Count, cancellationToken).ConfigureAwait(false);
+                await stream.WriteAsync(buffer, 0, result.Count, cancellationToken)
+                    .ConfigureAwait(false);
             }
         }
         finally
         {
-#if !NET45
             ArrayPool<byte>.Shared.Return(buffer);
-#endif
         }
     }
-
-    public void Dispose()
-    {
-        ((IDisposable)_stream).Dispose();
-    }
 }
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/CommonCommands.cs b/test/Docker.DotNet.Tests/CommonCommands.cs
new file mode 100644
index 00000000..484f28db
--- /dev/null
+++ b/test/Docker.DotNet.Tests/CommonCommands.cs
@@ -0,0 +1,8 @@
+namespace Docker.DotNet.Tests;
+
+public static class CommonCommands
+{
+    public static readonly string[] SleepInfinity = ["/bin/sh", "-c", "trap \"exit 0\" TERM INT; sleep infinity"];
+
+    public static readonly string[] EchoToStdoutAndStderr = ["/bin/sh", "-c", "trap \"exit 0\" TERM INT; while true; do echo \"stdout message\"; echo \"stderr message\" >&2; sleep 1; done"];
+}
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj b/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj
index 342e804d..470c3e68 100644
--- a/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj
+++ b/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj
@@ -14,6 +14,9 @@
     <ProjectReference Include="..\..\src\Docker.DotNet.X509\Docker.DotNet.X509.csproj" />
     <ProjectReference Include="..\..\src\Docker.DotNet\Docker.DotNet.csproj" />
   </ItemGroup>
+  <ItemGroup>
+    <Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
+  </ItemGroup>
   <ItemGroup>
     <Using Include="System" />
     <Using Include="System.Collections.Generic" />
@@ -24,7 +27,10 @@
     <Using Include="System.Threading" />
     <Using Include="System.Threading.Tasks" />
     <Using Include="Docker.DotNet.Models" />
+    <Using Include="Microsoft.Extensions.Logging" />
+    <Using Include="Microsoft.Extensions.Logging.Abstractions" />
     <Using Include="Xunit" />
     <Using Include="Xunit.Abstractions" />
+    <Using Include="Xunit.Sdk" />
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/IConfigOperationsTests.cs b/test/Docker.DotNet.Tests/IConfigOperationsTests.cs
index e3c9b78c..533d5d25 100644
--- a/test/Docker.DotNet.Tests/IConfigOperationsTests.cs
+++ b/test/Docker.DotNet.Tests/IConfigOperationsTests.cs
@@ -3,21 +3,21 @@ namespace Docker.DotNet.Tests;
 [Collection(nameof(TestCollection))]
 public class IConfigOperationsTests
 {
-    private readonly DockerClient _dockerClient;
-    private readonly TestOutput _output;
+    private readonly TestFixture _testFixture;
+    private readonly ITestOutputHelper _testOutputHelper;
 
-    public IConfigOperationsTests(TestFixture testFixture, ITestOutputHelper outputHelper)
+    public IConfigOperationsTests(TestFixture testFixture, ITestOutputHelper testOutputHelper)
     {
-        _dockerClient = testFixture.DockerClient;
-        _output = new TestOutput(outputHelper);
+        _testFixture = testFixture;
+        _testOutputHelper = testOutputHelper;
     }
 
     [Fact]
     public async Task SwarmConfig_CanCreateAndRead()
     {
-        var currentConfigs = await _dockerClient.Configs.ListConfigsAsync();
+        var currentConfigs = await _testFixture.DockerClient.Configs.ListConfigsAsync();
 
-        _output.WriteLine($"Current Configs: {currentConfigs.Count}");
+        _testOutputHelper.WriteLine($"Current Configs: {currentConfigs.Count}");
 
         var testConfigSpec = new SwarmConfigSpec
         {
@@ -31,15 +31,15 @@ public async Task SwarmConfig_CanCreateAndRead()
             Config = testConfigSpec
         };
 
-        var createdConfig = await _dockerClient.Configs.CreateConfigAsync(configParameters);
+        var createdConfig = await _testFixture.DockerClient.Configs.CreateConfigAsync(configParameters);
         Assert.NotNull(createdConfig.ID);
-        _output.WriteLine($"Config created: {createdConfig.ID}");
+        _testOutputHelper.WriteLine($"Config created: {createdConfig.ID}");
 
-        var configs = await _dockerClient.Configs.ListConfigsAsync();
+        var configs = await _testFixture.DockerClient.Configs.ListConfigsAsync();
         Assert.Contains(configs, c => c.ID == createdConfig.ID);
-        _output.WriteLine($"Current Configs: {configs.Count}");
+        _testOutputHelper.WriteLine($"Current Configs: {configs.Count}");
 
-        var configResponse = await _dockerClient.Configs.InspectConfigAsync(createdConfig.ID);
+        var configResponse = await _testFixture.DockerClient.Configs.InspectConfigAsync(createdConfig.ID);
 
         Assert.NotNull(configResponse);
 
@@ -49,10 +49,10 @@ public async Task SwarmConfig_CanCreateAndRead()
         Assert.Equal(configResponse.Spec.Templating, testConfigSpec.Templating);
 
 
-        _output.WriteLine("Config created is the same.");
+        _testOutputHelper.WriteLine("Config created is the same.");
 
-        await _dockerClient.Configs.RemoveConfigAsync(createdConfig.ID);
+        await _testFixture.DockerClient.Configs.RemoveConfigAsync(createdConfig.ID);
 
-        await Assert.ThrowsAsync<DockerApiException>(() => _dockerClient.Configs.InspectConfigAsync(createdConfig.ID));
+        await Assert.ThrowsAsync<DockerApiException>(() => _testFixture.DockerClient.Configs.InspectConfigAsync(createdConfig.ID));
     }
 }
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/IContainerOperationsTests.cs b/test/Docker.DotNet.Tests/IContainerOperationsTests.cs
index d8d2d10d..31e1b747 100644
--- a/test/Docker.DotNet.Tests/IContainerOperationsTests.cs
+++ b/test/Docker.DotNet.Tests/IContainerOperationsTests.cs
@@ -3,101 +3,55 @@ namespace Docker.DotNet.Tests;
 [Collection(nameof(TestCollection))]
 public class IContainerOperationsTests
 {
-    private readonly CancellationTokenSource _cts;
+    private readonly TestFixture _testFixture;
+    private readonly ITestOutputHelper _testOutputHelper;
 
-    private readonly TestOutput _output;
-    private readonly string _imageId;
-    private readonly DockerClientConfiguration _dockerClientConfiguration;
-    private readonly DockerClient _dockerClient;
-
-    public IContainerOperationsTests(TestFixture testFixture, ITestOutputHelper outputHelper)
+    public IContainerOperationsTests(TestFixture testFixture, ITestOutputHelper testOutputHelper)
     {
-        _output = new TestOutput(outputHelper);
-
-        _dockerClientConfiguration = testFixture.DockerClientConfiguration;
-        _dockerClient = _dockerClientConfiguration.CreateClient();
-
-        // Do not wait forever in case it gets stuck
-        _cts = CancellationTokenSource.CreateLinkedTokenSource(testFixture.Cts.Token);
-        _cts.CancelAfter(TimeSpan.FromMinutes(5));
-        _cts.Token.Register(() => throw new TimeoutException("ContainerOperationsTests timeout"));
-
-        _imageId = testFixture.Image.ID;
+        _testFixture = testFixture;
+        _testOutputHelper = testOutputHelper;
     }
 
     [Fact]
     public async Task CreateContainerAsync_CreatesContainer()
     {
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
             new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         Assert.NotNull(createContainerResponse);
         Assert.NotEmpty(createContainerResponse.ID);
     }
 
-    // Timeout causing task to be cancelled
-    [Theory(Skip = "There is nothing we can do to delay CreateContainerAsync (aka HttpClient.SendAsync) deterministic. We cannot control if it responses successful before the timeout.")]
-    [InlineData(1)]
-    [InlineData(5)]
-    [InlineData(10)]
-    public async Task CreateContainerAsync_TimeoutExpires_Fails(int millisecondsTimeout)
-    {
-        using var dockerClientWithTimeout = _dockerClientConfiguration.CreateClient();
-
-        dockerClientWithTimeout.DefaultTimeout = TimeSpan.FromMilliseconds(millisecondsTimeout);
-
-        _output.WriteLine($"Time available for CreateContainer operation: {millisecondsTimeout} ms'");
-
-        var timer = new Stopwatch();
-        timer.Start();
-
-        var createContainerTask = dockerClientWithTimeout.Containers.CreateContainerAsync(
-            new CreateContainerParameters
-            {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
-            },
-            _cts.Token);
-
-        _ = await Assert.ThrowsAsync<OperationCanceledException>(() => createContainerTask);
-
-        timer.Stop();
-        _output.WriteLine($"CreateContainerOperation finished after {timer.ElapsedMilliseconds} ms");
-
-        Assert.True(createContainerTask.IsCanceled);
-        Assert.True(createContainerTask.IsCompleted);
-    }
-
     [Fact]
     public async Task GetContainerLogs_Tty_False_Follow_True_TaskIsCompleted()
     {
         using var containerLogsCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = false
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         containerLogsCts.CancelAfter(TimeSpan.FromSeconds(5));
 
-        var containerLogsTask = _dockerClient.Containers.GetContainerLogsAsync(
+        var containerLogsTask = _testFixture.DockerClient.Containers.GetContainerLogsAsync(
             createContainerResponse.ID,
             new ContainerLogsParameters
             {
@@ -107,13 +61,13 @@ await _dockerClient.Containers.StartContainerAsync(
                 Follow = true
             },
             containerLogsCts.Token,
-            new Progress<string>(m => _output.WriteLine(m))
+            new Progress<string>(m => _testOutputHelper.WriteLine(m))
         );
 
-        await _dockerClient.Containers.StopContainerAsync(
+        await _testFixture.DockerClient.Containers.StopContainerAsync(
             createContainerResponse.ID,
             new ContainerStopParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         await containerLogsTask;
@@ -125,25 +79,25 @@ public async Task GetContainerLogs_Tty_False_Follow_False_ReadsLogs()
     {
         var logList = new List<string>();
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = false
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         await Task.Delay(TimeSpan.FromSeconds(5));
 
-        await _dockerClient.Containers.GetContainerLogsAsync(
+        await _testFixture.DockerClient.Containers.GetContainerLogsAsync(
             createContainerResponse.ID,
             new ContainerLogsParameters
             {
@@ -152,17 +106,17 @@ await _dockerClient.Containers.GetContainerLogsAsync(
                 Timestamps = true,
                 Follow = false
             },
-            default,
-            new Progress<string>(m => { logList.Add(m); _output.WriteLine(m); })
+            _testFixture.Cts.Token,
+            new Progress<string>(m => { _testOutputHelper.WriteLine(m); logList.Add(m); })
         );
 
-        await _dockerClient.Containers.StopContainerAsync(
+        await _testFixture.DockerClient.Containers.StopContainerAsync(
             createContainerResponse.ID,
             new ContainerStopParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        _output.WriteLine($"Line count: {logList.Count}");
+        _testOutputHelper.WriteLine($"Line count: {logList.Count}");
 
         Assert.NotEmpty(logList);
     }
@@ -172,25 +126,25 @@ public async Task GetContainerLogs_Tty_True_Follow_False_ReadsLogs()
     {
         var logList = new List<string>();
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = true
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         await Task.Delay(TimeSpan.FromSeconds(5));
 
-        await _dockerClient.Containers.GetContainerLogsAsync(
+        await _testFixture.DockerClient.Containers.GetContainerLogsAsync(
             createContainerResponse.ID,
             new ContainerLogsParameters
             {
@@ -199,17 +153,17 @@ await _dockerClient.Containers.GetContainerLogsAsync(
                 Timestamps = true,
                 Follow = false
             },
-            default,
-            new Progress<string>(m => { _output.WriteLine(m); logList.Add(m); })
+            _testFixture.Cts.Token,
+            new Progress<string>(m => { _testOutputHelper.WriteLine(m); logList.Add(m); })
         );
 
-        await _dockerClient.Containers.StopContainerAsync(
+        await _testFixture.DockerClient.Containers.StopContainerAsync(
             createContainerResponse.ID,
             new ContainerStopParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        _output.WriteLine($"Line count: {logList.Count}");
+        _testOutputHelper.WriteLine($"Line count: {logList.Count}");
 
         Assert.NotEmpty(logList);
     }
@@ -219,25 +173,25 @@ public async Task GetContainerLogs_Tty_False_Follow_True_Requires_Task_To_Be_Can
     {
         using var containerLogsCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = false
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         containerLogsCts.CancelAfter(TimeSpan.FromSeconds(5));
 
-        await Assert.ThrowsAsync<TaskCanceledException>(() => _dockerClient.Containers.GetContainerLogsAsync(
+        await Assert.ThrowsAsync<TaskCanceledException>(() => _testFixture.DockerClient.Containers.GetContainerLogsAsync(
             createContainerResponse.ID,
             new ContainerLogsParameters
             {
@@ -247,7 +201,7 @@ await Assert.ThrowsAsync<TaskCanceledException>(() => _dockerClient.Containers.G
                 Follow = true
             },
             containerLogsCts.Token,
-            new Progress<string>(m => _output.WriteLine(m))
+            new Progress<string>(m => _testOutputHelper.WriteLine(m))
         ));
     }
 
@@ -256,25 +210,25 @@ public async Task GetContainerLogs_Tty_True_Follow_True_Requires_Task_To_Be_Canc
     {
         using var containerLogsCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = true
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         containerLogsCts.CancelAfter(TimeSpan.FromSeconds(5));
 
-        var containerLogsTask = _dockerClient.Containers.GetContainerLogsAsync(
+        var containerLogsTask = _testFixture.DockerClient.Containers.GetContainerLogsAsync(
             createContainerResponse.ID,
             new ContainerLogsParameters
             {
@@ -284,7 +238,7 @@ await _dockerClient.Containers.StartContainerAsync(
                 Follow = true
             },
             containerLogsCts.Token,
-            new Progress<string>(m => _output.WriteLine(m))
+            new Progress<string>(m => _testOutputHelper.WriteLine(m))
         );
 
         await Assert.ThrowsAsync<TaskCanceledException>(() => containerLogsTask);
@@ -296,25 +250,25 @@ public async Task GetContainerLogs_Tty_True_Follow_True_ReadsLogs_TaskIsCancelle
         using var containerLogsCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
         var logList = new List<string>();
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = true
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         containerLogsCts.CancelAfter(TimeSpan.FromSeconds(5));
 
-        var containerLogsTask = _dockerClient.Containers.GetContainerLogsAsync(
+        var containerLogsTask = _testFixture.DockerClient.Containers.GetContainerLogsAsync(
             createContainerResponse.ID,
             new ContainerLogsParameters
             {
@@ -324,20 +278,19 @@ await _dockerClient.Containers.StartContainerAsync(
                 Follow = true
             },
             containerLogsCts.Token,
-            new Progress<string>(m => { _output.WriteLine(m); logList.Add(m); })
+            new Progress<string>(m => { _testOutputHelper.WriteLine(m); logList.Add(m); })
         );
 
         await Task.Delay(TimeSpan.FromSeconds(5));
 
-        await _dockerClient.Containers.StopContainerAsync(
+        await _testFixture.DockerClient.Containers.StopContainerAsync(
             createContainerResponse.ID,
             new ContainerStopParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-
         await Assert.ThrowsAsync<TaskCanceledException>(() => containerLogsTask);
-        _output.WriteLine($"Line count: {logList.Count}");
+        _testOutputHelper.WriteLine($"Line count: {logList.Count}");
 
         Assert.NotEmpty(logList);
     }
@@ -345,34 +298,34 @@ await _dockerClient.Containers.StopContainerAsync(
     [Fact]
     public async Task GetContainerStatsAsync_Tty_False_Stream_False_ReadsStats()
     {
-        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
         var containerStatsList = new List<ContainerStatsResponse>();
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
             new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = false
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        _ = await _dockerClient.Containers.StartContainerAsync(
+        _ = await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         tcs.CancelAfter(TimeSpan.FromSeconds(10));
 
-        await _dockerClient.Containers.GetContainerStatsAsync(
+        await _testFixture.DockerClient.Containers.GetContainerStatsAsync(
             createContainerResponse.ID,
             new ContainerStatsParameters
             {
                 Stream = false
             },
-            new Progress<ContainerStatsResponse>(m => { _output.WriteLine(m.ID); containerStatsList.Add(m); }),
+            new Progress<ContainerStatsResponse>(m => { _testOutputHelper.WriteLine(m.ID); containerStatsList.Add(m); }),
             tcs.Token
         );
 
@@ -380,31 +333,33 @@ await _dockerClient.Containers.GetContainerStatsAsync(
 
         Assert.NotEmpty(containerStatsList);
         Assert.Single(containerStatsList);
-        _output.WriteLine($"ConntainerStats count: {containerStatsList.Count}");
+        _testOutputHelper.WriteLine($"ConntainerStats count: {containerStatsList.Count}");
     }
 
     [Fact]
     public async Task GetContainerStatsAsync_Tty_False_StreamStats()
     {
-        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
         using (tcs.Token.Register(() => throw new TimeoutException("GetContainerStatsAsync_Tty_False_StreamStats")))
         {
-            _output.WriteLine($"Running test {MethodBase.GetCurrentMethod().Module}->{MethodBase.GetCurrentMethod().Name}");
+            var method = MethodBase.GetCurrentMethod();
+
+            _testOutputHelper.WriteLine($"Running test '{method!.Module}' -> '{method!.Name}'");
 
-            var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-                new CreateContainerParameters()
+            var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+                new CreateContainerParameters
                 {
-                    Image = _imageId,
-                    Name = Guid.NewGuid().ToString(),
+                    Image = _testFixture.Image.ID,
+                    Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                     Tty = false
                 },
-                _cts.Token
+                _testFixture.Cts.Token
             );
 
-            _ = await _dockerClient.Containers.StartContainerAsync(
+            _ = await _testFixture.DockerClient.Containers.StartContainerAsync(
                 createContainerResponse.ID,
                 new ContainerStartParameters(),
-                _cts.Token
+                _testFixture.Cts.Token
             );
 
             List<ContainerStatsResponse> containerStatsList = new List<ContainerStatsResponse>();
@@ -413,13 +368,13 @@ public async Task GetContainerStatsAsync_Tty_False_StreamStats()
             linkedCts.CancelAfter(TimeSpan.FromSeconds(5));
             try
             {
-                await _dockerClient.Containers.GetContainerStatsAsync(
+                await _testFixture.DockerClient.Containers.GetContainerStatsAsync(
                     createContainerResponse.ID,
                     new ContainerStatsParameters
                     {
                         Stream = true
                     },
-                    new Progress<ContainerStatsResponse>(m => { containerStatsList.Add(m); _output.WriteLine(JsonSerializer.Instance.Serialize(m)); }),
+                    new Progress<ContainerStatsResponse>(m => { containerStatsList.Add(m); _testOutputHelper.WriteLine(JsonSerializer.Instance.Serialize(m)); }),
                     linkedCts.Token
                 );
             }
@@ -428,7 +383,7 @@ await _dockerClient.Containers.GetContainerStatsAsync(
                 // this  is expected to  happen on task cancelaltion
             }
 
-            _output.WriteLine($"Container stats count: {containerStatsList.Count}");
+            _testOutputHelper.WriteLine($"Container stats count: {containerStatsList.Count}");
             Assert.NotEmpty(containerStatsList);
         }
     }
@@ -436,34 +391,34 @@ await _dockerClient.Containers.GetContainerStatsAsync(
     [Fact]
     public async Task GetContainerStatsAsync_Tty_True_Stream_False_ReadsStats()
     {
-        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
         var containerStatsList = new List<ContainerStatsResponse>();
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
             new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                 Tty = true
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        _ = await _dockerClient.Containers.StartContainerAsync(
+        _ = await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         tcs.CancelAfter(TimeSpan.FromSeconds(10));
 
-        await _dockerClient.Containers.GetContainerStatsAsync(
+        await _testFixture.DockerClient.Containers.GetContainerStatsAsync(
             createContainerResponse.ID,
             new ContainerStatsParameters
             {
                 Stream = false
             },
-            new Progress<ContainerStatsResponse>(m => { _output.WriteLine(m.ID); containerStatsList.Add(m); }),
+            new Progress<ContainerStatsResponse>(m => { _testOutputHelper.WriteLine(m.ID); containerStatsList.Add(m); }),
             tcs.Token
         );
 
@@ -471,32 +426,32 @@ await _dockerClient.Containers.GetContainerStatsAsync(
 
         Assert.NotEmpty(containerStatsList);
         Assert.Single(containerStatsList);
-        _output.WriteLine($"ConntainerStats count: {containerStatsList.Count}");
+        _testOutputHelper.WriteLine($"ConntainerStats count: {containerStatsList.Count}");
     }
 
     [Fact]
     public async Task GetContainerStatsAsync_Tty_True_StreamStats()
     {
-        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+        using var tcs = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
 
         using (tcs.Token.Register(() => throw new TimeoutException("GetContainerStatsAsync_Tty_True_StreamStats")))
         {
-            _output.WriteLine("Running test GetContainerStatsAsync_Tty_True_StreamStats");
+            _testOutputHelper.WriteLine("Running test GetContainerStatsAsync_Tty_True_StreamStats");
 
-            var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-                new CreateContainerParameters()
+            var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+                new CreateContainerParameters
                 {
-                    Image = _imageId,
-                    Name = Guid.NewGuid().ToString(),
+                    Image = _testFixture.Image.ID,
+                    Entrypoint = CommonCommands.EchoToStdoutAndStderr,
                     Tty = true
                 },
-                _cts.Token
+                _testFixture.Cts.Token
             );
 
-            _ = await _dockerClient.Containers.StartContainerAsync(
+            _ = await _testFixture.DockerClient.Containers.StartContainerAsync(
                 createContainerResponse.ID,
                 new ContainerStartParameters(),
-                _cts.Token
+                _testFixture.Cts.Token
             );
 
             List<ContainerStatsResponse> containerStatsList = new List<ContainerStatsResponse>();
@@ -506,23 +461,23 @@ public async Task GetContainerStatsAsync_Tty_True_StreamStats()
 
             try
             {
-                await _dockerClient.Containers.GetContainerStatsAsync(
+                await _testFixture.DockerClient.Containers.GetContainerStatsAsync(
                     createContainerResponse.ID,
                     new ContainerStatsParameters
                     {
                         Stream = true
                     },
-                    new Progress<ContainerStatsResponse>(m => { containerStatsList.Add(m); _output.WriteLine(JsonSerializer.Instance.Serialize(m)); }),
+                    new Progress<ContainerStatsResponse>(m => { containerStatsList.Add(m); _testOutputHelper.WriteLine(JsonSerializer.Instance.Serialize(m)); }),
                     linkedTcs.Token
                 );
             }
             catch (OperationCanceledException)
             {
-                // this  is expected to  happen on task cancelaltion
+                // This is expected to happen on task cancellation.
             }
 
             await Task.Delay(TimeSpan.FromSeconds(1));
-            _output.WriteLine($"Container stats count: {containerStatsList.Count}");
+            _testOutputHelper.WriteLine($"Container stats count: {containerStatsList.Count}");
             Assert.NotEmpty(containerStatsList);
         }
     }
@@ -530,63 +485,65 @@ await _dockerClient.Containers.GetContainerStatsAsync(
     [Fact]
     public async Task KillContainerAsync_ContainerRunning_Succeeds()
     {
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
             new CreateContainerParameters
             {
-                Image = _imageId
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr
             },
-            _cts.Token);
+            _testFixture.Cts.Token);
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        var inspectRunningContainerResponse = await _dockerClient.Containers.InspectContainerAsync(
+        var inspectRunningContainerResponse = await _testFixture.DockerClient.Containers.InspectContainerAsync(
             createContainerResponse.ID,
-            _cts.Token);
+            _testFixture.Cts.Token);
 
-        await _dockerClient.Containers.KillContainerAsync(
+        await _testFixture.DockerClient.Containers.KillContainerAsync(
             createContainerResponse.ID,
             new ContainerKillParameters(),
-            _cts.Token);
+            _testFixture.Cts.Token);
 
-        var inspectKilledContainerResponse = await _dockerClient.Containers.InspectContainerAsync(
+        var inspectKilledContainerResponse = await _testFixture.DockerClient.Containers.InspectContainerAsync(
             createContainerResponse.ID,
-            _cts.Token);
+            _testFixture.Cts.Token);
 
         Assert.True(inspectRunningContainerResponse.State.Running);
         Assert.False(inspectKilledContainerResponse.State.Running);
         Assert.Equal("exited", inspectKilledContainerResponse.State.Status);
 
-        _output.WriteLine("Killed");
-        _output.WriteLine(JsonSerializer.Instance.Serialize(inspectKilledContainerResponse));
+        _testOutputHelper.WriteLine("Killed");
+        _testOutputHelper.WriteLine(JsonSerializer.Instance.Serialize(inspectKilledContainerResponse));
     }
 
     [Fact]
     public async Task ListContainersAsync_ContainerExists_Succeeds()
     {
-        await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameters()
+        await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString()
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
             },
-            _cts.Token);
+            _testFixture.Cts.Token);
 
-        IList<ContainerListResponse> containerList = await _dockerClient.Containers.ListContainersAsync(
+        IList<ContainerListResponse> containerList = await _testFixture.DockerClient.Containers.ListContainersAsync(
             new ContainersListParameters
             {
                 Filters = new Dictionary<string, IDictionary<string, bool>>
                 {
                     ["ancestor"] = new Dictionary<string, bool>
                     {
-                        [_imageId] = true
+                        [_testFixture.Image.ID] = true
                     }
                 },
                 All = true
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         Assert.NotNull(containerList);
@@ -596,32 +553,32 @@ await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameter
     [Fact]
     public async Task ListProcessesAsync_RunningContainer_Succeeds()
     {
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString()
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.StartContainerAsync(
+        await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        var containerProcessesResponse = await _dockerClient.Containers.ListProcessesAsync(
+        var containerProcessesResponse = await _testFixture.DockerClient.Containers.ListProcessesAsync(
             createContainerResponse.ID,
             new ContainerListProcessesParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        _output.WriteLine($"Title  '{containerProcessesResponse.Titles[0]}' - '{containerProcessesResponse.Titles[1]}' - '{containerProcessesResponse.Titles[2]}' - '{containerProcessesResponse.Titles[3]}'");
+        _testOutputHelper.WriteLine($"Title  '{containerProcessesResponse.Titles[0]}' - '{containerProcessesResponse.Titles[1]}' - '{containerProcessesResponse.Titles[2]}' - '{containerProcessesResponse.Titles[3]}'");
 
         foreach (var processes in containerProcessesResponse.Processes)
         {
-            _output.WriteLine($"Process '{processes[0]}' - ''{processes[1]}' - '{processes[2]}' - '{processes[3]}'");
+            _testOutputHelper.WriteLine($"Process '{processes[0]}' - ''{processes[1]}' - '{processes[2]}' - '{processes[3]}'");
         }
 
         Assert.NotNull(containerProcessesResponse);
@@ -631,32 +588,32 @@ await _dockerClient.Containers.StartContainerAsync(
     [Fact]
     public async Task RemoveContainerAsync_ContainerExists_Succeedes()
     {
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString()
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        ContainerInspectResponse inspectCreatedContainer = await _dockerClient.Containers.InspectContainerAsync(
+        ContainerInspectResponse inspectCreatedContainer = await _testFixture.DockerClient.Containers.InspectContainerAsync(
             createContainerResponse.ID,
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Containers.RemoveContainerAsync(
+        await _testFixture.DockerClient.Containers.RemoveContainerAsync(
             createContainerResponse.ID,
             new ContainerRemoveParameters
             {
                 Force = true
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        Task inspectRemovedContainerTask = _dockerClient.Containers.InspectContainerAsync(
+        Task inspectRemovedContainerTask = _testFixture.DockerClient.Containers.InspectContainerAsync(
             createContainerResponse.ID,
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         Assert.NotNull(inspectCreatedContainer.State);
@@ -666,19 +623,19 @@ await _dockerClient.Containers.RemoveContainerAsync(
     [Fact]
     public async Task StartContainerAsync_ContainerExists_Succeeds()
     {
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters()
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
+            new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString()
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr,
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        var startContainerResult = await _dockerClient.Containers.StartContainerAsync(
+        var startContainerResult = await _testFixture.DockerClient.Containers.StartContainerAsync(
             createContainerResponse.ID,
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         Assert.True(startContainerResult);
@@ -687,10 +644,10 @@ public async Task StartContainerAsync_ContainerExists_Succeeds()
     [Fact]
     public async Task StartContainerAsync_ContainerNotExists_ThrowsException()
     {
-        Task startContainerTask = _dockerClient.Containers.StartContainerAsync(
+        Task startContainerTask = _testFixture.DockerClient.Containers.StartContainerAsync(
             Guid.NewGuid().ToString(),
             new ContainerStartParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
         await Assert.ThrowsAsync<DockerContainerNotFoundException>(() => startContainerTask);
@@ -703,20 +660,20 @@ public async Task WaitContainerAsync_TokenIsCancelled_OperationCancelledExceptio
 
         using var waitContainerCts = new CancellationTokenSource(delay: TimeSpan.FromMinutes(5));
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(
             new CreateContainerParameters
             {
-                Image = _imageId,
-                Name = Guid.NewGuid().ToString(),
+                Image = _testFixture.Image.ID,
+                Entrypoint = CommonCommands.EchoToStdoutAndStderr
             },
             waitContainerCts.Token
         );
 
-        _output.WriteLine($"CreateContainerResponse: '{JsonSerializer.Instance.Serialize(createContainerResponse)}'");
+        _testOutputHelper.WriteLine($"CreateContainerResponse: '{JsonSerializer.Instance.Serialize(createContainerResponse)}'");
 
-        _ = await _dockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters(), waitContainerCts.Token);
+        _ = await _testFixture.DockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters(), waitContainerCts.Token);
 
-        _output.WriteLine("Starting timeout to cancel WaitContainer operation.");
+        _testOutputHelper.WriteLine("Starting timeout to cancel WaitContainer operation.");
 
         TimeSpan delay = TimeSpan.FromSeconds(5);
 
@@ -724,14 +681,14 @@ public async Task WaitContainerAsync_TokenIsCancelled_OperationCancelledExceptio
         stopWatch.Start();
 
         // Will wait forever here if cancelation fails.
-        var waitContainerTask = _dockerClient.Containers.WaitContainerAsync(createContainerResponse.ID, waitContainerCts.Token);
+        var waitContainerTask = _testFixture.DockerClient.Containers.WaitContainerAsync(createContainerResponse.ID, waitContainerCts.Token);
 
         _ = await Assert.ThrowsAsync<TaskCanceledException>(() => waitContainerTask);
 
         stopWatch.Stop();
 
-        _output.WriteLine($"WaitContainerTask was cancelled after {stopWatch.ElapsedMilliseconds} ms");
-        _output.WriteLine($"WaitContainerAsync: {stopWatch.Elapsed} elapsed");
+        _testOutputHelper.WriteLine($"WaitContainerTask was cancelled after {stopWatch.ElapsedMilliseconds} ms");
+        _testOutputHelper.WriteLine($"WaitContainerAsync: {stopWatch.Elapsed} elapsed");
 
         // Task should be cancelled when CancelAfter timespan expires
         TimeSpan tolerance = TimeSpan.FromMilliseconds(500);
@@ -741,46 +698,41 @@ public async Task WaitContainerAsync_TokenIsCancelled_OperationCancelledExceptio
     }
 
     [Fact]
-    public async Task CreateImageAsync_NonexistantImage_ThrowsDockerImageNotFoundException()
+    public async Task CreateImageAsync_NonExistingImage_ThrowsDockerImageNotFoundException()
     {
-        var parameters = new CreateContainerParameters
-        {
-            Image = "no-such-image-ytfghbkufhresdhtrjygvb",
-        };
-        Func<Task> op = async () => await _dockerClient.Containers.CreateContainerAsync(parameters);
+        var createContainerParameters = new CreateContainerParameters();
+        createContainerParameters.Image = Guid.NewGuid().ToString("D");
+
+        Func<Task> op = () => _testFixture.DockerClient.Containers.CreateContainerAsync(createContainerParameters);
 
         await Assert.ThrowsAsync<DockerImageNotFoundException>(op);
     }
 
-    [Fact]
+    [Fact(Skip = "Refactor IExecOperations operations and writing/reading to/from stdin and stdout. It does not work reliably.")]
     public async Task MultiplexedStreamWriteAsync_DoesNotThrowAnException()
     {
         // Given
-        Exception exception;
+        var createContainerParameters = new CreateContainerParameters();
+        createContainerParameters.Image = _testFixture.Image.ID;
+        createContainerParameters.Entrypoint = CommonCommands.SleepInfinity;
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(
-            new CreateContainerParameters
-            {
-                Image = _imageId
-            });
+        var containerExecCreateParameters = new ContainerExecCreateParameters();
+        containerExecCreateParameters.AttachStdout = true;
+        containerExecCreateParameters.AttachStderr = true;
+        containerExecCreateParameters.AttachStdin = true;
+        containerExecCreateParameters.Cmd = new[] { "/bin/sh", "-c", "read line; echo Done" };
 
-        _ = await _dockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters());
+        var containerExecStartParameters = new ContainerExecStartParameters();
 
-        var containerExecCreateResponse = await _dockerClient.Exec.ExecCreateContainerAsync(createContainerResponse.ID,
-            new ContainerExecCreateParameters
-            {
-                AttachStdout = true,
-                AttachStderr = true,
-                AttachStdin = true,
-                Cmd = new [] { string.Empty }
-            });
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(createContainerParameters);
+        _ = await _testFixture.DockerClient.Containers.StartContainerAsync(createContainerResponse.ID, new ContainerStartParameters());
 
         // When
-        using (var stream = await _dockerClient.Exec.StartAndAttachContainerExecAsync(containerExecCreateResponse.ID, false))
-        {
-            var buffer = new byte[] { 10 };
-            exception = await Record.ExceptionAsync(() => stream.WriteAsync(buffer, 0, buffer.Length, default));
-        }
+        var containerExecCreateResponse = await _testFixture.DockerClient.Exec.ExecCreateContainerAsync(createContainerResponse.ID, containerExecCreateParameters);
+        using var stream = await _testFixture.DockerClient.Exec.StartWithConfigContainerExecAsync(containerExecCreateResponse.ID, containerExecStartParameters);
+
+        var buffer = new byte[] { 10 };
+        var exception = await Record.ExceptionAsync(() => stream.WriteAsync(buffer, 0, buffer.Length, _testFixture.Cts.Token));
 
         // Then
         Assert.Null(exception);
diff --git a/test/Docker.DotNet.Tests/IImageOperationsTests.cs b/test/Docker.DotNet.Tests/IImageOperationsTests.cs
index e74c80b5..50d522f7 100644
--- a/test/Docker.DotNet.Tests/IImageOperationsTests.cs
+++ b/test/Docker.DotNet.Tests/IImageOperationsTests.cs
@@ -3,38 +3,25 @@ namespace Docker.DotNet.Tests;
 [Collection(nameof(TestCollection))]
 public class IImageOperationsTests
 {
-    private readonly CancellationTokenSource _cts;
+    private readonly TestFixture _testFixture;
+    private readonly ITestOutputHelper _testOutputHelper;
 
-    private readonly TestOutput _output;
-    private readonly string _repositoryName;
-    private readonly string _tag;
-    private readonly DockerClient _dockerClient;
-
-    public IImageOperationsTests(TestFixture testFixture, ITestOutputHelper outputHelper)
+    public IImageOperationsTests(TestFixture testFixture, ITestOutputHelper testOutputHelper)
     {
-        _output = new TestOutput(outputHelper);
-
-        _dockerClient = testFixture.DockerClient;
-
-        // Do not wait forever in case it gets stuck
-        _cts = CancellationTokenSource.CreateLinkedTokenSource(testFixture.Cts.Token);
-        _cts.CancelAfter(TimeSpan.FromMinutes(5));
-        _cts.Token.Register(() => throw new TimeoutException("ImageOperationTests timeout"));
-
-        _repositoryName = testFixture.Repository;
-        _tag = testFixture.Tag;
+        _testFixture = testFixture;
+        _testOutputHelper = testOutputHelper;
     }
 
     [Fact]
-    public async Task CreateImageAsync_TaskCancelled_ThowsTaskCanceledException()
+    public async Task CreateImageAsync_TaskCancelled_ThrowsTaskCanceledException()
     {
-        using var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
 
         var newTag = Guid.NewGuid().ToString();
         var newRepositoryName = Guid.NewGuid().ToString();
 
-        await _dockerClient.Images.TagImageAsync(
-            $"{_repositoryName}:{_tag}",
+        await _testFixture.DockerClient.Images.TagImageAsync(
+            $"{_testFixture.Repository}:{_testFixture.Tag}",
             new ImageTagParameters
             {
                 RepositoryName = newRepositoryName,
@@ -43,13 +30,13 @@ await _dockerClient.Images.TagImageAsync(
             cts.Token
         );
 
-        var createImageTask = _dockerClient.Images.CreateImageAsync(
+        var createImageTask = _testFixture.DockerClient.Images.CreateImageAsync(
             new ImagesCreateParameters
             {
                 FromImage = $"{newRepositoryName}:{newTag}"
             },
             null,
-            new Progress<JSONMessage>((message) => _output.WriteLine(JsonSerializer.Instance.Serialize(message))),
+            new Progress<JSONMessage>(message => _testOutputHelper.WriteLine(JsonSerializer.Instance.Serialize(message))),
             cts.Token);
 
         TimeSpan delay = TimeSpan.FromMilliseconds(5);
@@ -63,8 +50,8 @@ await _dockerClient.Images.TagImageAsync(
     [Fact]
     public Task CreateImageAsync_ErrorResponse_ThrowsDockerApiException()
     {
-        return Assert.ThrowsAsync<DockerApiException>(() => _dockerClient.Images.CreateImageAsync(
-            new ImagesCreateParameters()
+        return Assert.ThrowsAsync<DockerApiException>(() => _testFixture.DockerClient.Images.CreateImageAsync(
+            new ImagesCreateParameters
             {
                 FromImage = "1.2.3.Apparently&this$is+not-a_valid%repository//name",
                 Tag = "ancient-one"
@@ -76,30 +63,30 @@ public async Task DeleteImageAsync_RemovesImage()
     {
         var newImageTag = Guid.NewGuid().ToString();
 
-        await _dockerClient.Images.TagImageAsync(
-            $"{_repositoryName}:{_tag}",
+        await _testFixture.DockerClient.Images.TagImageAsync(
+            $"{_testFixture.Repository}:{_testFixture.Tag}",
             new ImageTagParameters
             {
-                RepositoryName = _repositoryName,
+                RepositoryName = _testFixture.Repository,
                 Tag = newImageTag
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        var inspectExistingImageResponse = await _dockerClient.Images.InspectImageAsync(
-            $"{_repositoryName}:{newImageTag}",
-            _cts.Token
+        var inspectExistingImageResponse = await _testFixture.DockerClient.Images.InspectImageAsync(
+            $"{_testFixture.Repository}:{newImageTag}",
+            _testFixture.Cts.Token
         );
 
-        await _dockerClient.Images.DeleteImageAsync(
-            $"{_repositoryName}:{newImageTag}",
+        await _testFixture.DockerClient.Images.DeleteImageAsync(
+            $"{_testFixture.Repository}:{newImageTag}",
             new ImageDeleteParameters(),
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        Task inspectDeletedImageTask = _dockerClient.Images.InspectImageAsync(
-            $"{_repositoryName}:{newImageTag}",
-            _cts.Token
+        Task inspectDeletedImageTask = _testFixture.DockerClient.Images.InspectImageAsync(
+            $"{_testFixture.Repository}:{newImageTag}",
+            _testFixture.Cts.Token
         );
 
         Assert.NotNull(inspectExistingImageResponse);
diff --git a/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs b/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs
index ce25fb04..e620446c 100644
--- a/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs
+++ b/test/Docker.DotNet.Tests/ISwarmOperationsTests.cs
@@ -3,20 +3,13 @@ namespace Docker.DotNet.Tests;
 [Collection(nameof(TestCollection))]
 public class ISwarmOperationsTests
 {
-    private readonly CancellationTokenSource _cts;
+    private readonly TestFixture _testFixture;
+    private readonly ITestOutputHelper _testOutputHelper;
 
-    private readonly DockerClient _dockerClient;
-    private readonly string _imageId;
-
-    public ISwarmOperationsTests(TestFixture testFixture)
+    public ISwarmOperationsTests(TestFixture testFixture, ITestOutputHelper testOutputHelper)
     {
-        // Do not wait forever in case it gets stuck
-        _cts = CancellationTokenSource.CreateLinkedTokenSource(testFixture.Cts.Token);
-        _cts.CancelAfter(TimeSpan.FromMinutes(5));
-        _cts.Token.Register(() => throw new TimeoutException("SwarmOperationTests timeout"));
-
-        _dockerClient = testFixture.DockerClient;
-        _imageId = testFixture.Image.ID;
+        _testFixture = testFixture;
+        _testOutputHelper = testOutputHelper;
     }
 
     [Fact]
@@ -24,34 +17,34 @@ public async Task GetFilteredServicesByName_Succeeds()
     {
         var serviceName = $"service1-{Guid.NewGuid().ToString().Substring(1, 10)}";
 
-        var firstServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var firstServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = serviceName,
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var secondServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var secondServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service2-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var thirdServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var thirdServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service3-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var services = await _dockerClient.Swarm.ListServicesAsync(new ServiceListParameters
+        var services = await _testFixture.DockerClient.Swarm.ListServicesAsync(new ServiceListParameters
         {
             Filters = new Dictionary<string, IDictionary<string, bool>>
             {
@@ -64,42 +57,42 @@ public async Task GetFilteredServicesByName_Succeeds()
 
         Assert.Single(services);
 
-        await _dockerClient.Swarm.RemoveServiceAsync(firstServiceId);
-        await _dockerClient.Swarm.RemoveServiceAsync(secondServiceId);
-        await _dockerClient.Swarm.RemoveServiceAsync(thirdServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId);
     }
 
     [Fact]
     public async Task GetFilteredServicesById_Succeeds()
     {
-        var firstServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var firstServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service1-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var secondServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var secondServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service2-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var thirdServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var thirdServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service3-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var services = await _dockerClient.Swarm.ListServicesAsync(new ServiceListParameters
+        var services = await _testFixture.DockerClient.Swarm.ListServicesAsync(new ServiceListParameters
         {
             Filters = new Dictionary<string, IDictionary<string, bool>>
             {
@@ -112,69 +105,69 @@ public async Task GetFilteredServicesById_Succeeds()
 
         Assert.Single(services);
 
-        await _dockerClient.Swarm.RemoveServiceAsync(firstServiceId);
-        await _dockerClient.Swarm.RemoveServiceAsync(secondServiceId);
-        await _dockerClient.Swarm.RemoveServiceAsync(thirdServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId);
     }
 
     [Fact]
     public async Task GetServices_Succeeds()
     {
-        var initialServiceCount = (await _dockerClient.Swarm.ListServicesAsync(cancellationToken: CancellationToken.None)).Count();
+        var initialServiceCount = (await _testFixture.DockerClient.Swarm.ListServicesAsync(cancellationToken: CancellationToken.None)).Count();
 
-        var firstServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var firstServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service1-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var secondServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var secondServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service2-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var thirdServiceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var thirdServiceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = $"service3-{Guid.NewGuid().ToString().Substring(1, 10)}",
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID } }
             }
         })).ID;
 
-        var services = await _dockerClient.Swarm.ListServicesAsync(cancellationToken: CancellationToken.None);
+        var services = await _testFixture.DockerClient.Swarm.ListServicesAsync(cancellationToken: CancellationToken.None);
 
         Assert.True(services.Count() > initialServiceCount);
 
-        await _dockerClient.Swarm.RemoveServiceAsync(firstServiceId);
-        await _dockerClient.Swarm.RemoveServiceAsync(secondServiceId);
-        await _dockerClient.Swarm.RemoveServiceAsync(thirdServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(firstServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(secondServiceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(thirdServiceId);
     }
 
     [Fact]
     public async Task GetServiceLogs_Succeeds()
     {
         var cts = new CancellationTokenSource();
-        var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token, cts.Token);
+        var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token, cts.Token);
 
         var serviceName = $"service-withLogs-{Guid.NewGuid().ToString().Substring(1, 10)}";
-        var serviceId = (await _dockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
+        var serviceId = (await _testFixture.DockerClient.Swarm.CreateServiceAsync(new ServiceCreateParameters
         {
             Service = new ServiceSpec
             {
                 Name = serviceName,
-                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
+                TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _testFixture.Image.ID, Command = CommonCommands.EchoToStdoutAndStderr } }
             }
         })).ID;
 
-        var stream = await _dockerClient.Swarm.GetServiceLogsAsync(serviceName, false, new ServiceLogsParameters
+        using var stream = await _testFixture.DockerClient.Swarm.GetServiceLogsAsync(serviceName, false, new ServiceLogsParameters
         {
             Follow = true,
             ShowStdout = true,
@@ -231,12 +224,12 @@ public async Task GetServiceLogs_Succeeds()
 
                     // Reset the CancellationTokenSource for the next attempt
                     cts = new CancellationTokenSource();
-                    linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token, cts.Token);
+                    linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token, cts.Token);
                     cts.CancelAfter(delay);
                 }
             }
 
-            if (logLines.Any() && logLines.First().Contains("[INF]"))
+            if (logLines.Any())
             {
                 break;
             }
@@ -252,8 +245,7 @@ public async Task GetServiceLogs_Succeeds()
 
         Assert.NotNull(logLines);
         Assert.NotEmpty(logLines);
-        Assert.Contains("[INF]", logLines.First());
 
-        await _dockerClient.Swarm.RemoveServiceAsync(serviceId);
+        await _testFixture.DockerClient.Swarm.RemoveServiceAsync(serviceId);
     }
 }
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs b/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
index 5e1f814b..2857647d 100644
--- a/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
+++ b/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
@@ -3,46 +3,33 @@ namespace Docker.DotNet.Tests;
 [Collection(nameof(TestCollection))]
 public class ISystemOperationsTests
 {
-    private readonly CancellationTokenSource _cts;
+    private readonly TestFixture _testFixture;
+    private readonly ITestOutputHelper _testOutputHelper;
 
-    private readonly TestOutput _output;
-    private readonly string _repositoryName;
-    private readonly string _tag;
-    private readonly DockerClient _dockerClient;
-
-    public ISystemOperationsTests(TestFixture testFixture, ITestOutputHelper outputHelper)
+    public ISystemOperationsTests(TestFixture testFixture, ITestOutputHelper testOutputHelper)
     {
-        _output = new TestOutput(outputHelper);
-
-        _dockerClient = testFixture.DockerClient;
-
-        // Do not wait forever in case it gets stuck
-        _cts = CancellationTokenSource.CreateLinkedTokenSource(testFixture.Cts.Token);
-        _cts.CancelAfter(TimeSpan.FromMinutes(5));
-        _cts.Token.Register(() => throw new TimeoutException("SystemOperationsTests timeout"));
-
-        _repositoryName = testFixture.Repository;
-        _tag = testFixture.Tag;
+        _testFixture = testFixture;
+        _testOutputHelper = testOutputHelper;
     }
 
     [Fact]
     public void Docker_IsRunning()
     {
         var dockerProcess = Process.GetProcesses().FirstOrDefault(process => process.ProcessName.Equals("docker", StringComparison.InvariantCultureIgnoreCase) || process.ProcessName.Equals("dockerd", StringComparison.InvariantCultureIgnoreCase));
-        Assert.NotNull(dockerProcess); // docker is not running
+        Assert.NotNull(dockerProcess);
     }
 
     [Fact]
     public async Task GetSystemInfoAsync_Succeeds()
     {
-        var info = await _dockerClient.System.GetSystemInfoAsync();
+        var info = await _testFixture.DockerClient.System.GetSystemInfoAsync();
         Assert.NotNull(info.Architecture);
     }
 
     [Fact]
     public async Task GetVersionAsync_Succeeds()
     {
-        var version = await _dockerClient.System.GetVersionAsync();
+        var version = await _testFixture.DockerClient.System.GetVersionAsync();
         Assert.NotNull(version.APIVersion);
     }
 
@@ -55,20 +42,20 @@ public async Task MonitorEventsAsync_EmptyContainersList_CanBeCancelled()
         await cts.CancelAsync();
         await Task.Delay(1);
 
-        await Assert.ThrowsAsync<TaskCanceledException>(() => _dockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), progress, cts.Token));
+        await Assert.ThrowsAsync<TaskCanceledException>(() => _testFixture.DockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), progress, cts.Token));
 
     }
 
     [Fact]
     public async Task MonitorEventsAsync_NullParameters_Throws()
     {
-        await Assert.ThrowsAsync<ArgumentNullException>(() => _dockerClient.System.MonitorEventsAsync(null, null));
+        await Assert.ThrowsAsync<ArgumentNullException>(() => _testFixture.DockerClient.System.MonitorEventsAsync(null, null));
     }
 
     [Fact]
     public async Task MonitorEventsAsync_NullProgress_Throws()
     {
-        await Assert.ThrowsAsync<ArgumentNullException>(() => _dockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), null));
+        await Assert.ThrowsAsync<ArgumentNullException>(() => _testFixture.DockerClient.System.MonitorEventsAsync(new ContainerEventsParameters(), null));
     }
 
     [Fact]
@@ -78,29 +65,29 @@ public async Task MonitorEventsAsync_Succeeds()
 
         var wasProgressCalled = false;
 
-        var progressMessage = new Progress<Message>((m) =>
+        var progressMessage = new Progress<Message>(m =>
         {
-            _output.WriteLine($"MonitorEventsAsync_Succeeds: Message - {m.Action} - {m.Status} {m.From} - {m.Type}");
+            _testOutputHelper.WriteLine($"MonitorEventsAsync_Succeeds: Message - {m.Action} - {m.Status} {m.From} - {m.Type}");
             wasProgressCalled = true;
             Assert.NotNull(m);
         });
 
-        using var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
 
-        var task = _dockerClient.System.MonitorEventsAsync(
+        var task = _testFixture.DockerClient.System.MonitorEventsAsync(
             new ContainerEventsParameters(),
             progressMessage,
             cts.Token);
 
-        await _dockerClient.Images.TagImageAsync($"{_repositoryName}:{_tag}", new ImageTagParameters { RepositoryName = _repositoryName, Tag = newTag }, _cts.Token);
+        await _testFixture.DockerClient.Images.TagImageAsync($"{_testFixture.Repository}:{_testFixture.Tag}", new ImageTagParameters { RepositoryName = _testFixture.Repository, Tag = newTag }, _testFixture.Cts.Token);
 
-        await _dockerClient.Images.DeleteImageAsync(
-            name: $"{_repositoryName}:{newTag}",
+        await _testFixture.DockerClient.Images.DeleteImageAsync(
+            name: $"{_testFixture.Repository}:{newTag}",
             new ImageDeleteParameters
             {
                 Force = true
             },
-            _cts.Token);
+            _testFixture.Cts.Token);
 
         // Give it some time for output operation to complete before cancelling task
         await Task.Delay(TimeSpan.FromSeconds(1));
@@ -123,24 +110,24 @@ public async Task MonitorEventsAsync_IsCancelled_NoStreamCorruption()
             try
             {
                 // (1) Create monitor task
-                using var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
+                using var cts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
 
                 string newImageTag = Guid.NewGuid().ToString();
 
-                var monitorTask = _dockerClient.System.MonitorEventsAsync(
+                var monitorTask = _testFixture.DockerClient.System.MonitorEventsAsync(
                     new ContainerEventsParameters(),
-                    new Progress<Message>((value) => _output.WriteLine($"DockerSystemEvent: {JsonSerializer.Instance.Serialize(value)}")),
+                    new Progress<Message>(value => _testOutputHelper.WriteLine($"DockerSystemEvent: {JsonSerializer.Instance.Serialize(value)}")),
                     cts.Token);
 
                 // (2) Wait for some time to make sure we get into blocking IO call
                 await Task.Delay(100, CancellationToken.None);
 
                 // (3) Invoke another request that will attempt to grab the same buffer
-                var listImagesTask1 = _dockerClient.Images.TagImageAsync(
-                    $"{_repositoryName}:{_tag}",
+                var listImagesTask1 = _testFixture.DockerClient.Images.TagImageAsync(
+                    $"{_testFixture.Repository}:{_testFixture.Tag}",
                     new ImageTagParameters
                     {
-                        RepositoryName = _repositoryName,
+                        RepositoryName = _testFixture.Repository,
                         Tag = newImageTag,
                     }, CancellationToken.None);
 
@@ -153,17 +140,17 @@ public async Task MonitorEventsAsync_IsCancelled_NoStreamCorruption()
                     // noop
                 }
 
-                _output.WriteLine($"Waited for {sw.Elapsed.TotalMilliseconds} ms");
+                _testOutputHelper.WriteLine($"Waited for {sw.Elapsed.TotalMilliseconds} ms");
 
                 await cts.CancelAsync();
 
                 await listImagesTask1;
 
-                await _dockerClient.Images.TagImageAsync(
-                    $"{_repositoryName}:{_tag}",
+                await _testFixture.DockerClient.Images.TagImageAsync(
+                    $"{_testFixture.Repository}:{_testFixture.Tag}",
                     new ImageTagParameters
                     {
-                        RepositoryName = _repositoryName,
+                        RepositoryName = _testFixture.Repository,
                         Tag = newImageTag,
                     }, CancellationToken.None);
 
@@ -180,31 +167,31 @@ await _dockerClient.Images.TagImageAsync(
     public async Task MonitorEventsFiltered_Succeeds()
     {
         string newTag = $"MonitorTests-{Guid.NewGuid().ToString().Substring(1, 10)}";
-        string newImageRespositoryName = Guid.NewGuid().ToString();
+        string newImageRepositoryName = Guid.NewGuid().ToString();
 
-        await _dockerClient.Images.TagImageAsync(
-            $"{_repositoryName}:{_tag}",
+        await _testFixture.DockerClient.Images.TagImageAsync(
+            $"{_testFixture.Repository}:{_testFixture.Tag}",
             new ImageTagParameters
             {
-                RepositoryName = newImageRespositoryName,
+                RepositoryName = newImageRepositoryName,
                 Tag = newTag
             },
-            _cts.Token
+            _testFixture.Cts.Token
         );
 
-        ImageInspectResponse image = await _dockerClient.Images.InspectImageAsync(
-            $"{newImageRespositoryName}:{newTag}",
-            _cts.Token
+        ImageInspectResponse image = await _testFixture.DockerClient.Images.InspectImageAsync(
+            $"{newImageRepositoryName}:{newTag}",
+            _testFixture.Cts.Token
         );
 
         var progressCalledCounter = 0;
 
-        var eventsParams = new ContainerEventsParameters()
+        var eventsParams = new ContainerEventsParameters
         {
-            Filters = new Dictionary<string, IDictionary<string, bool>>()
+            Filters = new Dictionary<string, IDictionary<string, bool>>
             {
                 {
-                    "event", new Dictionary<string, bool>()
+                    "event", new Dictionary<string, bool>
                     {
                         {
                             "tag", true
@@ -215,7 +202,7 @@ await _dockerClient.Images.TagImageAsync(
                     }
                 },
                 {
-                    "type", new Dictionary<string, bool>()
+                    "type", new Dictionary<string, bool>
                     {
                         {
                             "image", true
@@ -223,7 +210,7 @@ await _dockerClient.Images.TagImageAsync(
                     }
                 },
                 {
-                    "image", new Dictionary<string, bool>()
+                    "image", new Dictionary<string, bool>
                     {
                         {
                             image.ID, true
@@ -233,21 +220,21 @@ await _dockerClient.Images.TagImageAsync(
             }
         };
 
-        var progress = new Progress<Message>((m) =>
+        var progress = new Progress<Message>(m =>
         {
             Interlocked.Increment(ref progressCalledCounter);
             Assert.True(m.Status == "tag" || m.Status == "untag");
-            _output.WriteLine($"MonitorEventsFiltered_Succeeds: Message received: {m.Action} - {m.Status} {m.From} - {m.Type}");
+            _testOutputHelper.WriteLine($"MonitorEventsFiltered_Succeeds: Message received: {m.Action} - {m.Status} {m.From} - {m.Type}");
         });
 
-        using var cts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token);
-        var task = Task.Run(() => _dockerClient.System.MonitorEventsAsync(eventsParams, progress, cts.Token));
+        using var cts = CancellationTokenSource.CreateLinkedTokenSource(_testFixture.Cts.Token);
+        var task = Task.Run(() => _testFixture.DockerClient.System.MonitorEventsAsync(eventsParams, progress, cts.Token));
 
-        await _dockerClient.Images.TagImageAsync($"{_repositoryName}:{_tag}", new ImageTagParameters { RepositoryName = _repositoryName, Tag = newTag });
-        await _dockerClient.Images.DeleteImageAsync($"{_repositoryName}:{newTag}", new ImageDeleteParameters());
+        await _testFixture.DockerClient.Images.TagImageAsync($"{_testFixture.Repository}:{_testFixture.Tag}", new ImageTagParameters { RepositoryName = _testFixture.Repository, Tag = newTag });
+        await _testFixture.DockerClient.Images.DeleteImageAsync($"{_testFixture.Repository}:{newTag}", new ImageDeleteParameters());
 
-        var createContainerResponse = await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameters { Image = $"{_repositoryName}:{_tag}" });
-        await _dockerClient.Containers.RemoveContainerAsync(createContainerResponse.ID, new ContainerRemoveParameters(), cts.Token);
+        var createContainerResponse = await _testFixture.DockerClient.Containers.CreateContainerAsync(new CreateContainerParameters { Image = $"{_testFixture.Repository}:{_testFixture.Tag}", Entrypoint = CommonCommands.SleepInfinity });
+        await _testFixture.DockerClient.Containers.RemoveContainerAsync(createContainerResponse.ID, new ContainerRemoveParameters(), cts.Token);
 
         await Task.Delay(TimeSpan.FromSeconds(1));
         await cts.CancelAsync();
@@ -261,6 +248,6 @@ await _dockerClient.Images.TagImageAsync(
     [Fact]
     public async Task PingAsync_Succeeds()
     {
-        await _dockerClient.System.PingAsync();
+        await _testFixture.DockerClient.System.PingAsync();
     }
 }
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/IVolumeOperationsTests.cs b/test/Docker.DotNet.Tests/IVolumeOperationsTests.cs
index f1ed7d4c..e77ad7d1 100644
--- a/test/Docker.DotNet.Tests/IVolumeOperationsTests.cs
+++ b/test/Docker.DotNet.Tests/IVolumeOperationsTests.cs
@@ -3,46 +3,39 @@ namespace Docker.DotNet.Tests;
 [Collection(nameof(TestCollection))]
 public class IVolumeOperationsTests
 {
-	private readonly CancellationTokenSource _cts;
-
-	private readonly DockerClient _dockerClient;
-
-	public IVolumeOperationsTests(TestFixture testFixture)
-	{
-		_dockerClient = testFixture.DockerClient;
-
-		// Do not wait forever in case it gets stuck
-		_cts = CancellationTokenSource.CreateLinkedTokenSource(testFixture.Cts.Token);
-		_cts.CancelAfter(TimeSpan.FromMinutes(5));
-		_cts.Token.Register(() => throw new TimeoutException("VolumeOperationsTests timeout"));
-	}
-
-	[Fact]
-	public async Task ListAsync_VolumeExists_Succeeds()
-	{
-		const string volumeName = "docker-dotnet-test-volume";
-
-		await _dockerClient.Volumes.CreateAsync(new VolumesCreateParameters
-			{
-				Name = volumeName,
-			},
-			_cts.Token);
-
-		try
-		{
-
-			var response = await _dockerClient.Volumes.ListAsync(new VolumesListParameters()
-				{
-					Filters = new Dictionary<string, IDictionary<string, bool>>(),
-				},
-				_cts.Token);
-
-			Assert.Contains(volumeName, response.Volumes.Select(volume => volume.Name));
-
-		}
-		finally
-		{
-			await _dockerClient.Volumes.RemoveAsync(volumeName, force: true, _cts.Token);
-		}
-	}
+    private readonly TestFixture _testFixture;
+    private readonly ITestOutputHelper _testOutputHelper;
+
+    public IVolumeOperationsTests(TestFixture testFixture, ITestOutputHelper testOutputHelper)
+    {
+        _testFixture = testFixture;
+        _testOutputHelper = testOutputHelper;
+    }
+
+    [Fact]
+    public async Task ListAsync_VolumeExists_Succeeds()
+    {
+        const string volumeName = "docker-dotnet-test-volume";
+
+        await _testFixture.DockerClient.Volumes.CreateAsync(new VolumesCreateParameters
+            {
+                Name = volumeName,
+            },
+            _testFixture.Cts.Token);
+
+        try
+        {
+            var response = await _testFixture.DockerClient.Volumes.ListAsync(new VolumesListParameters
+                {
+                    Filters = new Dictionary<string, IDictionary<string, bool>>(),
+                },
+                _testFixture.Cts.Token);
+
+            Assert.Contains(volumeName, response.Volumes.Select(volume => volume.Name));
+        }
+        finally
+        {
+            await _testFixture.DockerClient.Volumes.RemoveAsync(volumeName, force: true, _testFixture.Cts.Token);
+        }
+    }
 }
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/TestFixture.cs b/test/Docker.DotNet.Tests/TestFixture.cs
index 9ae79c6b..8ae4634b 100644
--- a/test/Docker.DotNet.Tests/TestFixture.cs
+++ b/test/Docker.DotNet.Tests/TestFixture.cs
@@ -1,36 +1,30 @@
 namespace Docker.DotNet.Tests;
 
-public sealed class TestFixture : IAsyncLifetime, IDisposable
+[CollectionDefinition(nameof(TestCollection))]
+public sealed class TestCollection : ICollectionFixture<TestFixture>;
+
+public sealed class TestFixture : Progress<JSONMessage>, IAsyncLifetime, IDisposable, ILogger
 {
-    /// <summary>
-    /// The Docker image name.
-    /// </summary>
-    private const string Name = "nats";
+    private const LogLevel MinLogLevel = LogLevel.Debug;
 
-    private static readonly Progress<JSONMessage> WriteProgressOutput;
+    private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
 
-    private bool _hasInitializedSwarm;
+    private readonly IMessageSink _messageSink;
 
-    static TestFixture()
-    {
-        WriteProgressOutput = new Progress<JSONMessage>(jsonMessage =>
-        {
-            var message = JsonSerializer.Instance.Serialize(jsonMessage);
-            Console.WriteLine(message);
-            Debug.WriteLine(message);
-        });
-    }
+    private bool _hasInitializedSwarm;
 
     /// <summary>
     /// Initializes a new instance of the <see cref="TestFixture" /> class.
     /// </summary>
-    /// <exception cref="TimeoutException">Thrown when tests are not finished within 5 minutes.</exception>
-    public TestFixture()
+    /// <param name="messageSink">The message sink.</param>
+    /// <exception cref="TimeoutException">Thrown when tests are not completed within 5 minutes.</exception>
+    public TestFixture(IMessageSink messageSink)
     {
+        _messageSink = messageSink;
         DockerClientConfiguration = new DockerClientConfiguration();
-        DockerClient = DockerClientConfiguration.CreateClient();
+        DockerClient = DockerClientConfiguration.CreateClient(logger: this);
         Cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
-        Cts.Token.Register(() => throw new TimeoutException("Docker.DotNet test timeout exception"));
+        Cts.Token.Register(() => throw new TimeoutException("Docker.DotNet tests timed out."));
     }
 
     /// <summary>
@@ -46,14 +40,14 @@ public TestFixture()
         = Guid.NewGuid().ToString("N");
 
     /// <summary>
-    /// Gets the Docker client.
+    /// Gets the Docker client configuration.
     /// </summary>
-    public DockerClient DockerClient { get; }
+    public DockerClientConfiguration DockerClientConfiguration { get; }
 
     /// <summary>
-    /// Gets the Docker client configuration.
+    /// Gets the Docker client.
     /// </summary>
-    public DockerClientConfiguration DockerClientConfiguration { get; }
+    public DockerClient DockerClient { get; }
 
     /// <summary>
     /// Gets the cancellation token source.
@@ -68,8 +62,12 @@ public TestFixture()
     /// <inheritdoc />
     public async Task InitializeAsync()
     {
+        const string repository = "alpine";
+
+        const string tag = "3.20";
+
         // Create image
-        await DockerClient.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = Name, Tag = "latest" }, null, WriteProgressOutput, Cts.Token)
+        await DockerClient.Images.CreateImageAsync(new ImagesCreateParameters { FromImage = repository, Tag = tag }, null, this, Cts.Token)
             .ConfigureAwait(false);
 
         // Get images
@@ -80,7 +78,7 @@ await DockerClient.Images.CreateImageAsync(new ImagesCreateParameters { FromImag
                     {
                         ["reference"] = new Dictionary<string, bool>
                         {
-                            [Name] = true
+                            [repository + ":" + tag] = true
                         }
                     }
                 }, Cts.Token)
@@ -103,9 +101,7 @@ await DockerClient.Images.CreateImageAsync(new ImagesCreateParameters { FromImag
         }
         catch
         {
-            const string message = "Couldn't init a new swarm, the node should take part of an existing one.";
-            Console.WriteLine(message);
-            Debug.WriteLine(message);
+            this.LogDebug("Couldn't init a new swarm, the node should take part of an existing one.");
 
             _hasInitializedSwarm = false;
         }
@@ -141,7 +137,7 @@ await DockerClient.Swarm.LeaveSwarmAsync(new SwarmLeaveParameters { Force = true
                     {
                         ["reference"] = new Dictionary<string, bool>
                         {
-                            [Image.RepoDigests.Single()] = true
+                            [Image.ID] = true
                         }
                     },
                     All = true
@@ -164,13 +160,44 @@ await DockerClient.Swarm.LeaveSwarmAsync(new SwarmLeaveParameters { Force = true
     /// <inheritdoc />
     public void Dispose()
     {
+        Cts.Dispose();
         DockerClient.Dispose();
         DockerClientConfiguration.Dispose();
-        Cts.Dispose();
     }
-}
 
-[CollectionDefinition(nameof(TestCollection))]
-public sealed class TestCollection : ICollectionFixture<TestFixture>
-{
+    /// <inheritdoc />
+    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
+    {
+        if (IsEnabled(logLevel))
+        {
+            var message = exception == null ? formatter.Invoke(state, null) : string.Join(Environment.NewLine, formatter.Invoke(state, exception), exception);
+            _messageSink.OnMessage(new DiagnosticMessage(string.Format("[Docker.DotNet {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, message)));
+        }
+    }
+
+    /// <inheritdoc />
+    public bool IsEnabled(LogLevel logLevel)
+    {
+        return logLevel >= MinLogLevel;
+    }
+
+    /// <inheritdoc />
+    public IDisposable BeginScope<TState>(TState state) where TState : notnull
+    {
+        return new Disposable();
+    }
+
+    /// <inheritdoc />
+    protected override void OnReport(JSONMessage value)
+    {
+        var message = JsonSerializer.Instance.Serialize(value);
+        this.LogDebug("Progress: '{Progress}'.", message);
+    }
+
+    private sealed class Disposable : IDisposable
+    {
+        public void Dispose()
+        {
+        }
+    }
 }
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/TestOutput.cs b/test/Docker.DotNet.Tests/TestOutput.cs
deleted file mode 100644
index 57e3f244..00000000
--- a/test/Docker.DotNet.Tests/TestOutput.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Docker.DotNet.Tests;
-
-public class TestOutput
-{
-    private readonly ITestOutputHelper _outputHelper;
-
-    public TestOutput(ITestOutputHelper outputHelper)
-    {
-        _outputHelper = outputHelper;
-    }
-
-    public void WriteLine(string line)
-    {
-        Console.WriteLine(line);
-        _outputHelper.WriteLine(line);
-        System.Diagnostics.Debug.WriteLine(line);
-    }
-}
\ No newline at end of file
diff --git a/test/Docker.DotNet.Tests/xunit.runner.json b/test/Docker.DotNet.Tests/xunit.runner.json
new file mode 100644
index 00000000..0e2fef59
--- /dev/null
+++ b/test/Docker.DotNet.Tests/xunit.runner.json
@@ -0,0 +1,4 @@
+{
+  "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
+  "diagnosticMessages" : true
+}
\ No newline at end of file