Skip to content

tiny-blocks/docker-container

Repository files navigation

Docker container

License

Overview

Manage Docker containers programmatically, simplifying the creation, running, and interaction with containers.

The library provides interfaces and implementations for adding network configurations, mapping ports, setting environment variables, and executing commands inside containers. Designed to support unit tests and integration tests, it enables developers to manage containerized environments with minimal effort.

Installation

composer require tiny-blocks/docker-container

How to use

Creating a container

Creates a container from a specified image and an optional name.

$container = GenericDockerContainer::from(image: 'php:8.5-fpm', name: 'my-container');

Running a container

Starts a container. Optionally accepts commands to run on startup and a wait strategy applied after the container starts.

$container->run();

With commands:

$container->run(commands: ['ls', '-la']);

With commands and a wait strategy:

$container->run(commands: ['ls', '-la'], waitAfterStarted: ContainerWaitForTime::forSeconds(seconds: 5));

Running if not exists

Starts a container only if a container with the same name is not already running.

$container->runIfNotExists();

Pulling images in parallel

Calling pullImage() starts downloading the image in the background via a non-blocking process. When run() or runIfNotExists() is called, it waits for the pull to complete before starting the container.

To pull multiple images in parallel, call pullImage() on all containers before calling run() on any of them. This way the downloads happen concurrently:

$mysql = MySQLDockerContainer::from(image: 'mysql:8.4', name: 'my-database')
    ->pullImage()
    ->withRootPassword(rootPassword: 'root');

$flyway = FlywayDockerContainer::from(image: 'flyway/flyway:12-alpine')
    ->pullImage()
    ->withMigrations(pathOnHost: '/path/to/migrations');

// Both images are downloading in the background.
// MySQL pull completes here, container starts and becomes ready.
$mySQLStarted = $mysql->runIfNotExists();

// Flyway pull already finished while MySQL was starting.
$flyway->withSource(container: $mySQLStarted, username: 'root', password: 'root')
    ->cleanAndMigrate();

Setting network

Sets the Docker network the container should join. The network is created automatically when the container is started via run() or runIfNotExists(), if it does not already exist. Networks created by the library are labeled with tiny-blocks.docker-container=true for safe cleanup.

$container->withNetwork(name: 'my-network');

Setting port mappings

Maps a port from the host to the container.

$container->withPortMapping(portOnHost: 8080, portOnContainer: 80);

After the container starts, both ports are available through the Address:

$ports = $started->getAddress()->getPorts();

$ports->firstExposedPort();  // 80   (container-internal)
$ports->firstHostPort();     // 8080 (host-accessible)

Setting volume mappings

Mounts a directory from the host into the container.

$container->withVolumeMapping(pathOnHost: '/host/data', pathOnContainer: '/container/data');

Setting environment variables

Adds an environment variable to the container.

$container->withEnvironmentVariable(key: 'APP_ENV', value: 'testing');

Disabling auto-remove

By default, containers are removed when stopped. This disables that behavior.

$container->withoutAutoRemove();

Copying files to a container

Registers files or directories to be copied from the host into the container after it starts.

$container->copyToContainer(pathOnHost: '/path/to/files', pathOnContainer: '/path/in/container');

Stopping a container

Stops a running container. An optional timeout (in seconds) controls how long to wait before forcing the stop. The default timeout is 300 seconds.

$started = $container->run();
$result = $started->stop();

With a custom timeout:

$result = $started->stop(timeoutInWholeSeconds: 60);

Stopping on shutdown

