Vexlo Logo

Secure SSH Web Console with Keycloak RBAC

Vexlo gives your team safe, role-based SSH access from the browser. Seamless SSO with Keycloak. Simple to deploy. Built for clarity and security.

Why Choose Vexlo?

Enterprise-grade security meets developer-friendly design

Keycloak SSO

Single sign-on with your existing identity provider. No additional user management required.

Role-Based Access

Grant VM access based on user roles. Perfect for team segregation and compliance.

Web Terminal

Full SSH functionality in your browser with WebSocket connections and session persistence.

Instant Setup

Deploy with Docker in under 5 minutes. Production-ready with minimal configuration.

Modern UI

Clean, responsive interface with dark mode support and intuitive navigation.

Open Source

MIT licensed, community-driven, and fully customizable for your needs.

Keycloak Realm Setup Guide

Step-by-step guide to configure your Keycloak realm for Vexlo

Prerequisites: You need a running Keycloak instance and admin access to configure realms and clients.
1 / 11

System Requirements

Everything you need to get started

Component Minimum Recommended Notes
Docker 20.10+ 24.0+ Container runtime
Docker Compose 2.0+ 2.20+ Multi-container management
Memory (RAM) 1GB 4GB For 50+ concurrent sessions
CPU 1 Core 2+ Cores x86_64 or ARM64
Storage 500MB 2GB For logs and session data
Network 80/443 80/443 + 8080 HTTPS recommended

See Vexlo in Action

Explore the intuitive interface designed for seamless VM management

Quick Setup

Deploy Vexlo with Docker in minutes

Prerequisites: Docker (Docker Compose optional) and a configured Keycloak realm. Follow our Keycloak setup guide above.
1

Configure & Choose a Run Method

Option A — Docker Run (one-liner)

Replace the placeholder values before running. The ./config folder is mounted for VMs/config.

docker run -d --name vexlo -p 8080:8080 \ -e FLASK_SECRET=your-super-secure-secret \ -e KEYCLOAK_BASE_URL=https://keycloak.yourcompany.com \ -e KEYCLOAK_REALM=your-realm \ -e KEYCLOAK_CLIENT_ID=vm-console \ -e KEYCLOAK_CLIENT_SECRET=your-client-secret \ -v ./config:/app/config \ -v vexlo_sessions:/app/sessions \ kayamii/vexlo:latest

Option B — Docker Compose: If you prefer Compose, create your .env file:

FLASK_SECRET=your-super-secure-secret-key-here KEYCLOAK_BASE_URL=https://keycloak.yourcompany.com KEYCLOAK_REALM=your-realm KEYCLOAK_CLIENT_ID=vm-console KEYCLOAK_CLIENT_SECRET=your-client-secret
docker-compose.yml Preview
version: '3.8' services: vexlo: image: kayamii/vexlo:latest ports: - "8080:8080" environment: - FLASK_SECRET=${FLASK_SECRET} - KEYCLOAK_BASE_URL=${KEYCLOAK_BASE_URL} - KEYCLOAK_REALM=${KEYCLOAK_REALM} - KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID} - KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET} volumes: - ./config:/app/config - vexlo_sessions:/app/sessions restart: unless-stopped volumes: vexlo_sessions:
2

Launch

Start Vexlo:

# If you used Option A (docker run), it's already running. # If you prefer Compose (Option B): docker compose up -d
Access your console at http://localhost:8080

Troubleshooting & FAQ

Common issues and solutions

Solution: This usually means the client secret is incorrect or the client configuration is wrong.

  • Verify client ID matches exactly: vm-console
  • Check client secret in Keycloak admin console
  • Ensure client type is set to "confidential"
  • Verify redirect URIs include your domain
# Debug with curl: curl -X POST "https://keycloak.domain.com/auth/realms/YOUR_REALM/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials&client_id=vm-console&client_secret=YOUR_SECRET"

Solution: Role mapping is not configured correctly.

  • Check if user has the required roles assigned
  • Verify role mapper includes roles in token claims
  • Ensure realm_access.roles claim is present
  • Check VM configuration file maps roles correctly
# Example VM config (config/vms.yml): servers: - name: "Production Web Server" host: "10.0.1.100" username: "ubuntu" roles: ["vm-admin", "vm-developer"] - name: "Dev Database" host: "10.0.1.200" username: "dev" roles: ["vm-developer"]

Solution: Network or SSH configuration issues.

  • Check if target server is reachable from Docker container
  • Verify SSH keys are properly mounted
  • Ensure target server allows SSH connections
  • Check firewall rules on both ends
# Test SSH from container: docker exec -it vexlo_vexlo_1 ssh -o ConnectTimeout=5 user@target-server # Check container network: docker exec -it vexlo_vexlo_1 ping target-server # View container logs: docker logs vexlo_vexlo_1

Solution: Usually a proxy or HTTPS configuration issue.

  • Check if using HTTPS, WebSocket must use WSS protocol
  • Configure reverse proxy to forward WebSocket headers
  • Verify no corporate firewall blocks WebSocket
  • Test directly without proxy first
# Nginx proxy config for WebSocket: location / { proxy_pass http://vexlo:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; }

Solution: Mount SSH keys as Docker volumes.

# Add to docker-compose.yml volumes: volumes: - ./ssh-keys:/app/ssh-keys:ro - ./config:/app/config # SSH key structure: ssh-keys/ ├── id_rsa_server1 # Private key for server1 ├── id_rsa_server1.pub # Public key for server1 └── id_ed25519_prod # Private key for production

Solution: Use reverse proxy with SSL termination.

# Example with Traefik labels in docker-compose.yml: labels: - "traefik.enable=true" - "traefik.http.routers.vexlo.rule=Host(`ssh.yourdomain.com`)" - "traefik.http.routers.vexlo.tls.certresolver=letsencrypt" - "traefik.http.services.vexlo.loadbalancer.server.port=8080" # Or use nginx with Let's Encrypt: # See: https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion

Recommendations for production:

  • Use Redis for session storage in multi-instance setup
  • Enable connection pooling for high concurrent usage
  • Configure proper resource limits
  • Use persistent volumes for logs and session data
# Production docker-compose.yml additions: services: vexlo: deploy: resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M environment: - REDIS_URL=redis://redis:6379 - MAX_CONNECTIONS=50 redis: image: redis:alpine volumes: - redis_data:/data volumes: redis_data:
Need More Help? Check our GitHub Issues or Discussions for community support.

Get in Touch

Have questions or need support? Let's connect!