SkillAgentSearch skills...

FluentDocker

Use docker, docker-compose local and remote in tests and your .NET core/full framework apps via a FluentAPI

Install / Use

/learn @mariotoffia/FluentDocker

README

CI Quality Gate Status License .NET

| Package | NuGet | Downloads | | ---------------|:--------------:|:---------------:| | FluentDocker | NuGet | Downloads | | Microsoft Test | NuGet | Downloads | | XUnit Test | NuGet | Downloads |

FluentDocker

This library enables docker and docker-compose interactions using a Fluent API. It is supported on Linux, Windows and Mac. It also has support for the legacy docker-machine interactions. The library supports both Docker and Podman as container engines. Podman is a daemonless, open-source container engine that is a drop-in replacement for Docker. See the Container Engine Configuration section below for details.

:bulb: Breaking changes. It will not adhere to AssumeComposeVersion instead it will autodetect if it supports docker compose subcommand and automatically set it to V2. If not supported, it will use docker-compose and this is V1.

Sample Fluent API usage

      using (
        var container =
          new Builder().UseContainer()
            .UseImage("kiasaki/alpine-postgres")
            .ExposePort(5432)
            .WithEnvironment("POSTGRES_PASSWORD=mysecretpassword")
            .WaitForPort("5432/tcp", 30000 /*30s*/)
            .Build()
            .Start())
      {
        var config = container.GetConfiguration(true);
        Assert.AreEqual(ServiceRunningState.Running, config.State.ToServiceState());
      }

This fires up a postgres and waits for it to be ready. To use compose, just do it like this:

      var file = Path.Combine(Directory.GetCurrentDirectory(),
        (TemplateString) "Resources/ComposeTests/WordPress/docker-compose.yml");

      // @formatter:off
      using (var svc = new Builder()
                        .UseContainer()
                        .UseCompose()
                        .FromFile(file)
                        .RemoveOrphans()
                        .WaitForHttp("wordpress", "http://localhost:8000/wp-admin/install.php") 
                        .Build().Start())
        // @formatter:on
      {
        // We now have a running WordPress with a MySql database        
        var installPage = await "http://localhost:8000/wp-admin/install.php".Wget();

        Assert.IsTrue(installPage.IndexOf("https://wordpress.org/", StringComparison.Ordinal) != -1);
        Assert.AreEqual(1, svc.Hosts.Count); // The host used by compose
        Assert.AreEqual(2, svc.Containers.Count); // We can access each individual container
        Assert.AreEqual(2, svc.Images.Count); // And the images used.
      }

:bulb Note for Linux Users: Docker requires sudo by default and the library by default expects that executing user do not need to do sudo in order to talk to the docker daemon. More description can be found in the Talking to Docker Daemon chapter.

Container Engine Configuration

FluentDocker supports both Docker and Podman as container engines. By default, the library will automatically detect and prefer Docker if both are available. You can explicitly configure which engine to use:

using Ductus.FluentDocker.Model.Common;
using Ductus.FluentDocker.Extensions;

// Use Podman explicitly
ContainerEngine.Podman.SetContainerEngine();

// Or use Docker explicitly
ContainerEngine.Docker.SetContainerEngine();

// Or let it auto-detect (default - prefers Docker if available)
ContainerEngine.Auto.SetContainerEngine();

When to use Podman:

  • You're on Windows and using Podman Desktop
  • You prefer a daemonless container engine
  • You need rootless containers
  • Docker is not available on your system

Note: Podman is designed as a drop-in replacement for Docker, so all FluentDocker APIs work the same way regardless of which engine you choose. The library automatically handles the differences in binary names (docker vs podman, docker-compose vs podman-compose).

