Docker with Python

Feb 9, 2026 · 3 min read

Soooo, I have decided to write more about some of the things that I am doing right now.

Today I decided to port my janky subprocess.run(cmd) approach to managing docker containers to docker-py sdk.

Client

To interact with the local docker service, we first have to create a client.

The client can be created as:

import docker

client = docker.from_env()

There are other options available to for various configurations like providing the DOCKER_HOST argument for remote hosts but I’ll leave it to you for reading up on it from docker client ref.

Image

Once we have the client we can do everything that docker cli can do via python such as creating an image. To create an image we can do:

docker_file_path = "/tmp/cool_docker_project/Dockerfile"
tag = "my_docker_image"

(built_image, output) = client.images.build(
    path=docker_file_path, ## this can even just be /tmp/cool_docker_project/
    tag=tag
)

image_id = built_image.id

## other attributes are available via attrs like
built_image.attrs

Checkout Image obj ref (the built_image variable is this obj) and images ref for a detailed view.

Container

The containers have the same API as images. Honestly, the docker-py sdk is dead simple to work with. Everything is just an attribute away from the client such as client.networks or client.containers or client.nodes. For the containers, we follow a similar creation logic as the container:

container = client.containers.create(
    image=my_image,
    name=some_container_name,
    detach=True,
    auto_remove=True,
    platform="linux/amd64",
    network=my_network
)

container.start()

Do note that the container might not get started until you explicitly start it. I have not tried it but the docker docs do that so I won’t be the one to break tradition. Again, container references at.

Network/s

Now we have arrived at the main reason that I decided to switch to docker-py from my subprocess jank. The networks are probably the most easily managed with the sdk.

Creating network is as easy as containers but now we can also configure network pools and such.

from docker.types import IPAMConfig, IPAMPool

## lets define an IPAM pool with a subnet

## IPAM = IP Address Management btw

subnet = "192.168.0.0/24"
ipam_pool = IPAMPool(subnet=subnet)
ipam_config = IPAMConfig(pool_configs=[ipam_pools])

## then creating a network is like

networks = []

network = client.networks.create(
    name=network_name,
    driver="bridge", # or any other driver. and i also found a new driver type that I didn't know existed!
    ipam=ipam_config,
    enable_ipv6=False,
    check_duplicate=True
)

networks.append(network)

The magic is when I want to create - let’s say 100 networks. For that, I can define a hell lot of IPAM pools then create networks at scale and tear them down just as easily.

for network in networks:
    network.remove()

As easy as that. Also you can do:

my_networks = client.networks.list(names="testnetwork")

which will match any network with the prefix testnetwork. This is very powerful since I can now manage a fleet of networks with a common basename with ease.

None Network

Before going, I didn’t know that there was a network driver called None. This is what my docker network ls gave me:

NETWORK ID         NAME     DRIVER    SCOPE
my_random_sha256   none     null      local

which broke some part of my code since it doesn’t provide any IPAM config or address pools and since I was checking whether I had already used an address pool, it was breaking my code (cue pdb). Apparently, if you don’t want any network access to the docker container, you can assign it the none network.

That’s it for today. Tia!

Ashim Mahara
Authors
Security Analytics
My research interests include Cyber Security, Security Analytics and Autonomous Agents.