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
- Node.js ≥ 20
- pnpm ≥ 9
- A Supabase project (or self-hosted Supabase instance)
- A server with at least 512 MB RAM
Build from Source
# 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.
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.
[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
# 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.
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
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.
#!/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:
# Check health every 2 minutes
*/2 * * * * /usr/local/bin/check-oversight-health.sh
Updating
To update oversight to the latest version with zero-downtime:
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:
[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
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.