Skip to content

OpenViking Server Deployment: My Journey from Local Dev to Production

Purpose

This post shows how to deploy OpenViking server in production using Docker, Docker Compose, and Kubernetes.

The Problem

After building my AI agent with OpenViking SDK locally, I needed to deploy it as a service. My agent needed to share context across multiple sessions and users, but running everything locally wouldn’t work for production.

I tried running OpenViking locally first:

Terminal
pip install openviking
openviking-server --config ./ov.conf

This worked for development, but when I tried to deploy to my server, I hit several issues:

deployment-issues.txt
Issue 1: No authentication - anyone could access the API
Issue 2: Data lost on restart - no persistent storage
Issue 3: Hard to scale - running on bare metal
Issue 4: No monitoring - couldn't see server health

I needed a proper production deployment strategy.

Deployment Options

OpenViking provides three deployment paths:

  1. Direct installation - Good for testing, bad for production
  2. Docker/Docker Compose - Good for single-server production
  3. Kubernetes (Helm) - Good for distributed, scalable deployments

Let me show you what worked for each.

Option 1: Docker Deployment

I started with Docker since it’s the simplest containerized approach.

Building the Image

First, I checked the Dockerfile:

Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Expose port
EXPOSE 1933
# Run server
CMD ["openviking-server", "--config", "/data/ov.conf"]

I built the image:

Terminal
docker build -t openviking:latest .

Running the Container

My first attempt failed:

Terminal
docker run -d -p 1933:1933 openviking:latest
# Error: Config file not found at /data/ov.conf

I realized I needed to mount my config file. I created a config directory:

Terminal
mkdir -p ./openviking-data

Then created the config file:

openviking-data/ov.conf
{
"storage": {
"workspace": "/data/openviking"
},
"log": {
"level": "INFO",
"output": "stdout"
},
"embedding": {
"dense": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "text-embedding-3-large",
"dimension": 3072
}
},
"vlm": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "gpt-4o"
}
}

Now I ran with volume mounts:

Terminal
docker run -d \
--name openviking \
-p 1933:1933 \
-v ./openviking-data:/data \
-e OPENVIKING_CONFIG_FILE=/data/ov.conf \
-e OPENAI_API_KEY=sk-your-key-here \
openviking:latest

I verified it was running:

Terminal
curl http://localhost:1933/health
# {"status": "ok"}

But I still had a problem: no authentication.

Adding Authentication

I added authentication to my config:

openviking-data/ov.conf
{
"storage": {
"workspace": "/data/openviking"
},
"log": {
"level": "INFO",
"output": "stdout"
},
"auth": {
"enabled": true,
"api_keys": ["my-secret-api-key-12345"]
},
"embedding": {
"dense": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "text-embedding-3-large",
"dimension": 3072
}
},
"vlm": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "gpt-4o"
}
}

Now clients need to include the API key:

Terminal
# Without API key - fails
curl http://localhost:1933/v1/resources
# {"error": "Unauthorized"}
# With API key - works
curl -H "X-API-Key: my-secret-api-key-12345" \
http://localhost:1933/v1/resources
# {"resources": []}

Option 2: Docker Compose

For a more complete setup, I used Docker Compose. This is what I recommend for single-server production.

I created a docker-compose.yml:

docker-compose.yml
services:
openviking:
build: .
ports:
- "1933:1933"
volumes:
- ./openviking-data:/data
environment:
- OPENVIKING_CONFIG_FILE=/data/ov.conf
- OPENAI_API_KEY=${OPENAI_API_KEY}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:1933/health"]
interval: 30s
timeout: 10s
retries: 3

I started the service:

Terminal
docker-compose up -d

I could now check the health:

Terminal
docker-compose ps
# NAME COMMAND STATUS
# openviking "openviking-server..." Up 2 minutes (healthy)

Option 3: Kubernetes with Helm

For scalable production deployments, I used Kubernetes with Helm.

The Helm Chart Structure

OpenViking provides a Helm chart in examples/k8s-helm/:

helm-structure.txt
examples/k8s-helm/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── secret.yaml
│ └── configmap.yaml

Installing the Chart

I set up the values:

values.yaml
replicaCount: 2
image:
repository: openviking
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 1933
config:
vlm:
apiKey: "" # Set via --set
embedding:
apiKey: "" # Set via --set
auth:
enabled: true
apiKeys:
- "production-api-key-xxxxx"
persistence:
enabled: true
size: 10Gi
storageClass: standard

I installed the chart:

Terminal
helm install openviking ./examples/k8s-helm \
--set config.vlm.apiKey=$OPENAI_API_KEY \
--set config.embedding.apiKey=$OPENAI_API_KEY \
--namespace openviking --create-namespace

I verified the deployment:

Terminal
kubectl get pods -n openviking
# NAME READY STATUS
# openviking-6d4f8b9c4-x2k7m 1/1 Running
# openviking-6d4f8b9c4-8p3n1 1/1 Running

Persistent Storage

For Kubernetes, I needed to configure persistent storage. OpenViking stores data in the workspace directory, so I needed a PersistentVolumeClaim:

pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: openviking-pvc
namespace: openviking
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: standard

The Helm chart handles this automatically when persistence.enabled: true.

Configuration Details

Let me explain the key configuration options I learned.

Environment Variables

OpenViking accepts configuration via environment variables:

Terminal
export OPENVIKING_CONFIG_FILE=/path/to/ov.conf
export OPENAI_API_KEY=sk-xxx
export OPENVIKING_LOG_LEVEL=DEBUG

