Skip to content

How to Self-Host AI Agents on a VPS for Business Automation

Purpose

In this post, I will demonstrate how to self-host AI agents on a VPS for 24/7 business automation using Docker.

Problem

I wanted AI agents running autonomously 24/7, but I ran into several problems with cloud-based solutions:

  • Dependency on third-party services creates single points of failure
  • Data privacy concerns when sensitive workflows run on external platforms
  • Need for custom integrations with internal tools and APIs
  • Cost unpredictability with cloud-based agent platforms
  • Limited control over the automation stack

I needed a solution that gives me full control, reduces dependency on external platforms, and allows agents to operate continuously with complete data privacy.

Environment

  • Ubuntu 22.04 VPS
  • Docker and Docker Compose
  • OpenClaw agent framework
  • Ollama for local model support
  • PostgreSQL for persistence
  • Caddy for reverse proxy with SSL

Solution

I deployed a complete AI agent stack on a VPS using Docker. Here’s how I did it.

Step 1: Choose Your VPS Provider

I selected a VPS with minimum specifications:

  • 4GB RAM
  • 2 vCPUs
  • 50GB SSD

Recommended providers include DigitalOcean, Linode, Hetzner, or AWS Lightsail. Ensure the provider allows Docker installation and outbound API calls.

Step 2: Install Docker and Docker Compose

install-docker.sh
# Update system
sudo apt update && sudo apt upgrade -y
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Add user to docker group
sudo usermod -aG docker $USER
# Log out and back in for group changes to take effect

Step 3: Create Project Structure

setup-directories.sh
# Create project directory
mkdir ~/ai-agents && cd ~/ai-agents
# Create necessary directories
mkdir -p data config logs backups

Step 4: Create Docker Compose Configuration

I created a complete docker-compose.yml with OpenClaw, PostgreSQL, Ollama, and Prometheus for monitoring:

docker-compose.yml
version: '3.8'
services:
openclaw:
image: openclaw/openclaw:latest
container_name: openclaw
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./data:/app/data
- ./config:/app/config
- ./logs:/app/logs
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- OLLAMA_BASE_URL=http://ollama:11434
- DATABASE_URL=postgresql://openclaw:${DB_PASSWORD}@postgres:5432/openclaw
depends_on:
- postgres
- ollama
networks:
- agent-network
postgres:
image: postgres:15-alpine
container_name: openclaw-postgres
restart: unless-stopped
environment:
- POSTGRES_USER=openclaw
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=openclaw
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- agent-network
ollama:
image: ollama/ollama:latest
container_name: ollama
restart: unless-stopped
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
networks:
- agent-network
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- agent-network
networks:
agent-network:
driver: bridge
volumes:
postgres_data:
ollama_data:

Step 5: Configure Environment Variables

create-env.sh
# Create .env file with your API keys
cat > .env << 'EOF'
OPENAI_API_KEY=your_openai_key_here
ANTHROPIC_API_KEY=your_anthropic_key_here
DB_PASSWORD=your_secure_db_password_here
ALERT_WEBHOOK=https://your-webhook-url.com/alert
EOF
# Secure the file
chmod 600 .env

Step 6: Set Up Reverse Proxy with SSL

I used Caddy for automatic HTTPS:

setup-caddy.sh
# Install Caddy
sudo apt install -y caddy
# Configure Caddyfile
cat > /etc/caddy/Caddyfile << 'EOF'
your-domain.com {
reverse_proxy localhost:3000
}
EOF
# Restart Caddy
sudo systemctl restart caddy

Step 7: Start the Stack

start-stack.sh
# Start all services
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f openclaw

Step 8: Create Health Check Script

I created a health check script to monitor agent health and restart if needed:

health-check.sh
#!/bin/bash
# health-check.sh - Monitor agent health and restart if needed
# Check if OpenClaw is responding
if ! curl -f http://localhost:3000/health > /dev/null 2>&1; then
echo "$(date): OpenClaw not responding, restarting..."
docker-compose restart openclaw
# Send alert
curl -X POST "${ALERT_WEBHOOK}" -d '{"text": "OpenClaw restarted due to health check failure"}'
fi
# Check disk space
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 80 ]; then
echo "$(date): Disk usage at ${DISK_USAGE}%"
# Clean up old logs
find ./logs -type f -mtime +7 -delete
fi

Step 9: Set Up Automated Tasks

setup-cron.sh
# Add to crontab with: crontab -e
# Run health check every 5 minutes
*/5 * * * * /home/user/ai-agents/health-check.sh >> /home/user/ai-agents/cron.log 2>&1
# Backup database daily at 2 AM
0 2 * * * docker exec openclaw-postgres pg_dump -U openclaw openclaw > /home/user/backups/openclaw_$(date +\%Y\%m\%d).sql

Step 10: Verify Everything Works

verify-setup.sh
# Check all containers are running
docker-compose ps
# Test OpenClaw health endpoint
curl http://localhost:3000/health
# Test Ollama
curl http://localhost:11434/api/version
# Test PostgreSQL connection
docker exec -it openclaw-postgres psql -U openclaw -d openclaw -c "SELECT 1;"

When I ran these commands, I saw:

verification-output.txt
$ docker-compose ps
NAME COMMAND STATUS PORTS
openclaw "/app/entrypoint.sh" Up 2 hours 0.0.0.0:3000->3000/tcp
openclaw-postgres "docker-entrypoint.s…" Up 2 hours 5432/tcp
ollama "/bin/ollama serve" Up 2 hours 0.0.0.0:11434->11434/tcp
prometheus "/bin/prometheus --c…" Up 2 hours 0.0.0.0:9090->9090/tcp
$ curl http://localhost:3000/health
{"status": "healthy", "version": "1.0.0"}

The Reason

Self-hosting AI agents on a VPS provides several key benefits:

Data Sovereignty: All sensitive business data stays on your infrastructure. No third-party access to your workflows or agent memory.

Cost Control: Predictable monthly VPS costs (around $20-40/month) versus variable cloud platform fees that can spike unexpectedly.

Customization Freedom: Modify agent behavior without platform restrictions. Add custom tools, integrate with internal APIs, and tune responses to your needs.

Reliability: No dependency on third-party uptime SLAs. Your agents run on your schedule.

Compliance: Meet regulatory requirements for data handling. Perfect for industries with strict data residency rules.

Common Mistakes to Avoid

I made these mistakes so you don’t have to:

Insufficient Resources: I initially deployed on a 1GB RAM VPS. The agent kept timing out. Upgrade to at least 4GB RAM for reliable operation.

No Backup Strategy: I lost agent memory and conversation history once. Now I back up the PostgreSQL database daily.

Ignoring Security: I initially exposed agent endpoints without authentication. Always use a reverse proxy with proper authentication.

Hardcoded Credentials: I accidentally committed API keys to git once. Use environment variables and add .env to .gitignore.

No Monitoring: Agents can fail silently. Set up Prometheus alerts for failures or anomalies.

Over-Reliance on Cloud Models: When OpenAI had an outage, my agents stopped. I added Ollama as a fallback for critical workflows.

Missing Rate Limits: I exhausted my API quota mid-task, leaving agents in inconsistent states. Now I implement rate limiting and checkpointing.

Summary

In this post, I demonstrated how to self-host AI agents on a VPS using Docker. The key components are: Docker Compose for orchestration, PostgreSQL for persistence, Ollama for local model fallback, and Caddy for secure access. Start with a basic deployment and add monitoring, backups, and health checks from day one.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments