Self-Hosting

Deploy oversight on any Linux VPS or bare-metal server. This guide covers building from source, running as a systemd service, and placing nginx in front as a reverse proxy.

Prerequisites

Build from Source

Install and build bash
# Clone the repository
git clone https://github.com/CopilotKit/oversight.git /opt/oversight
cd /opt/oversight

# Install dependencies
corepack enable
pnpm install --frozen-lockfile

# Build all packages
BUILD_HASH=$(git rev-parse --short HEAD) pnpm run build

Environment File

Create an environment file with your configuration. Store it outside the repo to keep secrets separate from source code.

/etc/oversight/env ini
NODE_ENV=production
PORT=3001

# Supabase
SUPABASE_URL=https://<ref>.supabase.co
SUPABASE_SERVICE_ROLE_KEY=<service-role-key>
SUPABASE_ANON_KEY=<anon-key>

# Build-time vars must be set before building
VITE_SUPABASE_URL=https://<ref>.supabase.co
VITE_SUPABASE_ANON_KEY=<anon-key>

systemd Service

Create a systemd unit file to manage the oversight server as a background service with automatic restarts.

/etc/systemd/system/oversight.service ini
[Unit]
Description=Oversight Server
After=network.target

[Service]
Type=simple
User=oversight
Group=oversight
WorkingDirectory=/opt/oversight
EnvironmentFile=/etc/oversight/env
ExecStart=/usr/bin/node packages/server/dist/index.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/oversight

[Install]
WantedBy=multi-user.target
Enable and start bash
# Create a dedicated user
sudo useradd --system --no-create-home oversight

# Set permissions
sudo mkdir -p /etc/oversight
sudo chmod 600 /etc/oversight/env
sudo chown -R oversight:oversight /opt/oversight

# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable oversight
sudo systemctl start oversight

# Check status
sudo systemctl status oversight
sudo journalctl -u oversight -f

nginx Reverse Proxy

Place nginx in front of the oversight server to handle TLS termination, static asset caching, and HTTP/2.

/etc/nginx/sites-available/oversight nginx
upstream oversight_backend {
    server 127.0.0.1:3001;
    keepalive 16;
}

server {
    listen 80;
    server_name oversight.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name oversight.example.com;

    ssl_certificate     /etc/letsencrypt/live/oversight.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/oversight.example.com/privkey.pem;

    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Proxy API requests
    location /api/ {
        proxy_pass http://oversight_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
    }

    # Static assets with caching
    location /assets/ {
        proxy_pass http://oversight_backend;
        proxy_cache_valid 200 7d;
        add_header Cache-Control "public, max-age=604800, immutable";
    }

    # SPA fallback
    location / {
        proxy_pass http://oversight_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
    }
}
Enable the site bash
# Enable the site
sudo ln -s /etc/nginx/sites-available/oversight /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

# Set up TLS with Let's Encrypt (if not already done)
sudo certbot --nginx -d oversight.example.com

Health Monitoring

The server exposes GET /api/health which returns {"status":"ok"}. Use this for uptime monitoring and load balancer health checks.

Health check script bash
#!/bin/bash
# /usr/local/bin/check-oversight-health.sh

RESPONSE=$(curl -sf http://localhost:3001/api/health)

if [ $? -ne 0 ] || [ "$(echo "$RESPONSE" | jq -r .status)" != "ok" ]; then
    echo "Oversight health check failed" | systemd-cat -t oversight-health
    systemctl restart oversight
fi

Add it to cron for periodic checks:

crontab bash
# Check health every 2 minutes
*/2 * * * * /usr/local/bin/check-oversight-health.sh

Updating

To update oversight to the latest version with zero-downtime:

Update script bash
cd /opt/oversight

# Pull latest changes
git pull origin main

# Rebuild
pnpm install --frozen-lockfile
BUILD_HASH=$(git rev-parse --short HEAD) pnpm run build

# Restart the service
sudo systemctl restart oversight

# Verify health
sleep 3
curl -sf http://localhost:3001/api/health && echo "Update successful"

Runner

If you want to run the task runner as a systemd service alongside the server:

/etc/systemd/system/oversight-runner.service ini
[Unit]
Description=Oversight Runner
After=oversight.service

[Service]
Type=simple
User=oversight
Group=oversight
WorkingDirectory=/opt/oversight
EnvironmentFile=/etc/oversight/env
ExecStart=/usr/bin/node packages/server/dist/runner.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
Note: The runner requires the claude CLI to be installed and accessible to the oversight user. The runner also needs access to your Claude configuration and plugins, which typically live in your home directory. For most setups, running the runner under your own user account is simpler than running it as a system service.