Skip to main content

Docker Environment Setup

The Docker adapter enables seamless command execution within Docker containers, supporting both existing containers and ephemeral runs. It provides full lifecycle management, volume mounting, and container orchestration capabilities.

Installation and Prerequisites​

Docker Installation​

Ensure Docker is installed and accessible:

# Check Docker installation
docker --version
docker info

The Docker adapter automatically detects Docker installations in common locations:

  • /usr/local/bin/docker (Docker Desktop on macOS)
  • /usr/bin/docker (Linux)
  • /opt/homebrew/bin/docker (Homebrew on macOS)

Basic Configuration​

import { $ } from '@xec-sh/core';

// Basic Docker execution using existing container
const result = await $({
adapterOptions: {
type: 'docker',
container: 'my-container'
}
})`ls -la /app`;

console.log(result.stdout);

Configuration Options​

Adapter Configuration​

Configure the Docker adapter with various options:

import { DockerAdapter } from '@xec-sh/core';

const docker = new DockerAdapter({
// Docker daemon configuration
socketPath: '/var/run/docker.sock', // Unix socket path
host: 'localhost', // Docker host
port: 2376, // Docker port
version: '1.41', // API version

// Default execution options
defaultExecOptions: {
User: '1000:1000', // Default user:group
WorkingDir: '/app', // Default working directory
Env: ['NODE_ENV=production'], // Default environment variables
Privileged: false, // Privileged mode
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Tty: false
},

// Auto-creation configuration
autoCreate: {
enabled: true, // Auto-create containers
image: 'alpine:latest', // Default image
autoRemove: true, // Remove after execution
volumes: ['/tmp:/tmp:rw'] // Default volumes
}
});

// Use the configured adapter
const result = await docker.execute({
command: 'whoami',
adapterOptions: {
type: 'docker',
container: 'my-app'
}
});

Container Options​

Specify container-specific execution options:

// Execute in existing container
await $({
adapterOptions: {
type: 'docker',
container: 'web-server',
user: 'www-data',
workdir: '/var/www/html',
tty: true
}
})`npm test`;

// Execute with environment variables
await $({
env: {
NODE_ENV: 'development',
DEBUG: 'app:*'
},
adapterOptions: {
type: 'docker',
container: 'node-app'
}
})`node server.js`;

Execution Modes​

Exec Mode​

Execute commands in existing containers:

// Execute in running container
const logs = await $({
adapterOptions: {
type: 'docker',
container: 'nginx-proxy',
runMode: 'exec' // Explicit exec mode
}
})`tail -f /var/log/nginx/access.log`;

Run Mode​

Create ephemeral containers for command execution:

// Run command in new container
const result = await $({
adapterOptions: {
type: 'docker',
container: 'test-runner', // Container name (optional)
image: 'node:18-alpine', // Required for run mode
runMode: 'run',
volumes: ['./src:/app/src:ro'],
workdir: '/app',
autoRemove: true // Clean up after execution
}
})`npm test`;

// Auto-detected run mode (when image is specified)
await $({
adapterOptions: {
type: 'docker',
image: 'ubuntu:22.04', // Image triggers run mode
volumes: ['./data:/data']
}
})`ls -la /data`;

Environment Variables​

Setting Environment Variables​

Pass environment variables to containers:

// Method 1: Command-level environment
await $({
env: {
API_KEY: 'secret-key',
DB_HOST: 'database.local',
PORT: '3000'
},
adapterOptions: {
type: 'docker',
container: 'api-server'
}
})`printenv | grep -E "API_KEY|DB_HOST|PORT"`;

// Method 2: Adapter default environment
const docker = new DockerAdapter({
defaultExecOptions: {
Env: [
'NODE_ENV=production',
'LOG_LEVEL=info'
]
}
});

Environment Variable Precedence​

Environment variables are merged with the following precedence:

  1. Command-level env (highest priority)
  2. Adapter defaultExecOptions.Env
  3. Global defaultEnv configuration
// Demonstrates environment variable precedence
const docker = new DockerAdapter({
defaultEnv: { GLOBAL: 'global-value' },
defaultExecOptions: {
Env: ['ADAPTER=adapter-value']
}
});

await $({
env: { COMMAND: 'command-value' },
adapterOptions: {
type: 'docker',
container: 'env-test'
}
})`printenv | sort`;
// Output includes: GLOBAL=global-value, ADAPTER=adapter-value, COMMAND=command-value

Working Directories​

Setting Working Directory​

Configure the working directory for command execution:

// Set working directory
await $({
adapterOptions: {
type: 'docker',
container: 'dev-container',
workdir: '/workspace/project'
}
})`pwd && ls -la`;

// Relative paths in container
await $({
adapterOptions: {
type: 'docker',
container: 'builder',
workdir: '/build'
}
})`
./configure --prefix=/usr/local
make && make install
`;

User and Permissions​

Running as Specific User​

Execute commands as different users:

// Run as specific user
await $({
adapterOptions: {
type: 'docker',
container: 'app-container',
user: 'appuser' // Username or UID
}
})`whoami && id`;

// Run as user:group
await $({
adapterOptions: {
type: 'docker',
container: 'data-processor',
user: '1000:1000' // UID:GID
}
})`touch /tmp/test-file && ls -la /tmp/test-file`;

// Default user from adapter configuration
const docker = new DockerAdapter({
defaultExecOptions: {
User: 'nobody:nogroup'
}
});

