A feature-rich RFB/VNC server written in pure Python
PyVNCServer is an RFC 6143-compliant VNC server that captures your desktop and streams it to any VNC viewer. It supports multiple encodings, adaptive compression, WebSocket transport for browser access via noVNC, and network-aware performance tuning -- all from a single Python package.
|
Protocol & Security
|
Encodings
|
|
Performance
|
Platform & Transport
|
git clone https://github.com/xulek/PyVNCServer.git
cd PyVNCServer
pip install -e .[dev]Alternative: run without installing
# PowerShell
$env:PYTHONPATH = "src"
python -m pyvncserver --help# Bash
PYTHONPATH=src python -m pyvncserver --helpOptional: Windows DXGI capture backend
pip install -e .[windows-capture]Provides hardware-accelerated screen capture via Desktop Duplication API. Falls back to MSS automatically if unavailable.
Edit config/pyvncserver.toml -- set at least password for authentication:
[server]
host = "0.0.0.0"
port = 5900
password = "secret"pyvncserver serve --config config/pyvncserver.tomlvncviewer localhost:5900PyVNCServer serves WebSocket and standard VNC on the same port -- no external proxy required.
1. Enable WebSocket in config/pyvncserver.toml:
[features]
enable_websocket = true
[websocket]
allowed_origins = ["http://localhost:8000"]2. Start the VNC server, then serve the web client:
pyvncserver serve --config config/pyvncserver.toml
python -m http.server 8000 # separate terminal3. Open http://localhost:8000/web/vnc_client.html in your browser.
For production
wss://transport, terminate TLS at a reverse proxy (e.g. Nginx). SeeWEBSOCKET.mdfor details and an Nginx config example.
All settings live in config/pyvncserver.toml, organized into sections:
[server] -- Core server settings
| Key | Type | Default | Description |
|---|---|---|---|
host |
str |
"0.0.0.0" |
Bind address |
port |
int |
5900 |
VNC port |
password |
str |
"" |
VNC password (empty = no auth) |
read_only_password |
str |
"" |
Optional view-only password |
frame_rate |
int |
30 |
Target FPS (WAN profile) |
lan_frame_rate |
int |
90 |
Target FPS (LAN profile) |
network_profile_override |
str|null |
"lan" |
Force localhost, lan, wan, or auto-detect ("") |
scale_factor |
float |
1.0 |
Capture scaling factor |
capture_backend |
str |
"auto" |
auto, dxcam, mss, or pil |
capture_probe_frames |
int |
0 |
Startup capture latency probe samples |
capture_probe_warn_ms |
float |
40.0 |
Probe warning threshold (ms) |
max_connections |
int |
10 |
Maximum simultaneous clients |
client_socket_timeout |
float |
60.0 |
Per-client read timeout (seconds) |
input_control_policy |
str |
"single-controller" |
single-controller or shared |
[features] -- Feature toggles
| Key | Default | Description |
|---|---|---|
enable_region_detection |
true |
Incremental update optimization |
enable_metrics |
true |
Internal metrics collection |
enable_request_coalescing |
true |
Drop stale framebuffer requests |
enable_lan_adaptive_encoding |
true |
LAN-tuned encoder parameter adaptation |
enable_websocket |
false |
WebSocket transport support |
enable_tight_security |
true |
TightVNC security type 16 |
enable_cursor_encoding |
false |
RichCursor & PointerPos pseudo-encodings |
enable_copyrect_encoding |
true |
CopyRect encoding |
enable_zrle_encoding |
true |
ZRLE encoding |
enable_tight_encoding |
true |
Tight encoding |
enable_jpeg_encoding |
true |
JPEG encoding |
enable_h264_encoding |
false |
H.264 encoding (requires PyAV) |
enable_parallel_encoding |
true |
Multi-threaded region encoding |
[lan] -- LAN adaptive encoding thresholds
| Key | Default | Description |
|---|---|---|
raw_area_threshold |
0.10 |
Area ratio below which Raw is preferred |
raw_max_pixels |
65536 |
Max rectangle size eligible for Raw |
zlib_area_threshold |
0.08 |
Area ratio above which Zlib is preferred |
zlib_min_pixels |
8192 |
Min rectangle size for Zlib |
zlib_compression_level |
2 |
Zlib compression level |
zlib_disable_if_request_gap_ms |
1500 |
Disable Zlib if client request gap exceeds this |
jpeg_area_threshold |
0.20 |
Area ratio above which JPEG is preferred |
jpeg_min_pixels |
16384 |
Min rectangle size for JPEG |
jpeg_quality_initial |
84 |
Starting JPEG quality |
jpeg_quality_min / max |
70 / 95 |
Adaptive JPEG quality bounds |
zrle_compression_level |
3 |
ZRLE compression level |
[websocket] -- WebSocket transport settings
| Key | Default | Description |
|---|---|---|
allowed_origins |
[] |
Allowed browser Origin values |
detect_timeout |
0.5 |
WebSocket handshake detection timeout |
max_handshake_bytes |
65536 |
Max HTTP upgrade header size |
max_payload_bytes |
8388608 |
Max inbound frame payload (8 MB) |
max_buffer_bytes |
16777216 |
Max receive buffer (16 MB) |
[limits] and [logging]
| Key | Default | Description |
|---|---|---|
max_set_encodings |
1024 |
Max SetEncodings items from client |
max_client_cut_text |
16777216 |
Max ClientCutText payload (16 MB) |
encoding_threads |
0 |
Worker threads for parallel encoding (0 = auto) |
log_level |
"INFO" |
Python logging level |
log_file |
"" |
Optional log file path |
# Start with default config
pyvncserver serve
# Start with custom config and debug logging
pyvncserver serve --config config/pyvncserver.toml --log-level DEBUG
# Run as Python module (no install required, set PYTHONPATH=src)
python -m pyvncserver serve --config config/pyvncserver.tomlProgrammatic startup:
from pyvncserver import VNCServer
server = VNCServer(config_file="config/pyvncserver.toml")
server.start()PyVNCServer/
├── src/
│ ├── pyvncserver/ # Packaged application (new code goes here)
│ │ ├── cli.py # CLI entrypoint
│ │ ├── config.py # TOML config loader
│ │ ├── app/server.py # Main VNCServerV3 class
│ │ ├── rfb/ # Protocol layer (auth, encodings, messages)
│ │ ├── platform/ # OS integration (capture, cursor, input)
│ │ ├── runtime/ # Connection pool, network profiles, threading
│ │ ├── features/ # Clipboard, session recording, WebSocket
│ │ └── observability/ # Logging, metrics, Prometheus, profiling
│ └── vnc_lib/ # Internal implementation library
│ ├── protocol.py # RFB protocol negotiation
│ ├── encodings.py # Encoder implementations + EncoderManager
│ ├── screen_capture.py # Screen capture with backend selection
│ ├── auth.py # VNC/Tight authentication
│ └── ... # 20+ modules
├── config/pyvncserver.toml # Default server configuration
├── tests/ # 320+ pytest tests
├── benchmarks/ # Performance measurement scripts
├── examples/ # Demo scripts
├── web/ # noVNC browser client
└── docs/ # Architecture & protocol docs
# Full test suite
python -m pytest tests/ -v --tb=short
# Single test file
python -m pytest tests/test_encodings.py -v
# Single test
python -m pytest tests/test_vnc_server.py::TestClassName::test_method -v
# With coverage report
python -m pytest tests/ --cov=pyvncserver --cov=vnc_lib --cov-report=term-missingpython benchmarks/benchmark_encoders.py # Encoder throughput (Raw/Zlib/Tight/ZRLE)
python benchmarks/benchmark_screen_capture.py # Capture backend performance
python benchmarks/benchmark_screen_capture_methods.py 20 # Comparative backend benchmark
python benchmarks/benchmark_lan_latency.py 127.0.0.1 5900 20 # Network latencyTo measure real capture latency at startup, set in config:
[server]
capture_probe_frames = 12
capture_probe_warn_ms = 40.0Important: VNC authentication uses DES-based challenge-response and provides only basic protection. Traffic is not encrypted by default.
For production deployments:
- Set a strong password in
config/pyvncserver.toml - Run behind SSH tunneling, a VPN, or a TLS-terminating reverse proxy
- Restrict
allowed_originsfor WebSocket access - Bind to
127.0.0.1if only local access is needed - Do not expose directly to untrusted networks
No screen capture or input in Linux headless environments
pyautogui and capture backends require a graphical session. For X11:
export DISPLAY=:0For headless servers, run Xvfb and ensure the process has display access.
Import or runtime issues with mss / Pillow
Reinstall dependencies:
pip install -r requirements.txtBrowser connects but shows nothing
- Verify
enable_websocket = truein[features] - Verify
allowed_originscontains the origin serving the page (e.g.,http://localhost:8000) - Serve the web client over HTTP, not
file://(ES modules require it) - Check that the VNC port is not blocked by a firewall
| Package | Purpose |
|---|---|
| mss | Fast cross-platform screen capture |
| Pillow | Image processing and PIL capture fallback |
| pyautogui | Keyboard and mouse input simulation |
| pycryptodome | DES encryption for VNC authentication |
| numpy | Fast pixel data operations |
| dxcam | (optional) Windows DXGI Desktop Duplication capture |
| PyAV | (optional) H.264 encoding via FFmpeg |
This project is licensed under the MIT License.
Built with Python 3.13+ • RFC 6143 compliant • github.com/xulek/PyVNCServer