NAME

API::Docker::API::Exec - Docker Engine Exec API

VERSION

version 0.004

SYNOPSIS

my $docker = API::Docker->new;

# Create an exec instance
my $exec = $docker->exec->create($container_id,
    Cmd         => ['/bin/sh', '-c', 'echo hello'],
    AttachStdout => 1,
    AttachStderr => 1,
);

# Start the exec -- ArrayRef of { stream => ..., data => ... } frames
my $frames = $docker->exec->start($exec->{Id});
my $output = join '', map { $_->{data} } @$frames;

# The exit status comes from a separate call
my $exit = $docker->exec->inspect($exec->{Id})->{ExitCode};

# Inspect exec instance
my $info = $docker->exec->inspect($exec->{Id});

DESCRIPTION

This module provides methods for executing commands inside running containers using the Docker Exec API.

Accessed via $docker->exec, or through "using" in API::Docker::Role::Using for a run of calls that needs its own transport bound: $docker->exec->using(read_timeout => 5).

client

Reference to API::Docker client. Weak reference to avoid circular dependencies.

create

my $exec = $exec->create($container_id,
    Cmd          => ['/bin/sh', '-c', 'echo hello'],
    AttachStdout => 1,
    AttachStderr => 1,
    Tty          => 0,
);

Create an exec instance. Returns hashref with Id.

Required config: Cmd (ArrayRef of command and arguments).

Common config keys: AttachStdin, AttachStdout, AttachStderr, Tty, Env, User, WorkingDir.

The boolean flags (AttachStdin, AttachStdout, AttachStderr, Tty, Privileged) may be given as a Perl 1/0 or as a JSON boolean; either goes out as a real JSON true/false, which the engine's body type-check requires. Passing 1 where the daemon wants a boolean would otherwise be rejected.

start

my $frames = $exec->start($exec_id, Detach => 0);

my $output = join '', map { $_->{data} } @$frames;

Start an exec instance. Returns an ArrayRef of frames in the same shape as "logs" in API::Docker::API::Containers:

[ { stream => 'stdout', data => "OUT\n" },
  { stream => 'stderr', data => "ERR\n" } ]

An exec instance created without a TTY multiplexes stdout and stderr into one framed stream, which this method demultiplexes. One created with a TTY has no frame headers and its output arrives as a single stream => 'raw' frame. A detached start produces no output, so it returns an empty ArrayRef.

The exit status is not part of this response. It comes from a separate call once the exec has finished:

my $exit = $exec->inspect($exec_id)->{ExitCode};

Options:

  • Detach - Run detached; the engine returns immediately and no output is streamed

  • Tty - Declares that this exec instance was created with a TTY. It is sent in the request body, where the engine expects it to match the Tty given to "create", and it also suppresses demultiplexing of the response. Framing is otherwise detected from the response bytes -- see "Detecting a framed stream" in API::Docker::Role::HTTP

  • on_frame - CodeRef called with each frame as it arrives, instead of the ArrayRef being collected and returned; see below

Watching the output as it is produced

Without a callback this returns when the command has finished and the daemon has closed the stream -- a command that runs for a minute is a minute of silence, and one that never finishes never returns. Pass on_frame and the frames are handed over as they arrive:

my $summary = $exec->start($exec_id,
    on_frame => sub {
        my ($frame, $stop) = @_;
        print $frame->{data};
        $stop->() if $frame->{data} =~ /ready/;
    },
);

$summary;   # { delivered => 9, stopped => 1 }

With a callback the return value is that summary HashRef, not the frames: delivered is how many went to the callback, stopped is 1 when the callback ended the stream and 0 when the daemon did. Nothing is accumulated, so joining the output is the callback's job. See "Streaming a response as it arrives" in API::Docker::Role::HTTP.

A detached start produces no output, so its summary is { delivered => 0, stopped => 0 } where the buffered call returns an empty ArrayRef.

Tty means something stronger on this path. The buffered path decides framing by walking the whole body, which is exactly what a streamed one does not have; so with on_frame it is a promise about the exec instance rather than a hint, and an undeclared stream that turns out not to be framed croaks instead of being handed back raw. Pass the same Tty that went to "create" -- the engine expects them to agree in any case.

resize

$exec->resize($exec_id, h => 40, w => 120);

Resize the TTY for an exec instance.

Options:

  • h - New height in character rows

  • w - New width in character columns

inspect

my $info = $exec->inspect($exec_id);

Get information about an exec instance.

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.