NAME

API::Docker - Perl client for the Docker Engine API

VERSION

version 0.003

SYNOPSIS

use API::Docker;

# Connect to local Docker daemon via Unix socket
my $docker = API::Docker->new;

# Or connect to remote Docker daemon
my $docker = API::Docker->new(
    host => 'tcp://192.168.1.100:2375',
);

# System information
my $info = $docker->system->info;
my $version = $docker->system->version;

# Container management
my $containers = $docker->containers->list(all => 1);
my $result = $docker->containers->create(
    Image => 'nginx:latest',
    name  => 'my-nginx',
);
$docker->containers->start($result->{Id});

# Image operations
$docker->images->pull(fromImage => 'nginx', tag => 'latest');
my $images = $docker->images->list;

# Network and volume management
my $networks = $docker->networks->list;
my $volumes = $docker->volumes->list;

DESCRIPTION

API::Docker is a Perl client for the Docker Engine API. It provides a clean object-oriented interface to manage Docker containers, images, networks, and volumes.

Key features:

  • Pure Perl implementation with minimal dependencies

  • Unix socket and TCP transport support, both plaintext -- there is no TLS, and "tls" croaks rather than implying there is

  • Automatic API version negotiation

  • Object-oriented entity classes (Container, Image, Network, Volume)

  • Comprehensive logging via Log::Any

Architecture

The distribution is organized into several layers:

host

Docker daemon connection URL. Defaults to $ENV{DOCKER_HOST} or unix:///var/run/docker.sock.

No other source is consulted; see "Socket discovery".

Supported formats:

  • unix:///path/to/socket - Unix socket (default)

  • tcp://host:port - TCP connection

api_version

Docker API version to use (e.g., 1.41). If not set, the client will automatically negotiate the highest API version supported by the daemon.

This attribute is set automatically by "negotiate_version".

tls

Not implemented. tls => 1 croaks at construction; the default of 0 is the only value this client accepts.

The attribute is kept rather than removed because silence is the failure mode worth preventing here. API::Docker::Role::HTTP builds a plain IO::Socket::INET connection and speaks HTTP over it, and nothing anywhere reads this attribute -- so a tcp:// daemon has always been addressed in cleartext, tls => 1 or not, and a caller who asked for TLS got an unencrypted connection and no indication of it. Anyone who was passing this option was, by definition, sending credentials in the clear while believing otherwise.

Terminate TLS in front of the daemon instead and point "host" at the local end of it:

# stunnel, socat or plain ssh -- whatever is already in the stack
ssh -N -L 2375:127.0.0.1:2376 dockerhost
my $docker = API::Docker->new(host => 'tcp://127.0.0.1:2375');

Implementing TLS here is new work, not a repair: it needs the socket builder to know about IO::Socket::SSL, client certificates and verification policy. Until then this croaks.

cert_path

Unused. Path to TLS certificates, defaulting to $ENV{DOCKER_CERT_PATH}. No code reads it.

Unlike "tls" it does not croak, and deliberately so: it defaults from the environment, and DOCKER_CERT_PATH is commonly exported on machines that also run the docker CLI. Croaking on it would break clients that never asked for TLS at all, over a value the caller did not pass. On its own it also asserts nothing and transmits nothing -- setting it cannot make an unencrypted connection look encrypted, which is the specific harm "tls" was doing. The path that would act on it is the one that croaks.

system

Returns API::Docker::API::System instance for system operations like info, version, ping, and events.

containers

Returns API::Docker::API::Containers instance for container operations like list, create, start, stop, and remove.

images

Returns API::Docker::API::Images instance for image operations like list, pull, push, and remove.

networks

Returns API::Docker::API::Networks instance for network operations like list, create, connect, and disconnect.

volumes

Returns API::Docker::API::Volumes instance for volume operations like list, create, and remove.

exec

Returns API::Docker::API::Exec instance for executing commands in containers.

negotiate_version

$docker->negotiate_version;

Automatically negotiate the highest API version supported by the Docker daemon. This is called automatically before the first API request if "api_version" is not set.

After negotiation, "api_version" will contain the negotiated version (e.g., 1.41).

CONTAINER ENGINES

This client speaks the Docker Engine HTTP API over a socket. It never shells out to the docker binary, so any engine serving that API works, whether or not Docker itself is installed.

Podman

Podman ships a Docker-compatible API service. Enable its rootless socket and point "host" at it:

systemctl --user enable --now podman.socket
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"

The socket announces API version 1.41, which "negotiate_version" picks up like any other daemon. Multi-stage builds are passed through unchanged, target included, down to skipping the stages the target does not depend on.

Socket discovery

"host" resolves in two steps and no more: $ENV{DOCKER_HOST}, then unix:///var/run/docker.sock. It deliberately does not read Docker contexts. currentContext in ~/.docker/config.json and the matching ~/.docker/contexts/meta/*/meta.json are ignored, so if you switch daemons with docker context use, that choice is not picked up here. Set DOCKER_HOST explicitly instead.

Other clients sit at different points on that scale. The docker CLI and docker-java resolve contexts, with DOCKER_HOST outranking them when set. docker-py's from_env() reads DOCKER_HOST and otherwise falls back to the default socket, leaving contexts to a separate API. Testcontainers layers its own ~/.testcontainers.properties and a rootless probe list ($XDG_RUNTIME_DIR/docker.sock, ~/.docker/run/docker.sock, ~/.docker/desktop/docker.sock, /run/user/$UID/docker.sock) on top.

What none of them do is guess Podman's socket path: that probe list is for rootless Docker, not for Podman. Every one of those projects documents DOCKER_HOST as the way to reach Podman, which is the same answer given above.

ENVIRONMENT VARIABLES

DOCKER_HOST

Docker daemon connection URL. Used as default for "host" if not explicitly set.

Examples: unix:///var/run/docker.sock, tcp://localhost:2375

Also the supported way to reach a non-Docker engine such as Podman: unix://$XDG_RUNTIME_DIR/podman/podman.sock. See "CONTAINER ENGINES".

DOCKER_CERT_PATH

Path to TLS certificates directory. Used as default for "cert_path", which nothing reads -- this client has no TLS support at all. Setting it changes nothing, and having it set (as machines running the docker CLI usually do) breaks nothing.

SEE ALSO

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-api-docker/issues.

CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

AUTHOR

Torsten Raudssus <getty@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.