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