Examples
Five complete workflow configurations covering image pull and container start, database migration exec, maintenance cleanup, network orchestration, and container health check loop.
Example 1: Pull a Docker Image and Start a Container with Port Mapping
Step 1 — image/pull
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "image",
"operation": "pull",
"Image": "myregistry.io/webapp:{{ vars.image_tag }}",
"Tag": "{{ vars.image_tag }}"
}
Step 2 — container/create
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "container",
"operation": "create",
"Image": "myregistry.io/webapp:{{ vars.image_tag }}",
"Name": "webapp-{{ vars.deploy_id }}",
"Env": "APP_ENV={{ vars.environment }}\nDB_HOST={{ vars.db_host }}\nDB_PORT=5432\nSECRET_KEY={{ vars.app_secret_key }}",
"PortBindings": "{\"8080/tcp\":[{\"HostPort\":\"{{ vars.host_port }}\"}]}"
}
Step 3 — container/start
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "container",
"operation": "start",
"ContainerId": "{{ nodes.createContainer.output.containerId }}"
}
Expected outcome: The latest image is pulled from the registry, a new named container is created with injected environment variables, and the container starts with host port mapping active. The container ID is available for subsequent health check or exec steps.
Example 2: Execute a Database Migration Script Inside a Running Container
Step 1 — container/exec (run migrations)
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "container",
"operation": "exec",
"ContainerId": "{{ vars.app_container_id }}",
"Command": "python manage.py migrate --no-input",
"Privileged": false
}
Step 2 — IfCondition node (check exit code)
Condition: {{ nodes.runMigrations.output.exitCode }} == 0
True port → continue deployment workflow
False port → alert node with vars.stderr = {{ nodes.runMigrations.output.stderr }}
Step 3 — container/logs (capture migration output on success)
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "container",
"operation": "logs",
"ContainerId": "{{ vars.app_container_id }}",
"Tail": 50,
"Timestamps": true
}
Expected outcome: The migration command runs inside the already-running application container. A non-zero exit code routes the workflow to an alert node with the stderr output as the error message. On success, the last 50 timestamped log lines are stored for audit logging.
Example 3: Clean Up Stopped Containers and Dangling Images (Maintenance Pipeline)
Step 1 — container/list (all containers)
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "container",
"operation": "list",
"All": true
}
Step 2 — Loop node (iterate over stopped containers)
Collection: {{ nodes.listContainers.output }}
Filter: {{ item.State }} == "exited"
Step 2a — container/remove
{
"resource": "container",
"operation": "remove",
"ContainerId": "{{ item.Id }}",
"Force": false,
"RemoveVolumes": false
}
Step 3 — image/list (dangling images)
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "image",
"operation": "list",
"All": true
}
Step 4 — Loop node (remove dangling images)
Collection: {{ nodes.listImages.output }}
Filter: {{ item.RepoTags }} == null
Step 4a — image/remove
{
"resource": "image",
"operation": "remove",
"ImageId": "{{ item.Id }}",
"Force": false,
"PruneChildren": true
}
Expected outcome: All stopped containers are removed, and all untagged (dangling) images are pruned. The workflow logs counts of removed containers and images for the nightly operations digest email.
Example 4: Create an Isolated Network and Connect Microservice Containers
Step 1 — network/create
{
"DockerHost": "unix:///var/run/docker.sock",
"ConnectionType": "UnixSocket",
"resource": "network",
"operation": "create",
"Name": "test-net-{{ vars.run_id }}",
"Driver": "bridge",
"Internal": true,
"LabelsJson": "{\"run_id\":\"{{ vars.run_id }}\",\"env\":\"test\"}"
}
Step 2 — container/create + container/start (database)
{
"resource": "container",
"operation": "create",
"Image": "postgres:15",
"Name": "db-{{ vars.run_id }}",
"Env": "POSTGRES_DB=testdb\nPOSTGRES_USER=testuser\nPOSTGRES_PASSWORD={{ vars.db_password }}"
}
Step 3 — network/connect (database)
{
"resource": "network",
"operation": "connect",
"NetworkId": "{{ nodes.createNetwork.output.networkId }}",
"ContainerId": "{{ nodes.createDb.output.containerId }}",
"Aliases": "db,postgres"
}
Step 4 — container/create + container/start + network/connect (API)
{
"resource": "container",
"operation": "create",
"Image": "myrepo/api:{{ vars.image_tag }}",
"Name": "api-{{ vars.run_id }}",
"Env": "DATABASE_URL=postgresql://testuser:{{ vars.db_password }}@db:5432/testdb"
}
network/connect:
{
"NetworkId": "{{ nodes.createNetwork.output.networkId }}",
"ContainerId": "{{ nodes.createApi.output.containerId }}",
"Aliases": "api,backend"
}
Step 5 — Teardown: container/remove + network/remove
// Remove API container
{ "resource": "container", "operation": "remove", "ContainerId": "{{ nodes.createApi.output.containerId }}", "Force": true }
// Remove DB container
{ "resource": "container", "operation": "remove", "ContainerId": "{{ nodes.createDb.output.containerId }}", "Force": true }
// Remove network
{ "resource": "network", "operation": "remove", "NetworkId": "{{ nodes.createNetwork.output.networkId }}" }
Expected outcome: Both containers communicate on the isolated internal network using DNS aliases (
db, api). No internet access is available. After the test run completes (or fails), all containers and the network are removed — leaving no orphaned resources on the host.
Example 5: Container Health Check Loop — Stop and Restart if Unhealthy Stats
Step 1 — system/ping (verify daemon is reachable)
{
"DockerHost": "tcp://prod-docker-host:2376",
"ConnectionType": "TcpTls",
"TlsCertPath": "/etc/bizfirst/docker-certs/prod",
"TimeoutSeconds": 10,
"resource": "system",
"operation": "ping"
}
Step 2 — container/stats (collect snapshot)
{
"DockerHost": "tcp://prod-docker-host:2376",
"ConnectionType": "TcpTls",
"TlsCertPath": "/etc/bizfirst/docker-certs/prod",
"resource": "container",
"operation": "stats",
"ContainerId": "{{ vars.monitored_container_id }}",
"Stream": false
}
Step 3 — IfCondition node (memory threshold check)
Condition: {{ nodes.getStats.output.memoryUsageMB }} / {{ nodes.getStats.output.memoryLimitMB }} > 0.9
True port → Step 4 (restart)
False port → workflow ends (healthy)
Step 4 — container/restart (graceful restart with timeout)
{
"DockerHost": "tcp://prod-docker-host:2376",
"ConnectionType": "TcpTls",
"TlsCertPath": "/etc/bizfirst/docker-certs/prod",
"resource": "container",
"operation": "restart",
"ContainerId": "{{ vars.monitored_container_id }}",
"Timeout": 30
}
Step 5 — container/logs (capture last 100 lines before restart for audit)
{
"resource": "container",
"operation": "logs",
"ContainerId": "{{ vars.monitored_container_id }}",
"Tail": 100,
"Timestamps": true
}
Expected outcome: The daemon reachability is verified before any operation. If the container's memory consumption exceeds 90% of its configured limit, it is restarted with a 30-second graceful stop window. The last 100 log lines are captured before restart and stored in a workflow variable for inclusion in an alert notification.
Production watchdog note: For watchdog workflows targeting a remote Docker host, always use
ConnectionType: TcpTls with certificates stored in a secure, access-controlled path. Begin every watchdog run with system/ping to fail fast if the host is unreachable before attempting stat collection or restarts.