Migrating to FluentDocker v3.0.0

This guide helps you migrate from v2.x.x to v3.0.0.

Step by Step

Breaking Changes Summary

Change Impact Action
Namespace: Ductus.FluentDockerFluentDocker HIGH Update using statements
Builder API: lambda + WithinDriver() scoping HIGH Rewrite builder code
Build() returns BuildResults HIGH Access services from results
Docker Machine removed HIGH Use Docker Contexts
Docker Toolbox removed HIGH Use Docker Desktop
Commands namespace removed HIGH Use Driver Layer
Compose: struct-based arguments MEDIUM Update Compose calls
Legacy test packages removed HIGH Use FluentDocker.Testing.* adapters (details)
FluentDockerTestBase base class removed HIGH Use XunitContainerFixture / MsTestResourceHelpers / NUnitResourceHelpers (or generic XunitResourceFixture<T> / CreateResourceAsync<T>)
xUnit v3: IAsyncLifetime returns ValueTask MEDIUM Update TaskValueTask

Step 1: Update NuGet Packages

<!-- OLD -->
<PackageReference Include="Ductus.FluentDocker" Version="2.*" />

<!-- NEW -->
<PackageReference Include="FluentDocker" Version="3.*" />

Step 2: Update Namespaces

// OLD
using Ductus.FluentDocker.Builders;
using Ductus.FluentDocker.Services;
using Ductus.FluentDocker.Model.Common;

// NEW
using FluentDocker.Builders;
using FluentDocker.Services;
using FluentDocker.Kernel;

Automated fix:

# Linux/macOS
find . -name "*.cs" -exec sed -i '' 's/Ductus\.FluentDocker/FluentDocker/g' {} \;

# Windows PowerShell
Get-ChildItem -Recurse -Filter *.cs | ForEach-Object {
    (Get-Content $_.FullName) -replace 'Ductus\.FluentDocker', 'FluentDocker' | Set-Content $_.FullName
}

Step 3: Create a Kernel

v3 requires a kernel with a registered driver before building containers.

// NEW - Required kernel setup (multiple kernels per app/test session are supported)
using var kernel = FluentDockerKernel.Create()
    .WithDockerCli("docker", d => d.AsDefault())
    .Build();

// Async variant
using var kernel = await FluentDockerKernel.Create()
    .WithDockerCli("docker", d => d.AsDefault())
    .BuildAsync();

Step 4: Update Builder API

Container Builder

// OLD
using var container = new Builder()
    .UseContainer()
    .UseImage("nginx:alpine")
    .ExposePort(80)
    .WaitForPort("80/tcp", 30000)
    .Build()
    .Start();

// NEW
using var results = new Builder()
    .WithinDriver("docker", kernel)
    .UseContainer(c => c
        .UseImage("nginx:alpine")
        .ExposePort("80")
        .WaitForPort("80/tcp", 30000))
    .Build();

var container = results.Containers.First();

Network Builder

// OLD
using var network = new Builder()
    .UseNetwork("my-network")
    .UseSubnet("10.18.0.0/16")
    .Build();

// NEW
using var nwResults = new Builder()
    .WithinDriver("docker", kernel)
    .UseNetwork(n => n
        .WithName("my-network")
        .WithSubnet("10.18.0.0/16"))
    .Build();

var network = nwResults.Networks.First();

Volume Builder

// OLD
using var vol = new Builder()
    .UseVolume("my-data")
    .Build();

// NEW
using var volResults = new Builder()
    .WithinDriver("docker", kernel)
    .UseVolume(v => v
        .WithName("my-data"))
    .Build();

var volume = volResults.Volumes.First();

Compose Builder

// OLD
using var svc = new Builder()
    .UseContainer()
    .UseCompose()
    .FromFile("docker-compose.yml")
    .RemoveOrphans()
    .WaitForHttp("web", "http://localhost:8000/health")
    .Build()
    .Start();

