better-openclaw supports bare-metal deployment with a hybrid architecture: services with native recipes run directly on the host, while others run in Docker.
Deployment types
better-openclaw offers two deployment models:
| Type | Description | Use Case |
|---|
| Docker | All services in containers | Default, easiest setup |
| Bare-metal | Hybrid: native + Docker | Better performance, resource efficiency |
Docker deployment (default)
All services run in containers:
npx create-better-openclaw --deployment-type docker
Start the stack:
cd my-openclaw-stack
docker compose up -d
Services with native recipes run on the host, others in Docker:
npx create-better-openclaw --deployment-type bare-metal --platform linux/amd64
Generated structure:
my-openclaw-stack/
├── install.sh # Top-level installer
├── docker-compose.yml # Docker-only services + gateway
├── native/
│ └── install-linux.sh # Native service installer
├── .env
└── config/
Run the installer:
cd my-openclaw-stack
bash install.sh
The installer:
- Installs native services on the host (e.g., Redis via apt/dnf)
- Starts native services (systemd)
- Runs
docker compose up for remaining services
npx create-better-openclaw \
--deployment-type bare-metal \
--platform linux/amd64
Generates: install.sh + native/install-linux.shPackage managers:
- apt (Ubuntu/Debian)
- dnf/yum (RHEL/CentOS/Fedora)
npx create-better-openclaw \
--deployment-type bare-metal \
--platform macos/arm64
Generates: install.sh + native/install-macos.shPackage managers:npx create-better-openclaw \
--deployment-type bare-metal \
--platform windows/amd64
Generates: install.ps1 + native/install-windows.ps1Package managers:
- Chocolatey or Scoop (recommended)
Services with native support
Currently, these services have native recipes:
| Service | Linux | macOS | Windows | Native Benefits |
|---|
| Redis | ✅ apt/dnf + systemd | ✅ brew + launchd | ❌ | Lower latency, better memory usage |
| PostgreSQL | 🚧 Coming soon | 🚧 | 🚧 | Direct disk I/O, native auth |
| Caddy | 🚧 | 🚧 | 🚧 | System-wide HTTPS, native systemd |
| Prometheus | 🚧 | 🚧 | 🚧 | Direct host metrics access |
Services without native recipes remain Docker-only (e.g., Ollama, n8n, Grafana).
Redis native deployment
When you select bare-metal deployment, Redis is installed natively:
Linux installation
#!/usr/bin/env bash
set -euo pipefail
# ─── 🔴 Redis ─────────────────────────────────────────
# Install Redis
if command -v apt-get &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y redis-server
elif command -v dnf &> /dev/null; then
sudo dnf install -y redis
fi
# Write Redis config
sudo tee /etc/redis/redis.conf > /dev/null << 'EOF'
bind 0.0.0.0
port 6379
protected-mode no
maxmemory 2gb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000
EOF
# Start Redis
sudo systemctl enable redis-server
sudo systemctl start redis-server
echo 'Native services started.'
Connecting from Docker
The OpenClaw gateway runs in Docker and connects to native Redis on the host via host.docker.internal:
services:
openclaw-gateway:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
REDIS_HOST: host.docker.internal
REDIS_PORT: 6379
This is automatically configured when you use --deployment-type bare-metal.
Environment variables
# Redis hostname for OpenClaw (native: host.docker.internal)
# Service: Redis | Required: Yes | Secret: No
REDIS_HOST=host.docker.internal
# Redis port for OpenClaw
# Service: Redis | Required: Yes | Secret: No
REDIS_PORT=6379
Installer scripts
Linux installer
Generated install.sh for Linux:
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Colors
RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}ℹ $*${NC}"; }
ok() { echo -e "${GREEN}✅ $*${NC}"; }
err() { echo -e "${RED}❌ $*${NC}" >&2; }
# Native services (install/start on host first)
if [ -f "native/install-linux.sh" ]; then
info "Installing and starting native services..."
bash native/install-linux.sh
ok "Native services ready."
fi
# Install Docker if missing
if ! command -v docker &> /dev/null; then
info "Installing Docker..."
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
ok "Docker installed."
fi
# Check Docker Compose
if ! docker compose version &> /dev/null; then
err "Docker Compose (v2) not found."
exit 1
fi
# Start stack
if [ -f ".env.example" ] && [ ! -f ".env" ]; then
cp .env.example .env
fi
info "Starting stack..."
docker compose up -d --remove-orphans
ok "Stack started. Gateway: http://localhost:${OPENCLAW_GATEWAY_PORT:-18789}"
macOS installer
Similar to Linux, but uses Homebrew:
brew install docker
open -a Docker # Start Docker Desktop
Windows installer
PowerShell script (install.ps1):
# Check Docker Desktop
$docker = Get-Command docker -ErrorAction SilentlyContinue
if (-not $docker) {
Write-Host "Docker not found. Install from: https://docs.docker.com/desktop/install/windows-install/"
exit 1
}
# Run native services
if (Test-Path "native/install-windows.ps1") {
& .\native\install-windows.ps1
}
# Start Docker stack
docker compose up -d --remove-orphans
- Lower latency: No Docker network overhead for native services
- Direct I/O: Native services access disk/network directly
- Memory efficiency: No container memory overhead (~50-100 MB per container)
Resource management
- System integration: Native services use systemd/launchd for lifecycle management
- Better observability: System tools (systemctl, journalctl) work natively
- Easier debugging: Logs in standard locations (
/var/log, journalctl)
Flexibility
- Mix and match: Run performance-critical services natively, others in Docker
- Gradual migration: Start with Docker, move to native as needed
- Existing infrastructure: Integrate with existing native services
Complexity
- Platform-specific: Different install steps for each OS
- Manual cleanup: Uninstalling requires OS-specific commands
- Dependency management: Native package managers handle dependencies
Portability
- Less portable: Can’t just copy the directory to another machine
- Version pinning: Harder to guarantee exact versions across systems
- State management: Native services have state in system directories
Security
- System-wide access: Native services run with more privileges
- No isolation: Services can access entire filesystem
- Shared resources: Can conflict with other system services
Managing native services
Linux (systemd)
# Check status
sudo systemctl status redis-server
# View logs
sudo journalctl -u redis-server -f
# Restart
sudo systemctl restart redis-server
# Stop
sudo systemctl stop redis-server
# Disable auto-start
sudo systemctl disable redis-server
macOS (launchd)
# Check status
brew services list
# Restart
brew services restart redis
# Stop
brew services stop redis
# View logs
tail -f /usr/local/var/log/redis.log
Windows (Services)
# Check status
Get-Service redis
# Restart
Restart-Service redis
# Stop
Stop-Service redis
# View logs
Get-EventLog -LogName Application -Source Redis
Uninstalling
Linux
# Stop Docker services
docker compose down -v
# Stop and remove native services
sudo systemctl stop redis-server
sudo systemctl disable redis-server
sudo apt-get remove --purge redis-server # or sudo dnf remove redis
# Remove data
sudo rm -rf /var/lib/redis
sudo rm -f /etc/redis/redis.conf
macOS
# Stop Docker services
docker compose down -v
# Stop and remove native services
brew services stop redis
brew uninstall redis
# Remove data
rm -rf /usr/local/var/db/redis
Windows
# Stop Docker services
docker compose down -v
# Uninstall via package manager
choco uninstall redis # or scoop uninstall redis
# Remove data
Remove-Item -Recurse -Force C:\ProgramData\Redis
Hybrid configurations
Example: Native Redis + Docker PostgreSQL
npx create-better-openclaw \
--services postgresql,redis,n8n \
--deployment-type bare-metal \
--platform linux/amd64
Result:
- Redis: Native (systemd)
- PostgreSQL: Docker (no native recipe yet)
- n8n: Docker (Node.js app, Docker-only)
- OpenClaw gateway: Docker (connects to both)
Example: All-Docker
For consistency across environments:
npx create-better-openclaw \
--services postgresql,redis,n8n \
--deployment-type docker
All services in Docker, even if native recipes exist.
Troubleshooting
Native service won’t start
Check service status:
# Linux
sudo systemctl status redis-server
sudo journalctl -u redis-server -n 50
# macOS
brew services list
tail -n 50 /usr/local/var/log/redis.log
Common issues:
- Port conflict: Another service using the port
- Permission denied: Config file not readable
- Missing dependencies: Package installation incomplete
Docker can’t connect to native service
Verify host.docker.internal:
docker compose exec openclaw-gateway ping host.docker.internal
If ping fails:
services:
openclaw-gateway:
extra_hosts:
- "host.docker.internal:172.17.0.1" # Docker bridge IP
Test connection:
docker compose exec openclaw-gateway redis-cli -h host.docker.internal ping
# Expected: PONG
Firewall blocking connections
Linux (UFW):
sudo ufw allow 6379/tcp comment 'Redis for OpenClaw'
sudo ufw reload
Linux (firewalld):
sudo firewall-cmd --permanent --add-port=6379/tcp
sudo firewall-cmd --reload
macOS:
System Preferences → Security & Privacy → Firewall → Firewall Options → Allow Redis
Port conflicts
Error: Address already in use
Find conflicting process:
# Linux/macOS
sudo lsof -i :6379
# Windows
netstat -ano | findstr :6379
Solutions:
- Stop the conflicting service
- Change the port in native service config:
# /etc/redis/redis.conf
port 6380
- Update
.env to match:
Redis: Native vs Docker
Native (systemd):
redis-benchmark -q -n 100000
PING_INLINE: 98234.56 requests per second
SET: 96712.23 requests per second
GET: 99800.40 requests per second
Docker:
docker compose exec redis redis-benchmark -q -n 100000
PING_INLINE: 92421.47 requests per second
SET: 90009.00 requests per second
GET: 94607.38 requests per second
Native Redis is ~5-8% faster for this workload.
Future native recipes
Planned for upcoming releases:
Contributions welcome! See CONTRIBUTING.md.