Skip to Content
Self HostingDocker Quick Start

Docker Quick Start

Run the CTO-GUI VM management interface on any Linux machine with KVM. No account or API keys required — just Docker and a KVM-capable host.

Prerequisites

  • Linux host with KVM/libvirt installed and running
  • Docker and Docker Compose (v2)
  • /dev/kvm accessible (your user in the kvm group)
  • libvirt socket at /var/run/libvirt/libvirt-sock

Verify your system is ready:

# KVM support (should return > 0) egrep -c '(vmx|svm)' /proc/cpuinfo # libvirt running sudo systemctl status libvirtd # Docker installed docker compose version # /dev/kvm accessible ls -la /dev/kvm

Start

Clone the repository and start the containers:

git clone https://github.com/openfactory-tech/openfactory.git cd openfactory/cto-gui docker compose -f docker-compose.self-hosted.yml up --build -d

Open http://localhost  in your browser. No login required.

What You Get

FeatureAvailable
VM creation and lifecycle (start, stop, delete)Yes
Network management (create, attach, detach)Yes
Network topology visualizationYes
VNC console accessYes
ISO upload and managementYes
CIS benchmark testingYes
Automated test executionYes
Live machine monitoringYes
AI-powered OS buildingNo — use console.openfactory.tech 
Security patch monitoringNo
Deployment schedulingNo
Policy document managementNo

Architecture

The Docker deployment runs two containers behind a single port:

┌──────────────────────────────────────┐ │ Browser │ │ http://localhost │ └──────────────┬───────────────────────┘ ┌──────▼──────┐ │ Nginx │ port 80 │ (frontend) │ React SPA + reverse proxy └──────┬──────┘ │ /api/* → backend:8001 ┌──────▼──────┐ │ Backend │ │ (FastAPI) │ VM management, benchmarks, │ │ ISO management, testing └──────┬──────┘ ┌──────▼──────┐ │ libvirtd │ Host's KVM/QEMU │ (host) │ via mounted socket └─────────────┘

The frontend container serves the React app and proxies /api/* requests to the backend. The backend container connects to the host’s libvirt daemon through the mounted socket.

Managing ISOs

Upload via the UI

The sidebar shows an ISO Images panel. Click the upload icon or drag and drop .iso files directly. Uploads stream to disk — no memory issues with large files.

Pre-place on the host

Copy ISOs directly to /var/lib/libvirt/images/ on the host. They appear automatically in the UI since the container mounts this directory.

sudo cp my-custom-os.iso /var/lib/libvirt/images/

Download from openfactory.tech

Build custom ISOs on console.openfactory.tech , download them, and upload to your self-hosted instance. This is the recommended workflow for teams that want AI-powered OS building with local VM management.

Creating VMs

  1. Select an ISO in the sidebar
  2. Click Create Live VM in the toolbar
  3. Choose the ISO, network, memory, vCPUs, and disk size
  4. The VM appears in the network topology and sidebar

VMs are created using the host’s libvirt — they’re real KVM virtual machines managed through the standard libvirt toolchain. You can also manage them with virsh on the host.

Configuration

Environment variables are set in docker-compose.self-hosted.yml. The defaults work for most setups.

Backend

VariableDefaultDescription
SELF_HOSTEDtrueEnables self-hosted mode (required)
KVM_URIqemu:///systemlibvirt connection URI
ISO_UPLOAD_DIR/var/lib/libvirt/imagesWhere ISOs are stored
MAX_ISO_UPLOAD_SIZE_GB10Maximum upload size in GB
DEBUGfalseSet to true to enable /docs (Swagger UI)
ALLOWED_ORIGINS["*"]CORS origins — restrict in production

Frontend

VariableDefaultDescription
REACT_APP_SELF_HOSTEDtrueBuild-time flag for self-hosted UI (set during docker build)

Volumes

Container PathHost PathPurpose
/var/run/libvirt/libvirt-sock/var/run/libvirt/libvirt-socklibvirt daemon socket
/var/lib/libvirt/images/var/lib/libvirt/imagesISO storage (shared with host)
/app/dataDocker volume cto-dataBenchmark results, test data, logs

Authentication

In self-hosted mode, authentication is disabled. All requests are handled as a local admin user — no login page, no tokens, no external auth providers.

This is appropriate for single-user setups and trusted LANs. For multi-user access control, see the full platform installation which includes NextAuth-based authentication.

Networking

The backend binds to port 8001 inside its container. The frontend’s Nginx proxies /api/* to backend:8001 using Docker’s internal DNS. Only port 80 is exposed to the host.

For HTTPS, place a reverse proxy (Nginx, Caddy, Traefik) in front of the frontend container:

server { listen 443 ssl http2; server_name vms.example.com; ssl_certificate /etc/letsencrypt/live/vms.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/vms.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:80; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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; client_max_body_size 10G; } }

Updating

Pull the latest changes and rebuild:

cd openfactory/cto-gui git pull origin main docker compose -f docker-compose.self-hosted.yml up --build -d

Data in the cto-data volume persists across rebuilds.

Troubleshooting

”FATAL: /dev/kvm does not exist”

KVM kernel module is not loaded:

sudo modprobe kvm sudo modprobe kvm_intel # or kvm_amd

“FATAL: /dev/kvm exists but is not accessible”

Add your user to the kvm group:

sudo usermod -aG kvm $USER # Log out and back in

“Could not connect to libvirt”

Ensure libvirtd is running and the socket exists:

sudo systemctl start libvirtd ls -la /var/run/libvirt/libvirt-sock

ISO upload fails or times out

  • Check disk space on /var/lib/libvirt/images
  • For files larger than 10 GB, set MAX_ISO_UPLOAD_SIZE_GB in the compose file
  • Ensure the container can write to the images directory

VMs created but not visible in UI

The backend queries libvirt directly. If VMs were created outside the UI (e.g., with virt-manager), they still appear. If they don’t, check that the KVM_URI matches your libvirt setup:

# Verify VMs are visible to libvirt sudo virsh list --all

Container can’t reach host libvirt

The compose file mounts the libvirt socket. If your socket is at a non-standard path, update the volume mount:

volumes: - /path/to/your/libvirt-sock:/var/run/libvirt/libvirt-sock