- Posted on
- • Artificial Intelligence
Artificial Intelligence Self-Hosted Artificial Intelligence Services
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Self-Hosted AI on Linux: Build Your Private AI Services with Bash
If you’re a Linux power user, you don’t need a cloud subscription to run serious AI. You can run chat assistants, voice transcription, and even retrieval-augmented generation (RAG) right on your own hardware—fast, private, and scriptable with Bash.
Problem/value:
Cloud AI risks vendor lock-in, unpredictable costs, and privacy concerns.
Self-hosted AI gives you control: keep data on-prem, tune the stack, and automate everything with the CLI you already love.
In this guide you’ll:
Spin up a local LLM API (Ollama)
Add a friendly web UI
Transcribe audio privately
Build a lightweight RAG pipeline with a vector database
Optionally front it all with Nginx
Notes:
Hardware: A modern CPU with 16+ GB RAM is comfortable for 7–8B parameter models. A GPU (CUDA/ROCm) helps a lot but isn’t required.
All commands are copy/paste friendly. Package manager installs are shown for apt, dnf, and zypper where used.
1) Prerequisites: CLI and Containers
Choose Docker or Podman (both are fine). Also install common tools.
APT (Debian/Ubuntu):
sudo apt update
# Option A: Docker
sudo apt install -y docker.io
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Option B: Podman
sudo apt install -y podman
# Common tools
sudo apt install -y curl git jq python3 python3-pip ffmpeg
DNF (Fedora/RHEL-like):
sudo dnf -y install curl git jq python3 python3-pip ffmpeg
# Option A: Podman
sudo dnf -y install podman
# Option B: Docker (Fedora often provides moby-engine)
sudo dnf -y install moby-engine
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
ZYPPER (openSUSE/SLE):
sudo zypper refresh
sudo zypper install -y curl git jq python3-pip ffmpeg
# Option A: Docker
sudo zypper install -y docker
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
# Option B: Podman
sudo zypper install -y podman
Log out/in (or run newgrp docker) if you just added yourself to the docker group.
2) Serve an LLM Locally with Ollama
Ollama is a lightweight LLM server that runs models locally and exposes a simple HTTP API.
Install Ollama:
curl -fsSL https://ollama.com/install.sh | sh
Run the server and pull a model:
# Start the Ollama API for this session
ollama serve >/tmp/ollama.log 2>&1 &
# Pull a capable small model (adjust as you like)
ollama pull llama3.1:8b
# Quick interactive chat
ollama run llama3.1:8b
Containerized alternative (Docker):
docker network create ai || true
docker run -d --name ollama --network ai --restart unless-stopped \
-p 11434:11434 -v ollama:/root/.ollama \
ollama/ollama:latest
# Pull a model inside the container
docker exec -it ollama ollama pull llama3.1:8b
Containerized alternative (Podman):
podman network create ai || true
podman run -d --name ollama --network ai --replace \
-p 11434:11434 -v ollama:/root/.ollama \
docker.io/ollama/ollama:latest
podman exec -it ollama ollama pull llama3.1:8b
Test the API with curl:
curl -s http://localhost:11434/api/generate -d '{
"model": "llama3.1:8b",
"prompt": "List three Linux commands to monitor CPU and memory.",
"stream": false
}' | jq -r .response
3) Add a Web UI (Open WebUI)
Open WebUI is a clean, self-hosted front end that talks to Ollama.
Docker:
docker run -d --name open-webui --network ai --restart unless-stopped \
-p 3000:8080 \
-e OLLAMA_BASE_URL=http://ollama:11434 \
-v open-webui:/app/backend/data \
ghcr.io/open-webui/open-webui:main
Podman:
podman run -d --name open-webui --network ai --replace \
-p 3000:8080 \
-e OLLAMA_BASE_URL=http://ollama:11434 \
-v open-webui:/app/backend/data \
ghcr.io/open-webui/open-webui:main
Open your browser to:
http://localhost:3000
4) Private Voice Transcription with Whisper (CPU/GPU)
Install dependencies (ffmpeg + pip already covered above), then install Whisper:
python3 -m pip install --user -U openai-whisper
Transcribe an audio file (CPU-friendly example):
whisper meeting.mp3 --model small --language en --fp16 False --output_format txt
You’ll get meeting.txt with the transcript—no cloud involved.
5) Build a Minimal RAG Stack (Qdrant + Ollama Embeddings)
We’ll use:
Ollama for embeddings (e.g., nomic-embed-text)
Qdrant for a vector database
Pull an embedding model:
ollama pull nomic-embed-text
Start Qdrant:
Docker:
docker run -d --name qdrant --network ai --restart unless-stopped \
-p 6333:6333 -v qdrant:/qdrant/storage \
qdrant/qdrant:latest
Podman:
podman run -d --name qdrant --network ai --replace \
-p 6333:6333 -v qdrant:/qdrant/storage \
docker.io/qdrant/qdrant:latest
Create a collection (nomic-embed-text produces 768-dim vectors):
curl -s -X PUT 'http://localhost:6333/collections/docs' \
-H 'Content-Type: application/json' \
-d '{
"vectors": { "size": 768, "distance": "Cosine" }
}' | jq
Index a directory of text files (docs/*.txt) into Qdrant:
#!/usr/bin/env bash
set -euo pipefail
MODEL="nomic-embed-text"
COLL="docs"
QDRANT="http://localhost:6333"
i=0
for f in docs/*.txt; do
[ -f "$f" ] || continue
content=$(tr -s '[:space:]' ' ' <"$f" | head -c 4000)
vec=$(curl -s http://localhost:11434/api/embeddings \
-d "{\"model\":\"$MODEL\",\"prompt\":$(jq -R -s . <<<"$content")}" | jq -c .embedding)
curl -s -X PUT "$QDRANT/collections/$COLL/points?wait=true" \
-H 'Content-Type: application/json' \
-d "{\"points\":[{\"id\":$i,\"vector\":$vec,\"payload\":{\"file\":\"$f\"}}]}" >/dev/null
echo "Indexed $f -> id $i"
((i++))
done
Search and answer with context:
query="Reset a forgotten user password on Ubuntu"
qvec=$(curl -s http://localhost:11434/api/embeddings \
-d "{\"model\":\"nomic-embed-text\",\"prompt\":$(jq -R -s . <<<"$query")}" | jq -c .embedding)
files=$(curl -s -X POST "http://localhost:6333/collections/docs/points/search" \
-H 'Content-Type: application/json' \
-d "{\"vector\": $qvec, \"limit\": 3}" | jq -r '.result[].payload.file')
context=""
for f in $files; do
context="$context
---
$(echo "$f")
$(head -c 2000 "$f")"
done
curl -s http://localhost:11434/api/generate -d "{
\"model\":\"llama3.1:8b\",
\"prompt\":\"Using the context below, answer the question concisely.\n\nQuestion: $query\n\nContext:\n$context\",
\"stream\": false
}" | jq -r .response
That’s a simple, end-to-end private RAG flow.
Optional: Reverse Proxy with Nginx
Serve everything under friendly paths and prepare for TLS.
APT:
sudo apt install -y nginx
DNF:
sudo dnf install -y nginx
ZYPPER:
sudo zypper install -y nginx
Minimal proxy (http://your-host/ollama/ and /chat/):
sudo tee /etc/nginx/conf.d/ai.conf >/dev/null <<'EOF'
server {
listen 80;
server_name _;
client_max_body_size 32m;
location /ollama/ {
proxy_pass http://127.0.0.1:11434/;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
location /chat/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}
EOF
sudo nginx -t && sudo systemctl enable --now nginx
Add TLS with your preferred method (e.g., Caddy or certbot) when exposing to the internet. For private LAN use, consider firewall rules or a VPN.
Real-World Uses You Can Ship Today
Private DevOps runbook assistant: Index your team’s docs and answer on-call questions with RAG.
Meeting transcription and summaries: Whisper + an LLM prompt for action items and timelines.
Air‑gapped knowledge base: All components run locally; no outbound calls required.
Conclusion and Next Steps
You now have the building blocks for a full, private AI stack:
Ollama for LLM serving (CLI + API)
Open WebUI for a clean chat interface
Whisper for local transcription
Qdrant + embeddings for search and RAG
Pick one component and ship it today:
If you want immediate value, start with Ollama and Open WebUI.
If you handle sensitive audio, add Whisper.
If you need “docs-aware” answers, wire up Qdrant.
When you’re ready, automate with Bash scripts, add systemd units, and proxy with Nginx. Your infrastructure, your data, your rules—on Linux.