When to Use
- CI/CD pipeline: Create a test container from the build image before running the test suite, allowing inspection of container config before starting.
- Ephemeral jobs: Create a single-use data processing container that runs one script and exits — then clean up with
container/remove.
- Blue-green deployment: Create the new (blue) container with an updated image and port bindings before cutting over traffic from the current (green) container.
- Service provisioning: Create an isolated database container for a new tenant with dedicated volumes and environment-specific configuration.
- Scheduled jobs: Create a container for a nightly batch job, start it, collect output, then remove it — keeping the host environment clean.
Never put secrets in Env strings directly. Use {{ $credentials.name }} expressions to inject values from the BizFirst Credentials Manager, or mount secret files as volumes. Hard-coded passwords in Env fields will appear in container inspect output and Docker daemon logs.
Configuration
Connection Fields
| Field | Type | Default | Description |
dockerHost | string | unix:///var/run/docker.sock | Docker socket path or TCP endpoint |
connectionType | string | unixsocket | unixsocket | tcp | tcptls |
tlsCertPath | string | — | TLS certificate directory. Required for tcptls. optional |
timeoutSeconds | int | 30 | Operation timeout in seconds. optional |
Operation Fields
| Field | Type | Required | Description |
resource | string | required | Must be container |
operation | string | required | Must be create |
Image | string | required | Image name and optional tag (e.g. myapp:v2.1.0, nginx:latest) |
Name | string | optional | Container name. Strongly recommended for workflow readability and idempotency checks. |
Command | string | optional | Override entrypoint command. JSON array string: ["node", "worker.js", "--mode", "batch"] |
Env | string | optional | JSON array of KEY=VALUE strings: ["NODE_ENV=production", "PORT=8080"] |
PortBindings | string | optional | JSON port mapping object. See format below. |
Volumes | string | optional | JSON volume mount specification. See format below. |
PortBindings Format
Map container ports to host ports. The key is {containerPort}/{protocol} and the value is an array of host binding objects:
{
"8080/tcp": [{ "HostIp": "", "HostPort": "3000" }],
"9229/tcp": [{ "HostIp": "127.0.0.1", "HostPort": "9229" }]
}
Volumes Format
Bind mounts and named volume mounts:
{
"/app/data": {},
"/app/config": {}
}
Sample Configuration
{
"resource": "container",
"operation": "create",
"Image": "myapp:v2.2.0",
"Name": "api-service-blue",
"Env": [
"NODE_ENV=production",
"PORT=8080",
"DB_URL={{ $credentials.dbUrl }}",
"API_KEY={{ $credentials.apiKey }}"
],
"PortBindings": "{\"8080/tcp\": [{\"HostIp\": \"\", \"HostPort\": \"3001\"}]}",
"Volumes": "{\"/app/data\": {}, \"/app/logs\": {}}",
"connection": {
"dockerHost": "unix:///var/run/docker.sock",
"connectionType": "unixsocket"
}
}
Validation Errors
| Error Code | Trigger | Resolution |
MISSING_INPUT | Image field is empty | Provide a valid image name. Run image/list to enumerate available images. |
NOT_FOUND | Image does not exist locally | Run image/pull before container/create to ensure the image is available. |
CONFLICT | Container name already in use | Remove the existing container with container/remove first, or use a unique name. |
CONFIG_ERROR | Malformed JSON in PortBindings or Env | Validate JSON syntax. Use a Function node to build the JSON string programmatically. |
DOCKER_API_ERROR | Docker daemon error during creation (e.g. invalid port) | Check the error message for specifics — common causes: port already in use, invalid volume path. |
Output
Success Port
| Field | Type | Description |
containerId | string | Full 64-character ID of the newly created container |
containerName | string | Name assigned to the container (with leading /) |
warnings | string[] | Non-fatal warnings from Docker daemon (e.g. deprecated options) |
status | string | success |
resource | string | container |
operation | string | create |
Error Port
On failure, the error port receives errorCode, message, resource, and operation. Handle CONFLICT separately if your workflow is idempotent — the container may already exist from a previous run.
Sample Output
{
"containerId": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890",
"containerName": "/api-service-blue",
"warnings": [],
"status": "success",
"resource": "container",
"operation": "create"
}
Expression Reference
| Expression | Returns |
{{ $output.containerId }} | Full ID of the created container |
{{ $output.containerId.substring(0, 12) }} | Short container ID (12 chars) for display |
{{ $output.containerName }} | Container name string (e.g. /api-service-blue) |
{{ $output.warnings.length }} | Number of creation warnings (0 = clean) |
{{ $output.warnings.join(', ') }} | Concatenated warning messages for logging |
Node Policies & GuardRails
| Policy | Detail |
| No secrets in Env strings | GuardRail static analysis flags any Env value that is not a {{ $credentials.* }} expression and contains patterns matching API keys, passwords, or tokens. |
| Always use named containers | Workflows that use container/create without a Name field are flagged in code review. Names enable idempotency checks and make cleanup workflows reliable. |
| Pull image before create | If the image is not local, the Docker daemon will attempt a pull. In production workflows, explicitly use image/pull beforehand for controlled versioning. |
| No remote TCP without TLS | Use tcptls for remote Docker hosts. Plain tcp is blocked in production GuardRail profiles. |
| Handle CONFLICT on error port | Re-running a workflow without cleanup will cause a name conflict. Add a CONFLICT handler that removes the old container or generates a unique name. |
Examples
Example 1: Blue-green deployment — create blue container
{
"resource": "container",
"operation": "create",
"Image": "myapp:{{ $trigger.newVersion }}",
"Name": "api-service-blue-{{ $trigger.deployId }}",
"Env": [
"NODE_ENV=production",
"PORT=8080",
"DB_URL={{ $credentials.dbUrl }}"
],
"PortBindings": "{\"8080/tcp\": [{\"HostIp\": \"\", \"HostPort\": \"3001\"}]}"
}
Example 2: Ephemeral batch job container
{
"resource": "container",
"operation": "create",
"Image": "batch-processor:stable",
"Name": "batch-job-{{ $now | date('YYYYMMDD-HHmmss') }}",
"Command": "[\"python\", \"run_batch.py\", \"--date\", \"{{ $trigger.batchDate }}\"]",
"Env": [
"S3_BUCKET={{ $vars.outputBucket }}",
"AWS_ACCESS_KEY_ID={{ $credentials.awsKeyId }}",
"AWS_SECRET_ACCESS_KEY={{ $credentials.awsSecretKey }}"
],
"Volumes": "{\"/app/output\": {}}"
}
Example 3: Tenant database provisioning
{
"resource": "container",
"operation": "create",
"Image": "postgres:15-alpine",
"Name": "db-tenant-{{ $trigger.tenantId }}",
"Env": [
"POSTGRES_DB={{ $trigger.tenantId }}",
"POSTGRES_USER=app",
"POSTGRES_PASSWORD={{ $credentials.tenantDbPassword }}"
],
"PortBindings": "{}",
"Volumes": "{\"/var/lib/postgresql/data\": {}}"
}