Exec into containers is a powerful operation. A command running inside a container has access to all container filesystems, environment variables, and network interfaces. Restrict the commands that can be executed via workflow configuration. Never interpolate user input into the Command field — this is equivalent to a remote code execution vulnerability.
When to Use
- Database migrations: Run database migration scripts inside an application container after deployment, before routing traffic.
- Health check verification: Execute an application-level health check script to confirm readiness beyond a simple TCP port check.
- Filesystem inspection: Check whether expected configuration files or directories exist inside a running container during a deployment verification step.
- Admin commands: Trigger a cache flush, reindex operation, or admin task inside a running application without restarting the container.
- Debugging: Capture the current state of the application's working directory or running processes for incident investigation.
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 | Timeout for exec operation. Increase for long-running commands (e.g. migrations). optional |
Operation Fields
| Field | Type | Required | Description |
resource | string | required | Must be container |
operation | string | required | Must be exec |
ContainerId | string | required | Container ID or name. Container must be running. |
Command | string | required | JSON array of command and arguments: ["sh", "-c", "ls /app"]. Never use string concatenation with external input. |
AttachStdin | bool | false | Attach stdin to the exec process. optional |
AttachStdout | bool | true | Capture stdout from the exec process. optional |
AttachStderr | bool | true | Capture stderr from the exec process. optional |
Sample Configuration
{
"resource": "container",
"operation": "exec",
"ContainerId": "api-service",
"Command": "[\"sh\", \"-c\", \"node scripts/health-check.js\"]",
"AttachStdout": true,
"AttachStderr": true,
"connection": {
"dockerHost": "unix:///var/run/docker.sock",
"connectionType": "unixsocket",
"timeoutSeconds": 15
}
}
Validation Errors
| Error Code | Trigger | Resolution |
MISSING_INPUT | ContainerId or Command is empty | Both fields are required. Command must be a valid JSON array string. |
NOT_FOUND | Container does not exist | Verify the container name or ID. |
CONFLICT | Container is not running (it is stopped or paused) | Start the container first with container/start. Exec only works on running containers. |
CONFIG_ERROR | Malformed JSON in Command field | Validate that the Command value is a proper JSON array: ["cmd", "arg1", "arg2"]. |
DOCKER_API_ERROR | Command executable not found inside container | Verify the command binary exists in the container's PATH. |
Output
Success Port
| Field | Type | Description |
exitCode | int | Exit code of the executed command. 0 = success, non-zero = failure. |
stdout | string | Standard output from the command |
stderr | string | Standard error from the command |
status | string | success — note: this means the exec call completed, not that the command itself succeeded. Always check exitCode. |
resource | string | container |
operation | string | exec |
Error Port
The error port fires for Docker API-level failures (container not found, daemon errors). A command that runs but exits with a non-zero code still fires the success port — check exitCode !== 0 with an If Condition node to handle command failures.
Sample Output
{
"exitCode": 0,
"stdout": "Health check passed\nDatabase connection: OK\nRedis connection: OK\nAll checks passed (3/3)",
"stderr": "",
"status": "success",
"resource": "container",
"operation": "exec"
}
Expression Reference
| Expression | Returns |
{{ $output.exitCode }} | Command exit code — 0 is success |
{{ $output.exitCode === 0 }} | Boolean — true if command succeeded |
{{ $output.stdout }} | Full standard output string |
{{ $output.stderr }} | Full standard error string |
{{ $output.stderr.length > 0 }} | Boolean — true if stderr has content |
{{ $output.stdout.trim() }} | Trimmed output for use in conditions or messages |
Node Policies & GuardRails
| Policy | Detail |
| Never interpolate user input into Command | GuardRail static analysis blocks any Command value that contains {{ $trigger.* }}, {{ $input.* }}, or other external input expressions. Commands must be fully static or built from validated workflow variables only. |
| Use pre-approved command lists | Organizations should maintain a whitelist of permitted exec commands. Arbitrary exec grants container-level root access. GuardRail exec policies can enforce prefix matching against an allowed command list. |
| Check exitCode, not just status | The node success port fires even when the command exits with code 1 or higher. Always follow exec with an If Condition node checking exitCode === 0. |
| Increase timeout for migrations | Database migrations can take minutes. Set timeoutSeconds high enough to avoid premature cancellation. |
| No remote TCP without TLS | Use tcptls for remote Docker hosts. |
Examples
Example 1: Run database migrations before traffic cutover
{
"resource": "container",
"operation": "exec",
"ContainerId": "api-service-blue",
"Command": "[\"node\", \"scripts/migrate.js\", \"--env\", \"production\"]",
"AttachStdout": true,
"AttachStderr": true,
"connection": { "timeoutSeconds": 120 }
}
// If Condition: exitCode === 0 → proceed to traffic switch
// Else: stop blue container, rollback deploy
Example 2: Application health check after start
{
"resource": "container",
"operation": "exec",
"ContainerId": "api-service",
"Command": "[\"sh\", \"-c\", \"wget -qO- http://localhost:8080/health || exit 1\"]",
"AttachStdout": true,
"AttachStderr": true
}
// exitCode 0 → container is healthy, proceed
// exitCode 1 → container started but application not ready, retry or alert
Example 3: Trigger cache flush in running application
{
"resource": "container",
"operation": "exec",
"ContainerId": "cache-service",
"Command": "[\"redis-cli\", \"FLUSHDB\"]",
"AttachStdout": true,
"AttachStderr": true
}
// stdout will contain "OK" on success
// If Condition: stdout.trim() === 'OK' → log success
// Else: alert operations team