Registers the container to be forcefully removed when the PHP process exits. On shutdown, the following cleanup is performed automatically:

  • The container is killed and removed (docker rm --force --volumes).
  • Anonymous volumes created by the container (e.g., MySQL's /var/lib/mysql) are removed.
  • Unused networks created by the library are pruned.

Only resources labeled with tiny-blocks.docker-container=true are affected. Containers, volumes, and networks from other environments are never touched.

$started = $container->run();
$started->stopOnShutdown();

Executing commands after startup

Runs commands inside an already-started container.

$started = $container->run();
$result = $started->executeAfterStarted(commands: ['php', '-v']);

The returned ExecutionCompleted provides the command output and success status:

$result->getOutput();
$result->isSuccessful();

Wait strategies

Waiting for a fixed time

Pauses execution for a specified number of seconds before or after starting a container.

$container->withWaitBeforeRun(wait: ContainerWaitForTime::forSeconds(seconds: 3));

Waiting for a dependency

Blocks until a readiness condition is satisfied, with a configurable timeout. This is useful when one container depends on another being fully ready.

$mySQLStarted = MySQLDockerContainer::from(image: 'mysql:8.4')
    ->withRootPassword(rootPassword: 'root')
    ->run();

$container = GenericDockerContainer::from(image: 'my-app:latest')
    ->withWaitBeforeRun(
        wait: ContainerWaitForDependency::untilReady(
            condition: MySQLReady::from(container: $mySQLStarted),
            timeoutInSeconds: 30
        )
    )
    ->run();

MySQL container

MySQLDockerContainer provides a specialized container for MySQL databases. It extends the generic container with MySQL-specific configuration and automatic readiness detection.

Configuring MySQL options

Method Parameter Description
withTimezone $timezone Sets the container timezone (e.g., America/Sao_Paulo).
withUsername $user Sets the MySQL user created on startup.
withPassword $password Sets the password for the MySQL user.
withDatabase $database Sets the default database created on startup.
withRootPassword $rootPassword Sets the root password for the MySQL instance.
withGrantedHosts $hosts Sets hosts granted root privileges (default: ['%', '172.%']).
$mySQLContainer = MySQLDockerContainer::from(image: 'mysql:8.4', name: 'my-database')
    ->withTimezone(timezone: 'America/Sao_Paulo')
    ->withUsername(user: 'app_user')
    ->withPassword(password: 'secret')
    ->withDatabase(database: 'my_database')
    ->withPortMapping(portOnHost: 3306, portOnContainer: 3306)
    ->withRootPassword(rootPassword: 'root')
    ->withGrantedHosts()
    ->run();

Setting readiness timeout

Configures how long the MySQL container waits for the database to become ready before throwing a ContainerWaitTimeout exception. The default timeout is 30 seconds.

$mySQLContainer = MySQLDockerContainer::from(image: 'mysql:8.4', name: 'my-database')
    ->withRootPassword(rootPassword: 'root')
    ->withReadinessTimeout(timeoutInSeconds: 60)
    ->run();

Retrieving connection data

After the MySQL container starts, connection details are available through the MySQLContainerStarted instance.

$address = $mySQLContainer->getAddress();
$ip = $address->getIp();
$hostname = $address->getHostname();

$ports = $address->getPorts();
$containerPort = $ports->firstExposedPort();  // e.g. 3306 (container-internal)
$hostPort = $ports->firstHostPort();          // e.g. 49153 (host-accessible)

$environmentVariables = $mySQLContainer->getEnvironmentVariables();
$database = $environmentVariables->getValueBy(key: 'MYSQL_DATABASE');
$username = $environmentVariables->getValueBy(key: 'MYSQL_USER');
$password = $environmentVariables->getValueBy(key: 'MYSQL_PASSWORD');

$jdbcUrl = $mySQLContainer->getJdbcUrl();

Use firstExposedPort() when connecting from another container in the same network. Use firstHostPort() when connecting from the host machine (e.g., tests running outside Docker).

Flyway container

FlywayDockerContainer provides a specialized container for running Flyway database migrations. It encapsulates Flyway configuration, database source detection, and migration file management.

Setting the database source

Configures the Flyway container to connect to a running MySQL container. Automatically detects the JDBC URL and target schema from MYSQL_DATABASE, and sets the history table to schema_history.

$flywayContainer = FlywayDockerContainer::from(image: 'flyway/flyway:12-alpine')
    ->withNetwork(name: 'my-network')
    ->withMigrations(pathOnHost: '/path/to/migrations')
    ->withSource(container: $mySQLStarted, username: 'root', password: 'root');

The schema and table can be overridden after calling withSource():

$flywayContainer
    ->withSource(container: $mySQLStarted, username: 'root', password: 'root')
    ->withSchema(schema: 'custom_schema')
    ->withTable(table: 'custom_history');

Configuring migrations

Sets the host directory containing Flyway migration SQL files. The files are copied into the container at /flyway/migrations.

$flywayContainer->withMigrations(pathOnHost: '/path/to/migrations');

Configuring Flyway options

Method Parameter Description
withTable $table Overrides the history table name (default: schema_history).
withSchema $schema Overrides the target schema (default: auto-detected from MySQL).
withCleanDisabled $disabled Enables or disables Flyway's clean command.
withConnectRetries $retries Sets the number of database connection retries.
withValidateMigrationNaming $enabled Enables or disables migration naming validation.

Running Flyway commands

Method Flyway command Description
migrate() migrate Applies pending migrations.
validate() validate Validates applied migrations against local.
repair() repair Repairs the schema history table.
cleanAndMigrate() clean migrate Drops all objects and re-applies migrations.
$flywayContainer->migrate();
$flywayContainer->cleanAndMigrate();

Usage examples

  • When running the containers from the library on a host (your local machine), map the volume /var/run/docker.sock:/var/run/docker.sock so the container has access to the Docker daemon on the host machine.
  • In some cases, it may be necessary to add the docker-cli dependency to your PHP image to interact with Docker from within the container.

MySQL with Flyway migrations

Configure both containers and start image pulls in parallel before running either one:

$mySQLContainer = MySQLDockerContainer::from(image: 'mysql:8.4', name: 'test-database')
    ->pullImage()
    ->withNetwork(name: 'my-network')
    ->withTimezone(timezone: 'America/Sao_Paulo')
    ->withPassword(password: 'secret')
    ->withDatabase(database: 'test_adm')
    ->withRootPassword(rootPassword: 'root')
    ->withGrantedHosts();

$flywayContainer = FlywayDockerContainer::from(image: 'flyway/flyway:12-alpine')
    ->pullImage()
    ->withNetwork(name: 'my-network')
    ->withMigrations(pathOnHost: '/path/to/migrations')
    ->withCleanDisabled(disabled: false)
    ->withConnectRetries(retries: 5)
    ->withValidateMigrationNaming(enabled: true);

$mySQLStarted = $mySQLContainer->runIfNotExists();
$mySQLStarted->stopOnShutdown();

$flywayContainer
    ->withSource(container: $mySQLStarted, username: 'root', password: 'root')
    ->cleanAndMigrate();

License

Docker container is licensed under MIT.

Contributing

Please follow the contributing guidelines to contribute to the project.

About

Manage Docker containers programmatically, simplifying the creation, running, and interaction with containers.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors