Skip to content

mariotoffia/FluentDocker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

865 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FluentDocker

CI codecov Release License .NET

Package NuGet Downloads
FluentDocker NuGet Downloads
Testing.Xunit NuGet Downloads
Testing.MsTest NuGet Downloads
Testing.NUnit NuGet Downloads

Quick Start

using System.Linq;
using FluentDocker.Builders;
using FluentDocker.Drivers.Podman;
using FluentDocker.Kernel;
using FluentDocker.Model.Drivers;

// Multiple kernels per app are supported.
// This kernel registers both Docker CLI and Podman CLI.
using var kernel = await FluentDockerKernel.Create()
    .WithDockerCli("docker", d => d.AsDefault())
    .WithPodmanCli("podman", d => d.WithAutoStartMachine())
    .BuildAsync();

1) Standard container (Docker CLI)

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

var endpoint = results.Containers.First()
    .ToHostExposedEndpoint("80/tcp");
Console.WriteLine($"Docker endpoint: {endpoint.Address}:{endpoint.Port}");

2) Standard container (Podman CLI)

await using var results = await new Builder()
    .WithinPodmanCli("podman", kernel)
    .UseContainer(c => c
        .UseImage("nginx:alpine")
        .ExposePort("80")
        .WaitForPort("80/tcp", 30000))
    .BuildAsync();

var endpoint = results.Containers.First()
    .ToHostExposedEndpoint("80/tcp");
Console.WriteLine($"Podman endpoint: {endpoint.Address}:{endpoint.Port}");

3) Docker Compose (Docker CLI)

await using var results = await new Builder()
    .WithinDockerCli("docker", kernel)
    .UseCompose(c => c
        .WithComposeFile("docker-compose.yml")
        .WithRemoveOrphans()
        .WithWait()
        .WithWaitTimeout(30))
    .BuildAsync();

var compose = results.ComposeServices.First();

4) Podman Kubernetes (kube play / kube down)

var context = new DriverContext("podman");
var kube = kernel.SysCtl<IPodmanKubernetesDriver>("podman");

await kube.PlayAsync(context, new KubePlayConfig
{
    YamlPath = "pod.yaml",
    Replace = true
});

// Teardown
await kube.DownAsync(context, "pod.yaml");

5) Test Support

FluentDocker supports xUnit, MSTest, and NUnit via adapter packages. See Test Support below for examples.

Installation

dotnet add package FluentDocker
dotnet add package FluentDocker.Testing.Xunit   # xUnit adapter
dotnet add package FluentDocker.Testing.MsTest  # MSTest adapter
dotnet add package FluentDocker.Testing.NUnit   # NUnit adapter

v3.0.0 is a major rewrite — multi-driver kernel, async-first API, Podman support, new test packages. See the 3.0.0 release notes and the migration guide for the full feature list and breaking changes.

Features

Container Management

// Create and start
using var results = new Builder()
    .WithinDockerCli("docker", kernel)
    .UseContainer(c => c
        .UseImage("nginx:latest")
        .ExposePort("80"))
    .Build();

var container = results.Containers.First();

// Get configuration
var config = container.GetConfiguration(true);

// Container stats (v3)
var stats = await container.GetStatsAsync();
Console.WriteLine($"CPU: {stats.CpuPercent:F2}%");

Port Mapping

// Explicit: host port 8080 → container port 80
.ExposePort(8080, 80)

// Random: let Docker choose host port
.ExposePort("80")

// Resolve actual endpoint
var endpoint = container.ToHostExposedEndpoint("80/tcp");

Wait Strategies

.WaitForPort("5432/tcp", 30000)           // Wait for port
.WaitForProcess("postgres", 30000)         // Wait for process
.WaitForLogMessage("ready", 30000)         // Wait for log message

Networks with Static IP

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

var network = nwResults.Networks.First();

using var cResults = new Builder()
    .WithinDockerCli("docker", kernel)
    .UseContainer(c => c
        .UseImage("nginx")
        .WithNetwork("my-network")
        .UseIpV4("10.18.0.100"))
    .Build();

Volume Mounts

// Host path mount
.WithVolume("/host/path", "/container/path")

// Named volume
.WithVolume("my-vol", "/data")

File Operations