The fluent API builds up one or more services. Each service may be composite or singular. Therefore it is possible to e.g. fire up several docker-compose based services and manage each of them as a single service or dig in and use all underlying services on each docker-compose service. It is also possible to use services directly e.g.

      var file = Path.Combine(Directory.GetCurrentDirectory(),
        (TemplateString) "Resources/ComposeTests/WordPress/docker-compose.yml");

      using (var svc = new DockerComposeCompositeService(DockerHost, new DockerComposeConfig
      {
        ComposeFilePath = new List<string> { file }, ForceRecreate = true, RemoveOrphans = true,
        StopOnDispose = true
      }))
      {
        svc.Start();
        
        // We now have a running WordPress with a MySql database
        var installPage = await $"http://localhost:8000/wp-admin/install.php".Wget();
        
        Assert.IsTrue(installPage.IndexOf("https://wordpress.org/", StringComparison.Ordinal) != -1);
      }

The above example creates a docker-compose service from a single compose file. When the service is disposed all underlying services is automatically stopped.

The library is supported by .NET full 4.51 framework and higher, .NET standard 1.6, 2.0. It is divided into three thin layers, each layer is accessible:

  1. Docker Binaries interactions - Static commands and docker environment
  2. Services - thin service layer to manage machines, containers etc.
  3. Fluent API - API to build/discover services to be used

The Majority of the service methods are extension methods and not hardwired into the service itself, making them lightweight and customizable. Since everything is accessible it is e.g. easy to add extensions method for a service that uses the layer 1 commands to provide functionality.

Contribution

I do welcome contribution, though there is no contribution guideline as of yet, make sure to adhere to .editorconfig when doing the Pull Requests. Otherwise the build will fail. I'll update with a real guideline sooner or later this year.

Basic Usage of Commands (Layer 1)

All commands needs a DockerUri to work with. It is the Uri to the docker daemon, either locally or remote. It can be discoverable or hardcoded. Discovery of local DockerUri can be done by

     var hosts = new Hosts().Discover();
     var _docker = hosts.FirstOrDefault(x => x.IsNative) ?? hosts.FirstOrDefault(x => x.Name == "default");

The example snipped will check for native, or docker beta "native" hosts, if not choose the docker-machine "default" as host. If you're using docker-machine and no machine exists or is not started it is easy to create / start a docker-machine by e.g. "test-machine".Create(1024,20000000,1). This will create a docker machine named "test-machine" with 1GB of RAM, 20GB Disk, and use one CPU.

It is now possible to use the Uri to communicate using the commands. For example to get the version of client and server docker binaries:

     var result = _docker.Host.Version(_docker.Certificates);
     Debug.WriteLine(result.Data); // Will Print the Client and Server Version and API Versions respectively.

All commands return a CommandResponse<T> such that it is possible to check success factor by response.Success. If any data associated with the command it is returned in the response.Data property.

Then it is simple as below to start and stop include delete a container using the commands. Below starts a container and do a PS on it and then deletes it.

     var id = _docker.Host.Run("nginx:latest", null, _docker.Certificates).Data;
     var ps = _docker.Host.Ps(null, _docker.Certificates).Data;
     
     _docker.Host.RemoveContainer(id, true, true, null, _docker.Certificates);

When running on windows, one can choose to run linux or windows container. Use the LinuxDaemon or WindowsDaemon to control which daemon to talk to.

     _docker.LinuxDaemon(); // ensures that it will talk to linux daemon, if windows daemon it will switch

Some commands returns a stream of data when e.g. events or logs is wanted using a continuous stream. Streams can be used in background tasks and support CancellationToken. Below example tails a log.

     using (var logs = _docker.Host.Logs(id, _docker.Certificates))
     {
          while (!logs.IsFinished)
          {
               var line = logs.TryRead(5000); // Do a read with timeout
               if (null == line)
               {
                    break;
               }

               Debug.WriteLine(line);
          }
     }

Utility methods exists for commands. They come in different flaviours such as network

Related Skills

View on GitHub
GitHub Stars1.4k
CategoryDevelopment
Updated1mo ago
Forks104

Languages

C#

Security Score

100/100

Audited on Feb 23, 2026

No findings