Skip to content

Self-Hosting

Run KVM Fleet on your own infrastructure.

Requirements

  • Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
  • 2 vCPUs, 4 GB RAM minimum (equivalent to Hetzner CX22)
  • PostgreSQL 15+
  • Redis 7+
  • A domain with DNS pointing to your server
  • Docker (optional, but recommended)

Docker Compose

The fastest way to self-host:

# docker-compose.yml
version: "3.8"

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: kvmfleet
      POSTGRES_USER: kvmfleet
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    restart: unless-stopped

  api:
    image: ghcr.io/kvmfleet/api:latest
    environment:
      DATABASE_URL: postgresql://kvmfleet:${POSTGRES_PASSWORD}@postgres/kvmfleet
      REDIS_URL: redis://redis:6379
      GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
      GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
      JWT_SECRET: ${JWT_SECRET}
      BASE_URL: https://${DOMAIN}
    depends_on:
      - postgres
      - redis
    restart: unless-stopped

  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    depends_on:
      - api
    restart: unless-stopped

volumes:
  pgdata:
  caddy_data:

Caddyfile

{$DOMAIN} {
    reverse_proxy api:8000
}

Caddy handles TLS automatically via Let's Encrypt.

Environment variables

Create a .env file:

DOMAIN=kvm.example.com
POSTGRES_PASSWORD=<generate-a-strong-password>
JWT_SECRET=<generate-a-strong-secret>
GOOGLE_CLIENT_ID=<from-google-cloud-console>
GOOGLE_CLIENT_SECRET=<from-google-cloud-console>

Google OAuth setup

  1. Go to Google Cloud Console
  2. Create an OAuth 2.0 Client ID (Web application)
  3. Add https://your-domain.com/v1/auth/google/callback as an authorized redirect URI
  4. Copy the Client ID and Client Secret to your .env

Database initialization

The init.sql script (included in the release) creates:

  • Tables and indexes
  • The kvmfleet_app role with RLS policies
  • The audit log with append-only permissions

Run manually if not using Docker:

psql -U kvmfleet -d kvmfleet -f init.sql

Start

docker compose up -d

Verify:

curl https://your-domain.com/v1/health

Agent configuration

When enrolling devices, point agents to your self-hosted instance:

curl -sSL https://your-domain.com/install | sh -s -- \
  --token <token> \
  --api https://your-domain.com

Backups

Back up Postgres regularly. The audit log hash chain makes it possible to verify backup integrity:

pg_dump -U kvmfleet kvmfleet | gzip > kvmfleet-$(date +%F).sql.gz

Upgrades

docker compose pull
docker compose up -d

Database migrations run automatically on API startup.

Limitations vs hosted

Feature Self-hosted Hosted (app.kvmfleet.io)
Automatic updates Manual pull Automatic
Transactional email Bring your own SMTP Included
Billing/plans N/A (unlimited) Stripe integration
Support Community Email support