createContainer method

Future<String> createContainer(
  1. String image, {
  2. List<String>? command,
  3. Map<String, String> env = const {},
  4. String? name,
  5. Map<int, int?> ports = const {},
  6. Map<String, ({String bind, String mode})> volumes = const {},
  7. Map<String, String>? tmpfs,
  8. Map<String, String>? labels,
  9. String? network,
  10. List<String>? networkAliases,
  11. Map<String, Object?>? kwargs,
})

Creates a container and returns its ID.

Sends POST /containers/create with the given configuration. The createLabels function is called automatically to stamp the container with testcontainers metadata labels.

Parameters:

  • image — Docker image name (with optional tag).
  • command — command override. null uses the image's default.
  • env — environment variables as a KEY → value map.
  • name — optional container name.
  • ports — map of containerPort → hostPort (use null for an ephemeral host port).
  • volumes — bind mounts: hostPath → (bind: containerPath, mode: 'ro'|'rw').
  • tmpfs — tmpfs mounts: containerPath → options string.
  • labels — additional Docker labels (must not start with org.testcontainers).
  • network — network name or ID to attach the container to.
  • networkAliases — DNS aliases on network.
  • kwargs — extra Docker HostConfig fields (camelCase Dart names are converted to PascalCase Docker names).

Returns the new container's full ID string.

Implementation

Future<String> createContainer(
  String image, {
  List<String>? command,
  Map<String, String> env = const {},
  String? name,
  Map<int, int?> ports = const {},
  Map<String, ({String bind, String mode})> volumes = const {},
  Map<String, String>? tmpfs,
  Map<String, String>? labels,
  String? network,
  List<String>? networkAliases,
  Map<String, Object?>? kwargs,
}) async {
  final exposedPorts = <String, dynamic>{};
  final portBindings = <String, dynamic>{};

  for (final entry in ports.entries) {
    final containerPort = '${entry.key}/tcp';
    exposedPorts[containerPort] = {};
    portBindings[containerPort] = [
      {'HostIp': '', 'HostPort': entry.value?.toString() ?? ''},
    ];
  }

  // Extract user-provided labels from kwargs so they don't end up in HostConfig
  final userLabels = kwargs?['labels'] as Map<String, String>? ?? labels;
  final hostConfigKwargs = kwargs != null
      ? (Map<String, Object?>.of(kwargs)..remove('labels'))
      : null;

  final body = <String, Object?>{
    'Image': image,
    'ExposedPorts': exposedPorts,
    'Env': env.entries.map((e) => '${e.key}=${e.value}').toList(),
    'Labels': createLabels(image, userLabels),
    'HostConfig': <String, Object?>{
      'PortBindings': portBindings,
      'Binds': [
        for (final e in volumes.entries)
          '${e.key}:${e.value.bind}:${e.value.mode}',
      ],
      if (tmpfs != null && tmpfs.isNotEmpty) 'Tmpfs': tmpfs,
      if (network != null) 'NetworkMode': network,
      ...?hostConfigKwargs?.map(
        (k, v) => MapEntry(_toDockerKey(k), v),
      ),
    },
    if (command != null) 'Cmd': command,
  };

  if (network != null && networkAliases != null) {
    body['NetworkingConfig'] = {
      'EndpointsConfig': {
        network: {
          'Aliases': networkAliases,
        },
      },
    };
  }

  final queryParams = name != null ? <String, String>{'name': name} : null;
  final resp = await _request(
    'POST',
    '/containers/create',
    queryParams: queryParams,
    body: body,
  );
  _throwIfError(resp);
  final data = resp.bodyJson as Map<String, dynamic>;
  final id = data['Id'] as String?;
  if (id == null || id.isEmpty) {
    throw StateError(
      'Docker API returned a container create response with no Id field. '
      'Response body: ${resp.bodyString}',
    );
  }
  return id;
}