{"id":15040597,"url":"https://github.com/mariotoffia/fluentdocker","last_synced_at":"2026-05-11T15:01:50.198Z","repository":{"id":37550381,"uuid":"54591411","full_name":"mariotoffia/FluentDocker","owner":"mariotoffia","description":"Use docker, docker-compose local and remote in tests and your .NET core/full framework apps via a FluentAPI","archived":false,"fork":false,"pushed_at":"2026-05-11T12:04:13.000Z","size":5189,"stargazers_count":1386,"open_issues_count":52,"forks_count":104,"subscribers_count":16,"default_branch":"master","last_synced_at":"2026-05-11T12:07:21.769Z","etag":null,"topics":["builder","compose","docker","docker-container","docker-machine","docker-stack","dotnetcore","fluent","linux","macos","net","test","windows"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mariotoffia.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["mariotoffia"]}},"created_at":"2016-03-23T20:36:52.000Z","updated_at":"2026-05-11T12:04:17.000Z","dependencies_parsed_at":"2026-05-11T12:04:21.518Z","dependency_job_id":null,"html_url":"https://github.com/mariotoffia/FluentDocker","commit_stats":{"total_commits":539,"total_committers":34,"mean_commits":"15.852941176470589","dds":0.3246753246753247,"last_synced_commit":"83ec85f16368a0a8ac2031f4a9f88cd4fd35812e"},"previous_names":[],"tags_count":63,"template":false,"template_full_name":null,"purl":"pkg:github/mariotoffia/FluentDocker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2FFluentDocker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2FFluentDocker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2FFluentDocker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2FFluentDocker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mariotoffia","download_url":"https://codeload.github.com/mariotoffia/FluentDocker/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mariotoffia%2FFluentDocker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32900044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["builder","compose","docker","docker-container","docker-machine","docker-stack","dotnetcore","fluent","linux","macos","net","test","windows"],"created_at":"2024-09-24T20:44:47.472Z","updated_at":"2026-05-11T15:01:50.185Z","avatar_url":"https://github.com/mariotoffia.png","language":"C#","funding_links":["https://github.com/sponsors/mariotoffia"],"categories":[],"sub_categories":[],"readme":"# FluentDocker\n\n[![CI](https://github.com/mariotoffia/FluentDocker/actions/workflows/ci.yml/badge.svg)](https://github.com/mariotoffia/FluentDocker/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/mariotoffia/FluentDocker/branch/master/graph/badge.svg)](https://codecov.io/gh/mariotoffia/FluentDocker)\n[![Release](https://img.shields.io/github/v/release/mariotoffia/FluentDocker?sort=semver\u0026display_name=tag\u0026color=brightgreen)](https://github.com/mariotoffia/FluentDocker/releases/latest)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![.NET](https://img.shields.io/badge/.NET-net8.0%20%7C%20net10.0-blueviolet)](https://dotnet.microsoft.com/)\n\n| Package | NuGet | Downloads |\n|---------|:-----:|:---------:|\n| FluentDocker | [![NuGet](https://img.shields.io/nuget/v/FluentDocker.svg)](https://www.nuget.org/packages/FluentDocker) | [![Downloads](https://img.shields.io/nuget/dt/FluentDocker.svg)](https://www.nuget.org/packages/FluentDocker) |\n| Testing.Xunit | [![NuGet](https://img.shields.io/nuget/v/FluentDocker.Testing.Xunit.svg)](https://www.nuget.org/packages/FluentDocker.Testing.Xunit) | [![Downloads](https://img.shields.io/nuget/dt/FluentDocker.Testing.Xunit.svg)](https://www.nuget.org/packages/FluentDocker.Testing.Xunit) |\n| Testing.MsTest | [![NuGet](https://img.shields.io/nuget/v/FluentDocker.Testing.MsTest.svg)](https://www.nuget.org/packages/FluentDocker.Testing.MsTest) | [![Downloads](https://img.shields.io/nuget/dt/FluentDocker.Testing.MsTest.svg)](https://www.nuget.org/packages/FluentDocker.Testing.MsTest) |\n| Testing.NUnit | [![NuGet](https://img.shields.io/nuget/v/FluentDocker.Testing.NUnit.svg)](https://www.nuget.org/packages/FluentDocker.Testing.NUnit) | [![Downloads](https://img.shields.io/nuget/dt/FluentDocker.Testing.NUnit.svg)](https://www.nuget.org/packages/FluentDocker.Testing.NUnit) |\n\n---\n\n## Quick Start\n\n```csharp\nusing System.Linq;\nusing FluentDocker.Builders;\nusing FluentDocker.Drivers.Podman;\nusing FluentDocker.Kernel;\nusing FluentDocker.Model.Drivers;\n\n// Multiple kernels per app are supported.\n// This kernel registers both Docker CLI and Podman CLI.\nusing var kernel = await FluentDockerKernel.Create()\n    .WithDockerCli(\"docker\", d =\u003e d.AsDefault())\n    .WithPodmanCli(\"podman\", d =\u003e d.WithAutoStartMachine())\n    .BuildAsync();\n```\n\n### 1) Standard container (Docker CLI)\n\n```csharp\nawait using var results = await new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"nginx:alpine\")\n        .ExposePort(\"80\")\n        .WaitForPort(\"80/tcp\", 30000))\n    .BuildAsync();\n\nvar endpoint = results.Containers.First()\n    .ToHostExposedEndpoint(\"80/tcp\");\nConsole.WriteLine($\"Docker endpoint: {endpoint.Address}:{endpoint.Port}\");\n```\n\n### 2) Standard container (Podman CLI)\n\n```csharp\nawait using var results = await new Builder()\n    .WithinPodmanCli(\"podman\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"nginx:alpine\")\n        .ExposePort(\"80\")\n        .WaitForPort(\"80/tcp\", 30000))\n    .BuildAsync();\n\nvar endpoint = results.Containers.First()\n    .ToHostExposedEndpoint(\"80/tcp\");\nConsole.WriteLine($\"Podman endpoint: {endpoint.Address}:{endpoint.Port}\");\n```\n\n### 3) Docker Compose (Docker CLI)\n\n```csharp\nawait using var results = await new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseCompose(c =\u003e c\n        .WithComposeFile(\"docker-compose.yml\")\n        .WithRemoveOrphans()\n        .WithWait()\n        .WithWaitTimeout(30))\n    .BuildAsync();\n\nvar compose = results.ComposeServices.First();\n```\n\n### 4) Podman Kubernetes (kube play / kube down)\n\n```csharp\nvar context = new DriverContext(\"podman\");\nvar kube = kernel.SysCtl\u003cIPodmanKubernetesDriver\u003e(\"podman\");\n\nawait kube.PlayAsync(context, new KubePlayConfig\n{\n    YamlPath = \"pod.yaml\",\n    Replace = true\n});\n\n// Teardown\nawait kube.DownAsync(context, \"pod.yaml\");\n```\n\n---\n\n### 5) Test Support\n\nFluentDocker supports xUnit, MSTest, and NUnit via adapter packages.\nSee [Test Support](#test-support) below for examples.\n\n## Installation\n\n```bash\ndotnet add package FluentDocker\ndotnet add package FluentDocker.Testing.Xunit   # xUnit adapter\ndotnet add package FluentDocker.Testing.MsTest  # MSTest adapter\ndotnet add package FluentDocker.Testing.NUnit   # NUnit adapter\n```\n\n---\n\n\u003e **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](https://github.com/mariotoffia/FluentDocker/releases/tag/3.0.0) and the [migration guide](docs/migration.md) for the full feature list and breaking changes.\n\n## Features\n\n### Container Management\n\n```csharp\n// Create and start\nusing var results = new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"nginx:latest\")\n        .ExposePort(\"80\"))\n    .Build();\n\nvar container = results.Containers.First();\n\n// Get configuration\nvar config = container.GetConfiguration(true);\n\n// Container stats (v3)\nvar stats = await container.GetStatsAsync();\nConsole.WriteLine($\"CPU: {stats.CpuPercent:F2}%\");\n```\n\n### Port Mapping\n\n```csharp\n// Explicit: host port 8080 → container port 80\n.ExposePort(8080, 80)\n\n// Random: let Docker choose host port\n.ExposePort(\"80\")\n\n// Resolve actual endpoint\nvar endpoint = container.ToHostExposedEndpoint(\"80/tcp\");\n```\n\n### Wait Strategies\n\n```csharp\n.WaitForPort(\"5432/tcp\", 30000)           // Wait for port\n.WaitForProcess(\"postgres\", 30000)         // Wait for process\n.WaitForLogMessage(\"ready\", 30000)         // Wait for log message\n```\n\n### Networks with Static IP\n\n```csharp\nusing var nwResults = new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseNetwork(n =\u003e n\n        .WithName(\"my-network\")\n        .WithSubnet(\"10.18.0.0/16\"))\n    .Build();\n\nvar network = nwResults.Networks.First();\n\nusing var cResults = new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"nginx\")\n        .WithNetwork(\"my-network\")\n        .UseIpV4(\"10.18.0.100\"))\n    .Build();\n```\n\n### Volume Mounts\n\n```csharp\n// Host path mount\n.WithVolume(\"/host/path\", \"/container/path\")\n\n// Named volume\n.WithVolume(\"my-vol\", \"/data\")\n```\n\n### File Operations\n\n```csharp\n// Copy to container\nawait container.CopyToAsync(\"/local/file\", \"/container/file\");\nawait container.CopyToAsync(\"/local/dir\", \"/container/dir\");  // v3: directories\n\n// Copy from container\nawait container.CopyFromAsync(\"/container/file\", \"/local/file\");\n```\n\n### Image Building\n\n```csharp\n// Inline Dockerfile\nusing var imgResults = new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseImage(\"mynode:latest\", img =\u003e img\n        .From(\"node:18-alpine\")\n        .Run(\"npm install -g nodemon\")\n        .ExposePorts(8080)\n        .Command(\"node\", \"app.js\"))\n    .Build();\n```\n\n---\n\n## Drivers\n\nFluentDocker ships with three drivers:\n\n- Docker CLI\n- Docker API\n- Podman CLI\n\nAll drivers share a common core (`IContainerDriver`, `IImageDriver`,\n`INetworkDriver`, `IVolumeDriver`, `ISystemDriver`, `IAuthDriver`,\n`IStreamDriver`) and add driver-specific capabilities on top.\n\n### Quick Driver Examples (Start Here)\n\nUse these imports in the snippets below:\n\n```csharp\nusing System.Collections.Generic;\nusing System.Linq;\nusing FluentDocker.Builders;\nusing FluentDocker.Drivers;\nusing FluentDocker.Drivers.Podman;\nusing FluentDocker.Kernel;\nusing FluentDocker.Model.Drivers;\n```\n\nExample: create a kernel with both Docker CLI and Podman CLI\n(multiple kernels per app are also supported):\n\n```csharp\nusing var kernel = await FluentDockerKernel.Create()\n    .WithDockerCli(\"docker\", d =\u003e d.AsDefault())\n    .WithPodmanCli(\"podman\", d =\u003e d\n        .WithAutoStartMachine() // macOS/Windows: ensure Podman VM is running\n        .AsDefault())\n    .BuildAsync();\n```\n\n#### 1) Standard container (Docker CLI)\n\n```csharp\nawait using var dockerResults = await new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"nginx:alpine\")\n        .ExposePort(\"80\")\n        .WaitForPort(\"80/tcp\", 30000))\n    .BuildAsync();\n\nvar dockerEndpoint = dockerResults.Containers.First()\n    .ToHostExposedEndpoint(\"80/tcp\");\n```\n\n#### 2) Standard container (Podman CLI)\n\n```csharp\nawait using var podmanResults = await new Builder()\n    .WithinPodmanCli(\"podman\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"nginx:alpine\")\n        .ExposePort(\"80\")\n        .WaitForPort(\"80/tcp\", 30000))\n    .BuildAsync();\n\nvar podmanEndpoint = podmanResults.Containers.First()\n    .ToHostExposedEndpoint(\"80/tcp\");\n```\n\n#### 3) Docker Compose (Docker CLI)\n\n```csharp\nawait using var composeResults = await new Builder()\n    .WithinDockerCli(\"docker\", kernel)\n    .UseCompose(c =\u003e c\n        .WithComposeFile(\"docker-compose.yml\")\n        .WithRemoveOrphans()\n        .WithWait())\n    .BuildAsync();\n\nvar compose = composeResults.ComposeServices.First();\n```\n\n#### 4) Kubernetes play/down (Podman CLI)\n\n```csharp\nvar podmanContext = new DriverContext(\"podman\");\nvar kube = kernel.SysCtl\u003cIPodmanKubernetesDriver\u003e(\"podman\");\n\nvar play = await kube.PlayAsync(podmanContext,\n    new KubePlayConfig\n    {\n        YamlPath = \"pod.yaml\",\n        Replace = true\n    });\n\n// Teardown when done\nawait kube.DownAsync(podmanContext, \"pod.yaml\");\n```\n\n#### 5) Swarm stack deploy/remove (Docker CLI)\n\n```csharp\n// Requires Docker Swarm mode: docker swarm init\nvar dockerContext = new DriverContext(\"docker\");\nvar stacks = kernel.SysCtl\u003cIStackDriver\u003e(\"docker\");\n\nvar deploy = await stacks.DeployAsync(dockerContext,\n    new StackDeployConfig\n    {\n        StackName = \"web\",\n        ComposeFiles = new List\u003cstring\u003e { \"docker-stack.yml\" }\n    });\n\nvar services = await stacks.GetServicesAsync(dockerContext, \"web\");\n\n// Teardown when done\nawait stacks.RemoveAsync(dockerContext, new[] { \"web\" });\n```\n\n### Capability per Driver\n\n| Capability | Docker CLI | Docker API | Podman CLI |\n|---|:---:|:---:|:---:|\n| Container / Image / Network / Volume | yes | yes | yes |\n| System / Auth / Streaming | yes | yes | yes |\n| Compose | yes | - | - |\n| Stack (Swarm) | yes | - | - |\n| Service (Swarm) | yes | yes | - |\n| Pods | - | - | yes |\n| Kubernetes play/generate | - | - | yes |\n| Machine management | - | - | yes |\n| Multi-arch manifests | - | - | yes |\n\n### Kernel Setup\n\nRegister one or more drivers in the kernel builder:\n\n```csharp\nusing var kernel = await FluentDockerKernel.Create()\n    .WithDockerCli(\"docker\", d =\u003e d.AsDefault())\n    .WithDockerApi(\"docker-api\", d =\u003e d\n        .WithConnectionTimeout(TimeSpan.FromSeconds(30)))\n    .WithPodmanCli(\"podman\", d =\u003e d\n        .WithAutoStartMachine()\n        .AsDefault())\n    .BuildAsync();\n```\n\n### Common API - Works with Any Driver\n\nThe fluent builder API is shared across drivers. Switch driver scope and keep\nthe same container definition style.\n\n```csharp\nawait using var results = await new Builder()\n    .WithinDriver(driverId, kernel) // driverId: \"docker\", \"docker-api\", \"podman\"\n    .UseContainer(c =\u003e c\n        .UseImage(\"postgres:15-alpine\")\n        .ExposePort(\"5432\")\n        .WithEnvironment(\"POSTGRES_PASSWORD=secret\")\n        .WaitForPort(\"5432/tcp\", 30000))\n    .BuildAsync();\n```\n\n### Docker API — Direct Engine Communication\n\nThe Docker API driver talks directly to the Docker Engine REST API over Unix\nsocket, named pipe, or TCP+TLS. No Docker CLI binary is required.\n\n```csharp\nusing var kernel = await FluentDockerKernel.Create()\n    .WithDockerApi(\"api\", d =\u003e d\n        .AtHost(\"unix:///var/run/docker.sock\")    // optional, auto-detected\n        .WithCertificates(\"/path/to/certs\")       // optional, for TLS\n        .WithConnectionTimeout(TimeSpan.FromSeconds(15))\n        .WithRequestTimeout(TimeSpan.FromMinutes(10))\n        .AsDefault())\n    .BuildAsync();\n\nawait using var results = await new Builder()\n    .WithinDockerApi(\"api\", kernel)\n    .UseContainer(c =\u003e c\n        .UseImage(\"redis:7-alpine\")\n        .ExposePort(\"6379\"))\n    .BuildAsync();\n\nvar stream = kernel.SysCtl\u003cIStreamDriver\u003e(\"api\");\nvar context = new DriverContext(\"api\");\nawait foreach (var ev in stream.StreamEventsAsync(context))\n    Console.WriteLine($\"Event: {ev.Action} on {ev.Type}\");\n```\n\n### Podman-Specific Features via Driver Layer\n\nAccess Podman-only capabilities through `SysCtl\u003cT\u003e` or `TrySysCtl\u003cT\u003e`:\n\n```csharp\nvar context = new DriverContext(\"podman\");\n\n// Pods\nvar pods = kernel.SysCtl\u003cIPodmanPodDriver\u003e(\"podman\");\nawait pods.CreatePodAsync(context, new PodCreateConfig { Name = \"my-pod\" });\n\n// Machine management\nvar machines = kernel.SysCtl\u003cIPodmanMachineDriver\u003e(\"podman\");\nvar list = await machines.ListAsync(context);\n\n// Multi-arch manifests\nvar manifest = kernel.SysCtl\u003cIPodmanManifestDriver\u003e(\"podman\");\nawait manifest.CreateAsync(context,\n    new ManifestCreateConfig\n    {\n        Name = \"myapp:latest\",\n        Images = new List\u003cstring\u003e { \"myapp:amd64\", \"myapp:arm64\" }\n    });\n```\n\n### Writing Driver-Portable Code\n\nUse `TrySysCtl\u003cT\u003e` when you want optional driver-specific behavior:\n\n```csharp\nif (kernel.TrySysCtl\u003cIPodmanPodDriver\u003e(driverId, out var podDriver))\n{\n    await podDriver.CreatePodAsync(context,\n        new PodCreateConfig { Name = \"my-pod\" });\n}\n```\n\n---\n\n## Test Support\n\nFluentDocker v3 includes `FluentDocker.Testing.Core` in the main assembly.\nFramework-specific adapters are available as separate packages.\n\n### xUnit (Testing.Core)\n\n```csharp\n// Option A — Abstract base (recommended):\npublic class MyRedisFixture : XunitContainerFixtureBase\n{\n  protected override void ConfigureContainer(IContainerBuilder builder)\n    =\u003e builder\n        .UseImage(\"redis:alpine\")\n        .ExposePort(6379)\n        .WaitForPort(\"6379/tcp\");\n}\n\n// Option B — Configure + IAsyncLifetime:\npublic class MyRedisFixture : XunitContainerFixture\n{\n  public MyRedisFixture()\n  {\n    Configure(builder =\u003e builder\n        .UseImage(\"redis:alpine\")\n        .ExposePort(6379)\n        .WaitForPort(\"6379/tcp\"));\n  }\n}\n```\n\n### MSTest (Testing.Core)\n\n```csharp\nusing FluentDocker.Testing.MsTest;\n\n[TestClass]\npublic class MyTests\n{\n    private static FluentDockerKernel _kernel;\n    private static ContainerResource _resource;\n\n    [ClassInitialize]\n    public static async Task ClassInit(TestContext context)\n    {\n        (_kernel, _resource) = await MsTestResourceHelpers.CreateContainerAsync(\n            builder =\u003e builder\n                .UseImage(\"redis:alpine\")\n                .WaitForPort(\"6379/tcp\"));\n    }\n\n    [ClassCleanup]\n    public static async Task ClassCleanup()\n    {\n        await MsTestResourceHelpers.DisposeAsync(_resource, _kernel);\n    }\n}\n```\n\nSee the [full testing docs](docs/testing.md) for NUnit, Compose, Topology,\nSwarm Stack, and Podman Kubernetes resource types.\n\n---\n\n## Linux Users\n\nDocker requires sudo by default. Configure per-driver:\n`WithSudo(SudoMechanism.NoPassword)` or add user to docker group:\n`sudo usermod -aG docker $USER`\n\n---\n\n## Contributing\n\nContributions welcome! Please adhere to `.editorconfig` for code style.\n\n---\n\n## Resources\n\n- [Documentation Site](https://mariotoffia.github.io/FluentDocker/) - Full documentation on GitHub Pages\n- [Migration Guide](docs/migration.md) - Upgrading from v2.x.x\n- [Architecture Docs](docs/architecture.md) - v3 architecture details\n- [NuGet Package](https://www.nuget.org/packages/FluentDocker)\n\n---\n\n## License\n\nApache 2.0 - See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmariotoffia%2Ffluentdocker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmariotoffia%2Ffluentdocker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmariotoffia%2Ffluentdocker/lists"}