// NEW
using var results = new Builder()
    .WithinDriver("docker", kernel)
    .UseCompose(c => c
        .WithComposeFile("docker-compose.yml")
        .WithRemoveOrphans()
        .WithWait()
        .WithWaitTimeout(30))
    .Build();

Image Builder

// OLD
using var img = new Builder()
    .DefineImage("myapp:latest")
    .From("node:18-alpine")
    .Run("npm install")
    .ExposePorts(8080)
    .Command("node", "app.js")
    .Build();

// NEW
using var imgResults = new Builder()
    .WithinDriver("docker", kernel)
    .UseImage("myapp:latest", img => img
        .From("node:18-alpine")
        .Run("npm install")
        .ExposePorts(8080)
        .Command("node", "app.js"))
    .Build();

Step 5: Update Test Base Classes

The legacy FluentDockerTestBase (xUnit) and FluentDockerTestBase (MSTest) base classes have been removed. Use the new adapter packages instead.

xUnit — XunitContainerFixture

// OLD
public class RedisFixture : FluentDockerTestBase
{
    protected override ContainerBuilder Build()
        => new Builder().UseContainer().UseImage("redis:alpine")
            .ExposePort(6379).WaitForPort("6379/tcp", 30000);
}

// NEW
public class RedisFixture : XunitContainerFixture
{
    public RedisFixture()
    {
        InitializeAsync(builder => builder
            .UseImage("redis:alpine")
            .ExposePort("6379")
            .WaitForPort("6379/tcp", 30000)
        ).GetAwaiter().GetResult();
    }
}

Tip: For new code, prefer the Configure(...) pattern shown in docs/testing/xunit.md instead of the sync-over-async constructor — it avoids deadlock risk in some sync contexts.

MSTest — MsTestResourceHelpers

// OLD
[TestClass]
public class RedisTests : FluentDockerTestBase
{
    protected override ContainerBuilder Build()
        => new Builder().UseContainer().UseImage("redis:alpine")
            .ExposePort(6379).WaitForPort("6379/tcp", 30000);
}

// NEW
[TestClass]
public class RedisTests
{
    private static FluentDockerKernel _kernel;
    private static ContainerResource _resource;

    [ClassInitialize]
    public static async Task ClassInit(TestContext ctx)
    {
        (_kernel, _resource) = await MsTestResourceHelpers.CreateContainerAsync(
            b => b.UseImage("redis:alpine").ExposePort("6379")
                  .WaitForPort("6379/tcp", 30000));
    }

    [ClassCleanup]
    public static async Task ClassCleanup()
        => await MsTestResourceHelpers.DisposeAsync(_resource, _kernel);
}

See Test Migration Guide for NUnit, Compose, and collection fixture examples.

Step 6: Remove Docker Machine Code

Docker Machine was deprecated by Docker and has been removed from v3.

// OLD - Remove this code
var machines = new Hosts().Discover();
var machine = machines.First(x => x.Name == "default");

// NEW - Create kernel and use WithinDriver
using var kernel = FluentDockerKernel.Create()
    .WithDockerCli("docker", d => d.AsDefault())
    .Build();

using var results = new Builder()
    .WithinDriver("docker", kernel)
    .UseContainer(c => c
        .UseImage("nginx:alpine"))
    .Build();

Step 7: Update Compose Commands

Compose commands now use struct-based arguments:

// OLD
host.ComposeBuild(altProjectName: "myproject", forceRm: true);
host.ComposeUp(composeFile: "docker-compose.yml", forceRecreate: true);
host.ComposeDown(removeOrphans: true, removeVolumes: true);

// NEW
await composeDriver.BuildAsync(context, new ComposeBuildConfig {
    ProjectName = "myproject",
    ForceRm = true
});

await composeDriver.UpAsync(context, new ComposeUpConfig {
    ComposeFiles = new List<string> { "docker-compose.yml" },
    ForceRecreate = true
});

