Blog / Tutorials

Private AI Stack on a VPS: Ollama + Open WebUI + n8n in One Setup

By the NoctHost TeamJuly 4, 20263 min read

Most AI tooling sends your data somewhere. OpenAI processes your prompts. Zapier runs your automations on their servers. Make stores your workflow history in the cloud.

A self-hosted AI stack changes this. Ollama handles inference locally. Open WebUI gives your team a chat interface. n8n automates workflows that call your local models. Everything runs on one VPS, nothing leaves it.

This guide sets up all three in a single Docker Compose stack.

Architecture

VPS
├── Ollama (port 11434)     — LLM inference
├── Open WebUI (port 3000)  — Chat interface
└── n8n (port 5678)         — Workflow automation
    └── calls Ollama via the internal network

All three communicate over a private Docker network. Only the web interfaces are exposed publicly, behind Nginx and HTTPS.

Requirements

  • VPS with 8 GB RAM minimum (16 GB recommended)
  • Ubuntu 22.04 or 24.04
  • A domain name

Step 1: Install Docker

apt update && apt upgrade -y
curl -fsSL https://get.docker.com | sh
apt install -y docker-compose-plugin

Step 2: Install Ollama

curl -fsSL https://ollama.com/install.sh | sh

Configure it for Docker network access:

mkdir -p /etc/systemd/system/ollama.service.d
cat > /etc/systemd/system/ollama.service.d/override.conf << EOF
[Service]
Environment="OLLAMA_HOST=0.0.0.0"
EOF
systemctl daemon-reload && systemctl restart ollama

Pull a model:

ollama pull llama3.1:8b

Step 3: Deploy the Full Stack

mkdir ~/ai-stack && cd ~/ai-stack
nano docker-compose.yml
version: '3.8'

services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    restart: always
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://host.docker.internal:11434
    volumes:
      - open-webui-data:/app/backend/data
    extra_hosts:
      - "host.docker.internal:host-gateway"

  n8n:
    image: n8nio/n8n:latest
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      - GENERIC_TIMEZONE=UTC
    volumes:
      - n8n-data:/home/node/.n8n
    extra_hosts:
      - "host.docker.internal:host-gateway"

volumes:
  open-webui-data:
  n8n-data:
docker compose up -d

Step 4: Configure Nginx

apt install -y nginx certbot python3-certbot-nginx

Create configs for both services:

for subdomain in ai n8n; do
cat > /etc/nginx/sites-available/$subdomain << EOF
server {
    server_name $subdomain.yourdomain.com;
    location / {
        proxy_pass http://localhost:$([ "$subdomain" = "ai" ] && echo "3000" || echo "5678");
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
    }
}
EOF
ln -s /etc/nginx/sites-available/$subdomain /etc/nginx/sites-enabled/
done

certbot --nginx -d ai.yourdomain.com -d n8n.yourdomain.com
nginx -t && systemctl reload nginx

Step 5: Connect n8n to Ollama

In n8n, create an HTTP Request node pointed at http://host.docker.internal:11434/api/generate. This lets your workflows call your local LLM directly — summarize documents, classify inputs, generate responses — without any external API.

What This Costs

A single NoctHost Pro VPS (4 vCPU, 8 GB RAM) handles this entire stack at $62/month. Compared to paying for ChatGPT Team ($25/user/month), n8n Cloud ($20/month), and API costs on top — this pays for itself quickly for any team running more than a few hundred AI-powered workflows per month.

Spin one up in about a minute

Email signup, pay with crypto, hourly billing. Trying a box costs cents — destroy it when you are done.

Deploy a server

Frequently asked

Can I add more services to this stack?
Yes — just add more services to the docker-compose.yml. Common additions include Flowise for visual AI pipelines, Qdrant for vector storage, and Langfuse for LLM observability.
How do I update everything?
Pull the latest images and refresh the model: cd ~/ai-stack, then docker compose pull, then docker compose up -d, and finally ollama pull llama3.1:8b to update the model.
Is this production-ready?
For team internal use, yes. For public-facing applications, add authentication to Open WebUI, rate limiting in Nginx, and daily backups of the Docker volumes.

Keep reading