Multi-Environment Execution
Xec provides a unified interface for executing commands across multiple environments, from local shells to remote Kubernetes clusters. This section covers how to configure and use each environment type effectively.
Supported Environmentsβ
Local Environmentβ
Execute commands directly on your local machine using native shell integration. The local adapter supports both Node.js and Bun runtimes with automatic detection.
Key Features:
- Native shell execution (bash, sh, zsh, cmd.exe)
- Bun runtime optimization when available
- Process lifecycle management
- Signal handling and timeout support
- Stream piping and buffering
Basic Usage:
import { $ } from '@xec-sh/core';
// Simple local execution
const result = await $`ls -la`;
console.log(result.stdout);
// With custom shell
const output = await $.with({ shell: '/bin/zsh' })`echo $ZSH_VERSION`;
SSH Environmentβ
Execute commands on remote servers via SSH with advanced connection management.
Key Features:
- Connection pooling with automatic reuse
- SSH tunneling and port forwarding
- Batch operations across multiple hosts
- Secure password handling
- SFTP file transfers
- Keep-alive and auto-reconnect
Basic Usage:
// Single host execution
const result = await $({
ssh: {
host: 'server.example.com',
username: 'user',
privateKey: '/path/to/key'
}
})`uname -a`;
// Connection pooling (enabled by default)
const ssh = {
host: 'server.example.com',
username: 'user',
connectionPool: {
enabled: true,
maxConnections: 10,
idleTimeout: 300000
}
};
await $.with({ ssh })`command1`;
await $.with({ ssh })`command2`; // Reuses connection
Docker Environmentβ
Execute commands inside Docker containers with comprehensive lifecycle management.
Key Features:
- Container lifecycle management
- Docker Compose integration
- Volume and network management
- Image building and management
- Real-time log streaming
- Health check support
Basic Usage:
// Execute in existing container
const result = await $({
docker: {
container: 'my-app'
}
})`npm test`;
// Run ephemeral container
const output = await $({
docker: {
image: 'node:18',
runMode: 'run',
rm: true
}
})`node --version`;
// With volume mounts
await $({
docker: {
image: 'alpine',
runMode: 'run',
volumes: ['/local/path:/container/path']
}
})`ls /container/path`;
Kubernetes Environmentβ
Execute commands in Kubernetes pods with cluster-aware features.
Key Features:
- Pod and container selection
- Multi-container pod support
- Port forwarding to services
- Real-time log streaming
- Namespace management
- Context switching
Basic Usage:
// Execute in pod
const result = await $({
kubernetes: {
pod: 'app-pod-xyz',
namespace: 'production'
}
})`df -h`;
// Specific container in multi-container pod
await $({
kubernetes: {
pod: 'app-pod-xyz',
container: 'web',
namespace: 'production'
}
})`nginx -t`;
// With context
await $({
kubernetes: {
pod: 'debug-pod',
context: 'staging-cluster'
}
})`env`;
Environment Detection and Auto-Selectionβ
Xec can automatically detect the appropriate environment based on the command configuration:
import { $, auto } from '@xec-sh/core';
// Auto-detect based on options
const result = await $(auto({
// Will use SSH if host is provided
host: process.env.REMOTE_HOST,
// Falls back to local if not
fallback: 'local'
}))`echo "Hello from ${await $`hostname`}"`;
Adapter Configurationβ
Global Configurationβ
Set default configurations that apply to all executions:
import { configure } from '@xec-sh/core';
configure({
// Default timeout for all commands
defaultTimeout: 30000,
// Default encoding
encoding: 'utf8',
// Maximum output buffer size
maxBuffer: 10 * 1024 * 1024,
// Throw on non-zero exit codes
throwOnNonZeroExit: true,
// Default environment variables
defaultEnv: {
NODE_ENV: 'production'
}
});
Per-Execution Configurationβ
Override settings for specific executions:
const result = await $({
timeout: 60000,
throwOnNonZeroExit: false,
env: {
DEBUG: 'true'
},
cwd: '/app'
})`npm run build`;
Environment Chainingβ
Execute commands that span multiple environments:
// Copy file from remote to local via Docker
const remotePath = '/remote/data.tar.gz';
const containerPath = '/tmp/data.tar.gz';
const localPath = './data.tar.gz';
// Download from remote server
await $.with({ ssh: sshConfig })`cat ${remotePath}`
.pipe($.with({ docker: { container: 'processor' } })`cat > ${containerPath}`);
// Process in Docker
await $.with({ docker: { container: 'processor' } })`
cd /tmp &&
tar -xzf data.tar.gz &&
./process.sh
`;
// Copy result to local
const processed = await $.with({ docker: { container: 'processor' } })`cat /tmp/result.json`;
await $`echo '${processed}' > ${localPath}`;
Parallel Execution Across Environmentsβ
Execute commands simultaneously across multiple environments:
import { parallel } from '@xec-sh/core';
const results = await parallel([
$`local-command`,
$.with({ ssh: server1 })`remote-command-1`,
$.with({ ssh: server2 })`remote-command-2`,
$.with({ docker: { container: 'app' } })`container-command`,
$.with({ kubernetes: { pod: 'pod-1' } })`pod-command`
]);
// Results array maintains order
const [local, remote1, remote2, docker, k8s] = results;
Error Handlingβ
Each environment provides specific error information:
try {
await $.with({ ssh: sshConfig })`false`;
} catch (error) {
if (error.code === 'ECONNREFUSED') {
console.error('SSH connection refused');
} else if (error.exitCode === 1) {
console.error('Command failed with exit code 1');
}
// Access environment-specific details
console.error('Host:', error.details?.host);
console.error('Command:', error.command);
}
Stream Processingβ
All environments support unified stream processing:
// Stream from SSH to Docker
await $.with({ ssh: remoteConfig })`tail -f /var/log/app.log`
.pipe($.with({ docker: { container: 'logger' } })`tee /logs/remote.log`);
// Real-time processing
const proc = $.with({ kubernetes: { pod: 'streamer' } })`watch -n 1 date`;
proc.stdout.on('data', (chunk) => {
console.log('K8s output:', chunk.toString());
});
// Graceful shutdown
setTimeout(() => proc.kill(), 10000);
Performance Optimizationβ
Connection Reuseβ
SSH and Kubernetes adapters automatically reuse connections:
// SSH connection pooling
const sshPool = {
host: 'server.com',
connectionPool: {
maxConnections: 5,
idleTimeout: 600000, // 10 minutes
keepAlive: true
}
};
// Execute 100 commands using only 5 connections
await Promise.all(
Array.from({ length: 100 }, (_, i) =>
$.with({ ssh: sshPool })`echo "Task ${i}"`
)
);
Batch Operationsβ
Execute commands efficiently across multiple targets:
const hosts = ['server1.com', 'server2.com', 'server3.com'];
// Parallel execution with connection pooling
const results = await Promise.all(
hosts.map(host =>
$.with({
ssh: {
host,
username: 'deploy',
connectionPool: { enabled: true }
}
})`systemctl restart app.service`
)
);
Best Practicesβ
1. Use Connection Poolingβ
Always enable connection pooling for SSH when executing multiple commands:
// Good - reuses connection
const ssh = { host: 'server', connectionPool: { enabled: true } };
await $.with({ ssh })`command1`;
await $.with({ ssh })`command2`;
// Bad - creates new connection each time
await $.with({ ssh: { host: 'server' } })`command1`;
await $.with({ ssh: { host: 'server' } })`command2`;
2. Handle Environment-Specific Errorsβ
Each environment can produce unique errors:
try {
await $.with({ docker: { container: 'app' } })`test -f /app/config.json`;
} catch (error) {
if (error.code === 'CONTAINER_NOT_FOUND') {
// Container doesn't exist
await $.with({ docker: { image: 'app:latest', runMode: 'run' } })`setup.sh`;
} else if (error.exitCode === 1) {
// File doesn't exist
await $.with({ docker: { container: 'app' } })`cp /defaults/config.json /app/`;
}
}
3. Use Appropriate Timeoutsβ
Set timeouts based on environment latency:
// Local - short timeout
await $.with({ timeout: 5000 })`quick-local-command`;
// SSH - medium timeout
await $.with({ ssh: config, timeout: 30000 })`remote-command`;
// K8s - longer timeout for cluster operations
await $.with({ kubernetes: config, timeout: 60000 })`kubectl apply -f manifest.yaml`;
4. Clean Up Resourcesβ
Always dispose of adapters when done:
import { createExecutionEngine } from '@xec-sh/core';
const engine = createExecutionEngine();
try {
await engine.execute({ ssh: config }, 'command');
} finally {
await engine.dispose(); // Closes all connections
}
Environment-Specific Featuresβ
Local: Shell Detectionβ
The local adapter automatically detects and uses the appropriate shell:
// Auto-detects bash, zsh, sh, or cmd.exe
await $`echo $SHELL`;
// Force specific shell
await $.with({ shell: '/bin/bash' })`echo $BASH_VERSION`;
SSH: Tunnelingβ
Create SSH tunnels for secure access:
import { createSSHTunnel } from '@xec-sh/core';
const tunnel = await createSSHTunnel({
ssh: sshConfig,
localPort: 3306,
remotePort: 3306,
remoteHost: 'database.internal'
});
// Use tunnel
const mysql = new MySQL({ host: 'localhost', port: 3306 });
// Clean up
await tunnel.close();
Docker: Compose Integrationβ
Work with Docker Compose projects:
import { compose } from '@xec-sh/core';
// Start services
await compose.up({
file: 'docker-compose.yml',
detach: true
});
// Execute in service
await $.with({
docker: {
container: 'myapp_web_1'
}
})`npm run migrate`;
// Stop services
await compose.down();
Kubernetes: Port Forwardingβ
Forward ports from Kubernetes services:
import { portForward } from '@xec-sh/core';
const forward = await portForward({
service: 'web-service',
namespace: 'default',
localPort: 8080,
remotePort: 80
});
// Access service locally
const response = await fetch('http://localhost:8080');
// Clean up
await forward.close();
Next Stepsβ
- Local Environment Setup - Configure local shell execution
- SSH Configuration - Set up SSH connections and authentication
- Docker Integration - Work with Docker containers
- Kubernetes Operations - Execute in Kubernetes clusters
- Hybrid Orchestration - Combine multiple environments