TTY and Interactive Mode​

TTY Configuration​

Configure TTY and interactive modes:

// Interactive TTY mode
await $({
stdin: 'echo "Hello from stdin"',
adapterOptions: {
type: 'docker',
container: 'interactive-app',
tty: true
}
})`cat`;

// Non-TTY mode for scripting
await $({
adapterOptions: {
type: 'docker',
container: 'batch-processor',
tty: false
}
})`
for i in {1..5}; do
echo "Processing batch $i"
sleep 1
done
`;

The adapter automatically detects TTY support and optimizes settings:

  • Interactive mode enabled when stdin is provided or TTY requested
  • TTY mode only enabled when environment supports it
  • Warnings shown when TTY requested but not available

Container Validation​

Security Validation​

The Docker adapter performs security validation on container names:

// Valid container names
const validNames = [
'my-container',
'web_server',
'app-v1.2.3',
'namespace_app'
];

// Invalid container names (will throw errors)
const invalidNames = [
'', // Empty name
'container; rm -rf /', // Command injection
'../container', // Path traversal
'container|malicious', // Shell metacharacters
'/absolute/path' // Absolute path
];

// Container names must match: [a-zA-Z0-9][a-zA-Z0-9_.-]*
try {
await $({
adapterOptions: {
type: 'docker',
container: 'valid-name'
}
})`echo "Safe execution"`;
} catch (error) {
console.error('Container validation failed:', error.message);
}

Error Handling​

Common Error Scenarios​

Handle Docker-specific errors:

import { DockerError } from '@xec-sh/core';

try {
await $({
adapterOptions: {
type: 'docker',
container: 'non-existent'
}
})`echo "test"`;
} catch (error) {
if (error instanceof DockerError) {
console.error('Docker error:', error.container, error.operation, error.message);
}
}

// Handle container not found
try {
await $({
adapterOptions: {
type: 'docker',
container: 'missing-container'
}
})`ls /app`;
} catch (error) {
if (error.message.includes('not found')) {
console.log('Container does not exist');
// Auto-create or handle gracefully
}
}

Auto-Creation Error Handling​

Configure automatic container creation:

const docker = new DockerAdapter({
autoCreate: {
enabled: true,
image: 'alpine:latest',
autoRemove: true
}
});

// Will auto-create container if it doesn't exist
try {
const result = await docker.execute({
command: 'echo',
args: ['Container auto-created'],
adapterOptions: {
type: 'docker',
container: 'auto-created-container'
}
});
console.log('Success:', result.stdout);
} catch (error) {
console.error('Auto-creation failed:', error.message);
}

Availability Check​

Docker Environment Detection​

Check Docker availability before execution:

import { DockerAdapter } from '@xec-sh/core';

const docker = new DockerAdapter();

// Check if Docker is available
const isAvailable = await docker.isAvailable();
if (!isAvailable) {
console.error('Docker is not available');
process.exit(1);
}

// Check Docker version
const version = await $({
adapterOptions: { type: 'docker', container: 'temp' }
})`docker --version`.catch(() => null);

if (version?.exitCode === 0) {
console.log('Docker version:', version.stdout.trim());
}

Resource Cleanup​

Automatic Cleanup​

The Docker adapter automatically manages temporary containers:

const docker = new DockerAdapter({
autoCreate: {
enabled: true,
autoRemove: true // Automatically remove temporary containers
}
});

// Temporary containers are automatically cleaned up
await docker.execute({
command: 'echo',
args: ['Temporary execution'],
adapterOptions: {
type: 'docker',
container: 'temp-container'
}
});

// Manual cleanup
await docker.dispose(); // Cleans up all temporary containers

Event Monitoring​

Monitor Docker operations with events:

docker.on('docker:run', (event) => {
console.log('Container run:', event.image, event.container);
});

docker.on('docker:exec', (event) => {
console.log('Container exec:', event.container, event.command);
});

docker.on('temp:cleanup', (event) => {
console.log('Cleaning up:', event.path);
});

// Execute command with event monitoring
await $({
adapterOptions: {
type: 'docker',
image: 'alpine',
container: 'monitored'
}
})`echo "Monitored execution"`;

Best Practices​

Performance Optimization​

  • Use existing containers for repeated executions
  • Enable connection reuse for multiple operations
  • Use appropriate base images (alpine for minimal overhead)
  • Configure resource limits appropriately

Security Considerations​

  • Always validate container names
  • Use non-root users when possible
  • Mount volumes with appropriate permissions
  • Avoid passing secrets in environment variables
  • Use secure secret management systems

Resource Management​

  • Enable auto-removal for ephemeral containers
  • Monitor container resource usage
  • Clean up unused images and containers
  • Use multi-stage builds for smaller images
// Example of optimized Docker usage
const docker = new DockerAdapter({
autoCreate: {
enabled: true,
image: 'alpine:latest',
autoRemove: true
},
defaultExecOptions: {
User: '1000:1000', // Non-root user
WorkingDir: '/workspace'
}
});

// Efficient execution pattern
const results = await Promise.all([
docker.execute({
command: 'echo',
args: ['Task 1'],
adapterOptions: { type: 'docker', container: 'worker-1' }
}),
docker.execute({
command: 'echo',
args: ['Task 2'],
adapterOptions: { type: 'docker', container: 'worker-2' }
})
]);

// Cleanup
await docker.dispose();