// Copy to container
await container.CopyToAsync("/local/file", "/container/file");
await container.CopyToAsync("/local/dir", "/container/dir");  // v3: directories

// Copy from container
await container.CopyFromAsync("/container/file", "/local/file");

Image Building

// Inline Dockerfile
using var imgResults = new Builder()
    .WithinDockerCli("docker", kernel)
    .UseImage("mynode:latest", img => img
        .From("node:18-alpine")
        .Run("npm install -g nodemon")
        .ExposePorts(8080)
        .Command("node", "app.js"))
    .Build();

Drivers

FluentDocker ships with three drivers:

  • Docker CLI
  • Docker API
  • Podman CLI

All drivers share a common core (IContainerDriver, IImageDriver, INetworkDriver, IVolumeDriver, ISystemDriver, IAuthDriver, IStreamDriver) and add driver-specific capabilities on top.

Quick Driver Examples (Start Here)

Use these imports in the snippets below:

using System.Collections.Generic;
using System.Linq;
using FluentDocker.Builders;
using FluentDocker.Drivers;
using FluentDocker.Drivers.Podman;
using FluentDocker.Kernel;
using FluentDocker.Model.Drivers;

Example: create a kernel with both Docker CLI and Podman CLI (multiple kernels per app are also supported):

using var kernel = await FluentDockerKernel.Create()
    .WithDockerCli("docker", d => d.AsDefault())
    .WithPodmanCli("podman", d => d
        .WithAutoStartMachine() // macOS/Windows: ensure Podman VM is running
        .AsDefault())
    .BuildAsync();

1) Standard container (Docker CLI)

await using var dockerResults = await new Builder()
    .WithinDockerCli("docker", kernel)
    .UseContainer(c => c
        .UseImage("nginx:alpine")
        .ExposePort("80")
        .WaitForPort("80/tcp", 30000))
    .BuildAsync();

var dockerEndpoint = dockerResults.Containers.First()
    .ToHostExposedEndpoint("80/tcp");

2) Standard container (Podman CLI)

await using var podmanResults = await new Builder()
    .WithinPodmanCli("podman", kernel)
    .UseContainer(c => c
        .UseImage("nginx:alpine")
        .ExposePort("80")
        .WaitForPort("80/tcp", 30000))
    .BuildAsync();

var podmanEndpoint = podmanResults.Containers.First()
    .ToHostExposedEndpoint("80/tcp");

3) Docker Compose (Docker CLI)

await using var composeResults = await new Builder()
    .WithinDockerCli("docker", kernel)
    .UseCompose(c => c
        .WithComposeFile("docker-compose.yml")
        .WithRemoveOrphans()
        .WithWait())
    .BuildAsync();

var compose = composeResults.ComposeServices.First();

4) Kubernetes play/down (Podman CLI)

var podmanContext = new DriverContext("podman");
var kube = kernel.SysCtl<IPodmanKubernetesDriver>("podman");

var play = await kube.PlayAsync(podmanContext,
    new KubePlayConfig
    {
        YamlPath = "pod.yaml",
        Replace = true
    });

// Teardown when done
await kube.DownAsync(podmanContext, "pod.yaml");

5) Swarm stack deploy/remove (Docker CLI)

// Requires Docker Swarm mode: docker swarm init
var dockerContext = new DriverContext("docker");
var stacks = kernel.SysCtl<IStackDriver>("docker");

var deploy = await stacks.DeployAsync(dockerContext,
    new StackDeployConfig
    {
        StackName = "web",
        ComposeFiles = new List<string> { "docker-stack.yml" }
    });

var services = await stacks.GetServicesAsync(dockerContext, "web");

// Teardown when done
await stacks.RemoveAsync(dockerContext, new[] { "web" });

Capability per Driver

Capability Docker CLI Docker API Podman CLI
Container / Image / Network / Volume yes yes yes
System / Auth / Streaming yes yes yes
Compose yes - -
Stack (Swarm) yes - -
Service (Swarm) yes yes -
Pods - - yes
Kubernetes play/generate - - yes
Machine management - - yes
Multi-arch manifests - - yes

Kernel Setup

Register one or more drivers in the kernel builder:

using var kernel = await FluentDockerKernel.Create()
    .WithDockerCli("docker", d => d.AsDefault())
    .WithDockerApi("docker-api", d => d
        .WithConnectionTimeout(TimeSpan.FromSeconds(30)))
    .WithPodmanCli("podman", d => d
        .WithAutoStartMachine()
        .AsDefault())
    .BuildAsync();

Common API - Works with Any Driver

The fluent builder API is shared across drivers. Switch driver scope and keep the same container definition style.

await using var results = await new Builder()
    .WithinDriver(driverId, kernel) // driverId: "docker", "docker-api", "podman"
    .UseContainer(c => c
        .UseImage("postgres:15-alpine")
        .ExposePort("5432")
        .WithEnvironment("POSTGRES_PASSWORD=secret")
        .WaitForPort("5432/tcp", 30000))
    .BuildAsync();

Docker API — Direct Engine Communication

The Docker API driver talks directly to the Docker Engine REST API over Unix socket, named pipe, or TCP+TLS. No Docker CLI binary is required.

using var kernel = await FluentDockerKernel.Create()
    .WithDockerApi("api", d => d
        .AtHost("unix:///var/run/docker.sock")    // optional, auto-detected
        .WithCertificates("/path/to/certs")       // optional, for TLS
        .WithConnectionTimeout(TimeSpan.FromSeconds(15))
        .WithRequestTimeout(TimeSpan.FromMinutes(10))
        .AsDefault())
    .BuildAsync();

await using var results = await new Builder()
    .WithinDockerApi("api", kernel)
    .UseContainer(c => c
        .UseImage("redis:7-alpine")
        .ExposePort("6379"))
    .BuildAsync();

var stream = kernel.SysCtl<IStreamDriver>("api");
var context = new DriverContext("api");
await foreach (var ev in stream.StreamEventsAsync(context))
    Console.WriteLine($"Event: {ev.Action} on {ev.Type}");

Podman-Specific Features via Driver Layer

Access Podman-only capabilities through SysCtl<T> or TrySysCtl<T>:

var context = new DriverContext("podman");

// Pods
var pods = kernel.SysCtl<IPodmanPodDriver>("podman");
await pods.CreatePodAsync(context, new PodCreateConfig { Name = "my-pod" });

// Machine management
var machines = kernel.SysCtl<IPodmanMachineDriver>("podman");
var list = await machines.ListAsync(context);

// Multi-arch manifests
var manifest = kernel.SysCtl<IPodmanManifestDriver>("podman");
await manifest.CreateAsync(context,
    new ManifestCreateConfig
    {
        Name = "myapp:latest",
        Images = new List<string> { "myapp:amd64", "myapp:arm64" }
    });

Writing Driver-Portable Code

Use TrySysCtl<T> when you want optional driver-specific behavior:

if (kernel.TrySysCtl<IPodmanPodDriver>(driverId, out var podDriver))
{
    await podDriver.CreatePodAsync(context,
        new PodCreateConfig { Name = "my-pod" });
}

Test Support

FluentDocker v3 includes FluentDocker.Testing.Core in the main assembly. Framework-specific adapters are available as separate packages.

xUnit (Testing.Core)

// Option A — Abstract base (recommended):
public class MyRedisFixture : XunitContainerFixtureBase
{
  protected override void ConfigureContainer(IContainerBuilder builder)
    => builder
        .UseImage("redis:alpine")
        .ExposePort(6379)
        .WaitForPort("6379/tcp");
}

// Option B — Configure + IAsyncLifetime:
public class MyRedisFixture : XunitContainerFixture
{
  public MyRedisFixture()
  {
    Configure(builder => builder
        .UseImage("redis:alpine")
        .ExposePort(6379)
        .WaitForPort("6379/tcp"));
  }
}

MSTest (Testing.Core)

using FluentDocker.Testing.MsTest;

[TestClass]
public class MyTests
{
    private static FluentDockerKernel _kernel;
    private static ContainerResource _resource;

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

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

See the full testing docs for NUnit, Compose, Topology, Swarm Stack, and Podman Kubernetes resource types.


Linux Users

Docker requires sudo by default. Configure per-driver: WithSudo(SudoMechanism.NoPassword) or add user to docker group: sudo usermod -aG docker $USER


Contributing

Contributions welcome! Please adhere to .editorconfig for code style.


Resources


License

Apache 2.0 - See LICENSE for details.

Sponsor this project

 

Packages

 
 
 

Contributors

Languages