Storage Configuration

The storage section controls where data is persisted:

storage-config.json
{
"storage": {
"workspace": "/data/openviking",
"vector_db": {
"type": "pgvector",
"connection_string": "postgresql://user:pass@localhost:5432/openviking"
}
}
}

Embedding Configuration

For embeddings, I tested multiple providers:

embedding-config.json
{
"embedding": {
"dense": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "text-embedding-3-large",
"dimension": 3072
},
"sparse": {
"provider": "bm25",
"enabled": true
}
}
}

VLM Configuration

The Vision Language Model (VLM) is used for document understanding:

vlm-config.json
{
"vlm": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "gpt-4o"
}
}

Health Checks and Monitoring

OpenViking provides a health endpoint:

Terminal
curl http://localhost:1933/health
# {"status": "ok"}

For more detailed status:

Terminal
# Using the CLI
ov status
# Output:
# Server: http://localhost:1933
# Status: healthy
# Version: 0.5.0
# Resources: 42 indexed
# Sessions: 3 active

Prometheus Metrics

OpenViking exposes Prometheus metrics at /metrics:

Terminal
curl http://localhost:1933/metrics
# HELP openviking_requests_total Total number of requests
# TYPE openviking_requests_total counter
# openviking_requests_total{method="GET",endpoint="/v1/resources"} 1523
# openviking_requests_total{method="POST",endpoint="/v1/search"} 892

Security Best Practices

I learned these security practices the hard way.

1. Never Hardcode API Keys

Wrong:

wrong-config.json
{
"embedding": {
"dense": {
"api_key": "sk-hardcoded-key-bad-idea"
}
}
}

Right:

right-config.json
{
"embedding": {
"dense": {
"api_key": "${OPENAI_API_KEY}"
}
}
}

Then set the environment variable:

Terminal
export OPENAI_API_KEY=sk-your-actual-key

2. Use a Reverse Proxy

I put OpenViking behind nginx:

nginx.conf
server {
listen 443 ssl;
server_name openviking.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://localhost:1933;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

3. Rotate API Keys

I rotate my API keys periodically:

auth-config.json
{
"auth": {
"enabled": true,
"api_keys": [
"new-key-abc123",
"old-key-xyz789" # Keep old key during transition
]
}
}

Troubleshooting

Here are issues I encountered and how I fixed them.

Server Won’t Start

error-log.txt
Error: Config file not found at /data/ov.conf

Fix: Ensure the config file exists and is mounted correctly:

Terminal
docker exec openviking ls -la /data/
# Should show ov.conf

Connection Refused

error-log.txt
curl: (7) Failed to connect to localhost port 1933: Connection refused

Fix: Check if the port is exposed:

Terminal
docker port openviking
# 1933/tcp -> 0.0.0.0:1933

Authentication Errors

error-log.txt
{"error": "Unauthorized"}

Fix: Verify the API key header:

Terminal
# Check header name - must be exactly X-API-Key
curl -H "X-API-Key: your-key" http://localhost:1933/v1/resources

Storage Permission Errors

error-log.txt
PermissionError: [Errno 13] Permission denied: '/data/openviking'

Fix: Ensure the container user has write permissions:

Terminal
chmod -R 777 ./openviking-data

Cloud Deployment Example

I deployed OpenViking on AWS EC2. Here’s what I did:

Step 1: Launch EC2 Instance

Terminal
# Using AWS CLI
aws ec2 run-instances \
--image-id ami-0c55b159cbfafe1f0 \
--instance-type t3.medium \
--key-name my-key \
--security-group-ids sg-xxxxx

Step 2: Install Docker

Terminal
# SSH into instance
ssh -i my-key.pem [email protected]
# Install Docker
sudo yum update -y
sudo yum install -y docker
sudo service docker start
sudo usermod -a -G docker ec2-user

Step 3: Run OpenViking

Terminal
# Create data directory
mkdir -p ~/openviking-data
# Create config
cat > ~/openviking-data/ov.conf << 'EOF'
{
"storage": {
"workspace": "/data/openviking"
},
"auth": {
"enabled": true,
"api_keys": ["prod-api-key-secure"]
},
"embedding": {
"dense": {
"api_base": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"provider": "openai",
"model": "text-embedding-3-large",
"dimension": 3072
}
}
}
EOF
# Run container
docker run -d \
--name openviking \
-p 1933:1933 \
-v ~/openviking-data:/data \
-e OPENVIKING_CONFIG_FILE=/data/ov.conf \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
--restart unless-stopped \
openviking:latest

Step 4: Set Up Load Balancer

I used AWS Application Load Balancer with HTTPS:

alb-config.txt
Target Group: openviking-tg
Protocol: HTTP
Port: 1933
Health Check: /health
Listener: HTTPS:443
Forward to: openviking-tg
SSL Certificate: my-cert

Summary

In this post, I showed how to deploy OpenViking server from local development to production. The key point is that OpenViking supports multiple deployment paths with proper authentication and persistent storage.

For single-server production, I recommend Docker Compose with mounted volumes and API key authentication. For scalable deployments, Kubernetes with Helm charts provides the best flexibility.

The main things I learned:

  • Always mount config files and data directories as volumes
  • Enable authentication before exposing the API
  • Use environment variables for secrets, never hardcode
  • Put OpenViking behind a reverse proxy with TLS
  • Monitor health endpoint and Prometheus metrics

With these practices, OpenViking runs reliably in production for my AI agent applications.

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