await composeDriver.DownAsync(context, new ComposeDownConfig {
    RemoveOrphans = true,
    RemoveVolumes = true
});

Step 8: Switch to Microsoft.Extensions.Logging

BREAKING CHANGE in v3.0.0 — the static Logging.Enabled() / Logging.Disabled() toggle and the FluentDocker.Common.Logger static class are removed entirely. FluentDocker now logs through Microsoft.Extensions.Logging.Abstractions, and an ILoggerFactory is a required constructor argument on KernelBuilder and FluentDockerKernel.Create.

The compiler enforces this: any code that calls FluentDockerKernel.Create() or constructs KernelBuilder / FluentDockerKernel / DriverRegistry without supplying a factory fails to compile.

// OLD (v2)
using Ductus.FluentDocker.Services;
Logging.Enabled();
Logging.Disabled();

// NEW (v3) — supply an ILoggerFactory to the kernel builder
using Microsoft.Extensions.Logging;
using FluentDocker.Kernel;

using var factory = LoggerFactory.Create(b => b.AddConsole());
var kernel = await FluentDockerKernel.Create(factory)
    .WithDockerCli("docker", d => d.AsDefault())
    .BuildAsync();

To suppress all logging (equivalent to v2’s Logging.Disabled()), pass NullLoggerFactory.Instance explicitly:

using Microsoft.Extensions.Logging.Abstractions;

var silentKernel = await FluentDockerKernel.Create(NullLoggerFactory.Instance)
    .WithDockerCli("docker", d => d.AsDefault())
    .BuildAsync();

The factory is automatically propagated through DriverContext.LoggerFactory to all driver packs and component drivers, so third-party IDriverPack implementations receive it without any interface change. Each FluentDocker type uses its FQN as its log category for fine-grained filtering — see Utilities → Logging for details.

Removed Features

Docker Machine

  • All IMachineDriver APIs removed
  • Use Docker Contexts: docker context create/use

Docker Toolbox

  • DOCKER_TOOLBOX_INSTALL_PATH detection removed
  • Use Docker Desktop for Windows/Mac

docker-compose Binary

  • v3 uses docker compose (Compose V2) automatically
  • The standalone docker-compose binary is no longer supported

Commands Namespace

  • Replaced by Driver Layer architecture
  • Commands are now internal implementation details

New Features in v3.0.0

Container Stats

var stats = await container.GetStatsAsync();
Console.WriteLine($"CPU: {stats.Cpu.UsagePercent:F2}%");
Console.WriteLine($"Memory: {stats.Memory.Usage} bytes");

Directory Copy

// Copy entire directory to container
await container.CopyToAsync("/local/dir/", "/container/dir/");

// Copy from container
await container.CopyFromToPathAsync("/container/logs/", "/local/logs/");

Static IPv4/IPv6

using var nwResults = new Builder()
    .WithinDriver("docker", kernel)
    .UseNetwork(n => n
        .WithName("mynet")
        .WithSubnet("10.10.0.0/16"))
    .Build();

using var cResults = new Builder()
    .WithinDriver("docker", kernel)
    .UseContainer(c => c
        .UseImage("nginx:alpine")
        .WithNetwork("mynet")
        .WithIPv4("10.10.0.100")
        .WithIPv6("2001:db8::100"))
    .Build();

Full Async/Await

// All service operations are now async
await container.StartAsync();
await container.StopAsync();
await container.ExecuteAsync("echo hello");
var stats = await container.GetStatsAsync();

Migration Checklist

  • Update NuGet packages
  • Find & replace namespaces
  • Add kernel creation
  • Rewrite builder code to lambda + WithinDriver pattern
  • Replace test base classes with adapter packages (see Step 5)
  • Remove Docker Machine code
  • Remove Docker Toolbox code
  • Update Compose commands to struct-based
  • Update logging configuration
  • Run tests to verify
  • Update CI/CD pipelines

Detailed Migration Resources

For in-depth migration guidance, see these companion documents:

Getting Help


Table of contents