PyVNCServer

# PyVNCServer ### A modern, high-performance VNC / RFB server written in Python RFB 3.8 · UltraVNC interoperability · Tight / ZRLE / Hextile / Zlib · WebSocket / noVNC · multi-client · low-latency capture

Python Version RFB UltraVNC WebSocket Tests Documentation

**PyVNCServer** is a Python implementation of a VNC/RFB server focused on protocol correctness, practical client interoperability, low-latency desktop streaming and a security-conscious default configuration. [**Documentation**](https://xulek.github.io/PyVNCServer/) · [**GitHub**](https://github.com/xulek/PyVNCServer)

Why PyVNCServer?

PyVNCServer is designed for projects that need more control than a black-box VNC server provides. The protocol stack, capture pipeline, encoding selection and connection lifecycle are all implemented in Python and can be extended or embedded directly.

Highlights:

For the complete guides, configuration reference, architecture and troubleshooting documentation, see https://xulek.github.io/PyVNCServer/.


Client compatibility

Client / transport Status Notes
UltraVNC Viewer ✅ Regression-covered Tight/RRE fixes retained; Raw/RRE/Hextile/Zlib/ZRLE/Tight switching is covered by end-to-end tests
Standard RFB 3.8 clients ✅ Supported Client must advertise at least one encoding implemented by the server
noVNC / browser clients ✅ Supported transport noVNC is tracked as web/noVNC; enable WebSocket and configure an Origin allowlist
Raw TCP VNC ✅ Supported Default transport
VeNCrypt 0.2 ✅ Protocol + real TLS tested Security type 19; X509None/X509Vnc recommended
Legacy TLS-wrapped VNC ✅ Optional Pre-RFB TLS wrapper retained for compatibility

[!NOTE] Tight encoding and Tight security/capability negotiation are different concepts. Tight capability negotiation does not provide transport encryption by itself.


Quick start

Requirements

Install from source

Clone recursively to initialize the bundled noVNC submodule:

git clone --recurse-submodules https://github.com/xulek/PyVNCServer.git
cd PyVNCServer
python -m pip install -U pip
python -m pip install -e .

If you already cloned the repository without submodules:

git submodule update --init --recursive

For the faster optional capture stack:

python -m pip install -e ".[performance]"

Optional H.264 support:

python -m pip install -e ".[h264]"

Install both:

python -m pip install -e ".[performance,h264]"

Start the server

pyvncserver serve

The packaged defaults listen on:

127.0.0.1:5900

You can also run the package directly:

python -m pyvncserver serve

Use a custom TOML configuration:

pyvncserver serve --config config/pyvncserver.toml

Override the configured log level:

pyvncserver serve --log-level DEBUG

Connecting with UltraVNC

For a local test:

  1. Start PyVNCServer.
  2. Open UltraVNC Viewer.
  3. Connect to 127.0.0.1:5900.
  4. Start with Auto, Tight, ZRLE or Hextile as the preferred encoding.

PyVNCServer includes specific UltraVNC interoperability fixes:

Relevant compatibility switches:

[features]
tight_stream_reset_for_ultravnc = false

[limits]
tight_disable_for_ultravnc = false

Use these as troubleshooting switches rather than enabling them automatically for every client.

More details: UltraVNC documentation.


noVNC / WebSocket mode

WebSocket support is disabled by default. Enable it explicitly:

[features]
enable_websocket = true

[websocket]
allowed_origins = [
    "http://127.0.0.1:6080",
    "http://localhost:6080",
]

The WebSocket implementation accepts the binary transport used by noVNC and enforces:

noVNC is tracked as the web/noVNC Git submodule. PyVNCServer provides the VNC WebSocket transport; serve the noVNC frontend with your preferred HTTP server or reverse proxy.

More details: noVNC & WebSocket documentation.


Modern RFB extensions (v3.4)

PyVNCServer 3.4 adds protocol support aimed especially at modern viewers and noVNC:

Extension ID / message Status
ContinuousUpdates -313 / client 150 ✅ Push-style framebuffer updates
Fence -312 / 248 ✅ Negotiation probe + request/response
LastRect -224 ✅ Implemented, opt-in by default
ExtendedDesktopSize -308 ✅ Layout reporting and resize status
SetDesktopSize client 251 ✅ Parsed; host resize rejected safely by default

ContinuousUpdates runs inside the existing per-client session thread, so Tight/Zlib encoder state remains ordered. ExtendedDesktopSize now uses the correct 16-byte SCREEN record and immediately advertises the current screen layout when negotiated.


Encoding support

Encoding ID Implementation Typical use
Raw 0 Simple baseline, LAN/debugging
CopyRect 1 Efficient screen movement/scroll-like updates
RRE 2 Flat-color / simple regions
Hextile 5 General compatibility with tiled updates
Zlib 6 General-purpose compressed rectangles
Tight 7 Strong VNC client compatibility and compression
ZRLE 16 Efficient tiled zlib/RLE encoding
JPEG path extension ✅ Optional path Image-like content where lossy compression is appropriate
H.264 path extension ⚙️ Optional Requires av; extension/client support is required

The server negotiates only encodings advertised by the client and can select different encodings for different update regions.

See the encoding reference for protocol and fallback details.


Capture pipeline

capture_backend = "auto" selects the best available backend.

Backend Platform Notes
DXCam / DXGI Windows Preferred optional high-performance path
MSS Cross-platform Main portable capture backend
Pillow ImageGrab Platform dependent Fallback capture path

Install performance extras on Windows to make DXCam available:

python -m pip install -e ".[performance]"

The server-wide CaptureProducer captures once and publishes generations of the framebuffer to client sessions. This avoids scaling capture work linearly with the number of connected clients.

[!IMPORTANT] On Windows with DXCam 0.3.0+, PyVNCServer can harvest native DXGI dirty/move rectangles. If metadata cannot be read safely, it falls back to the software change detector instead of assuming that the screen did not change.


Multi-monitor mode (v3.4)

Combined desktop capture is available through MSS:

[server]
capture_backend = "auto"
capture_all_monitors = true
monitor_index = 0

When enabled with capture_backend = "auto", PyVNCServer prefers MSS monitor 0, which represents the virtual desktop spanning all displays. Physical monitor coordinates are normalized to the RFB framebuffer origin and reported through ExtendedDesktopSize.

DXCam still captures one DXGI output per camera; use MSS/auto for a combined multi-monitor framebuffer.

See docs/MULTI_MONITOR.md.


Security model

PyVNCServer deliberately uses conservative defaults.

Default behavior

[server]
host = "127.0.0.1"
port = 5900
handshake_timeout = 5.0
max_connections = 10
max_connections_per_ip = 4
max_unauthenticated_connections = 4

[security]
password = ""
read_only_password = ""
allow_insecure_no_auth = false

The server refuses an unauthenticated non-loopback bind unless you explicitly opt into it.

Classic VNC authentication caveat

Classic VNC authentication:

For untrusted networks, prefer VeNCrypt X509Vnc, SSH tunnelling or a VPN.

VeNCrypt is negotiated as normal RFB security type 19; TLS begins only after the client selects a VeNCrypt subtype.

Recommended password-authenticated configuration:

[security]
password = "secret"

vencrypt_enabled = true
vencrypt_subtypes = ["x509-vnc"]
tls_cert_file = "server.crt"
tls_key_file = "server.key"
tls_minimum_version = "1.2"
require_encrypted_transport = true

Supported v3.5 subtypes:

VeNCrypt subtype ID User authentication Server identity
X509None 260 none X.509 certificate
X509Vnc 261 classic VNC auth inside TLS X.509 certificate
TLSNone 257 none anonymous TLS
TLSVnc 258 classic VNC auth inside TLS anonymous TLS

TLSNone / TLSVnc are legacy anonymous-TLS compatibility modes and are disabled unless vencrypt_allow_anonymous_tls = true.

If a VNC password exists, PyVNCServer will not offer a *None subtype unless vencrypt_allow_no_auth_with_password = true is explicitly set. This prevents accidentally turning encryption into an authentication bypass.

Legacy direct TLS

The old pre-RFB TLS wrapper remains available:

[security]
tls_enabled = true
tls_cert_file = "server.crt"
tls_key_file = "server.key"
tls_minimum_version = "1.2"

tls_enabled and vencrypt_enabled are intentionally mutually exclusive.

Authentication throttling

[security]
auth_max_failures = 5
auth_failure_window_seconds = 30.0
auth_backoff_max_seconds = 2.0

Additional protections include connection admission limits, handshake timeouts and WebSocket payload limits.

See the full security guide.


Low-latency profile (v4.1)

The default v4.1 performance policy favors fresh frames over queued frames. On LAN/localhost this means smaller TCP send queues, a faster shared capture producer, stale ContinuousUpdates frame dropping and fewer unnecessary copies.

[performance]
profile = "low-latency"
capture_producer_fps = 120
socket_send_buffer_bytes = 262144
socket_receive_buffer_bytes = 131072
framebuffer_send_coalesce_bytes = 131072
producer_conversion_cache_entries = 4
producer_conversion_cache_max_bytes = 67108864
drop_stale_continuous_frames = true

Measure CPU-side hot paths locally:

PYTHONPATH=src python benchmarks/benchmark_latency_pipeline.py --iterations 100

For meaningful end-to-end numbers, run benchmark_lan_latency.py against the actual Windows/DXGI host and viewer/network path.

Configuration

The safe packaged configuration is stored in:

src/pyvncserver/default_config.toml

A project-level example is available at:

config/pyvncserver.toml

Important options:

[server]
host = "127.0.0.1"
port = 5900
frame_rate = 30
lan_frame_rate = 90
network_profile_override = "auto"
scale_factor = 1.0
capture_backend = "auto"
monitor_index = 0
capture_all_monitors = false
max_connections = 10
input_control_policy = "single-controller"

[features]
enable_region_detection = true
enable_metrics = true
enable_request_coalescing = true
enable_lan_adaptive_encoding = true
enable_websocket = false
enable_copyrect_encoding = true
enable_zrle_encoding = true
enable_tight_encoding = true
enable_jpeg_encoding = true
enable_h264_encoding = false
enable_parallel_encoding = true
enable_capture_producer = true
enable_dxgi_metadata = true
enable_continuous_updates = true
enable_fence = true
enable_last_rect = false
enable_extended_desktop_size = true
allow_client_resize = false

[adaptive]
enabled = true
min_fps = 12
target_utilization = 0.80
merge_regions = true
cache_enabled = true
reorder_encodings = true
adapt_tight_compression = true

[limits]
encoding_threads = 0
max_set_encodings = 1024
max_client_cut_text = 16777216

network_profile_override = "auto" allows the server to classify the connection instead of forcing LAN tuning for every client.

The complete option reference is available in the configuration documentation.


Architecture

flowchart LR
    C1[UltraVNC / RFB client]
    C2[noVNC browser]

    TCP[TCP transport]
    WS[WebSocket transport]
    AUTH[RFB handshake + security]
    SESSION[Client session]
    PIPE[Framebuffer pipeline]
    ENC[Encoding manager]
    CAP[Shared CaptureProducer]
    BACKENDS[DXCam / MSS / Pillow]
    INPUT[Keyboard / pointer / clipboard]

    C1 --> TCP --> AUTH
    C2 --> WS --> AUTH
    AUTH --> SESSION
    SESSION --> PIPE --> ENC
    CAP --> PIPE
    BACKENDS --> CAP
    SESSION --> INPUT

Main modules

src/pyvncserver/
├── app/
│   └── server.py              listener, admission, auth, lifecycle
├── session/
│   ├── loop.py                per-client RFB message loop
│   └── runtime.py             framebuffer / encoding session helpers
├── platform/
│   └── producer.py            shared framebuffer producer
├── runtime/
│   ├── adaptive.py            per-client congestion control + encoded-region cache
│   ├── security.py            per-IP limits and auth throttling
│   ├── connection_limiter.py
│   └── connection_registry.py
├── rfb/                       structured RFB subpackage
├── observability/             metrics / logging / profiling facades
├── plugins/                   capture / encoding / security plugin contracts
├── capture.py                 stable capture facade
├── encodings.py               stable encoding facade
├── protocol.py                stable protocol facade
├── security.py                stable security facade
├── errors.py                  stable exception facade
├── config.py                  validated TOML configuration
├── cli.py                     command-line entry point
└── _core/                     private implementation detail

4.0 package and plugin API

Version 4.0 removes the old top-level vnc_lib package. Stable embedding imports are now grouped under:

from pyvncserver.capture import ScreenCapture
from pyvncserver.encodings import EncoderManager
from pyvncserver.protocol import RFBProtocol
from pyvncserver.security import VNCAuth, VeNCryptServer
from pyvncserver.errors import ProtocolError
from pyvncserver import PluginManager, VNCServerV3

Validate the architectural boundary with:

python scripts/check_architecture.py

Operations CLI

PyVNCServer 3.7 adds deployment-oriented commands that do not require starting the RFB listener:

pyvncserver info
pyvncserver doctor --config config/pyvncserver.toml
pyvncserver config init pyvncserver.toml
pyvncserver config validate pyvncserver.toml
pyvncserver benchmark --config pyvncserver.toml
pyvncserver release check --root .

Observability

[observability]
prometheus_enabled = true
prometheus_host = "127.0.0.1"
prometheus_port = 9100

This exposes /metrics, /healthz, /readyz and /status. The observability listener is disabled by default.

Clipboard policy

[clipboard]
enabled = true
direction = "both"
max_bytes = 1048576
encoding = "latin-1"

utf-8 can be enabled explicitly when both endpoints agree on it; Latin-1 remains the compatibility default.


Performance design

The server contains several latency and throughput optimizations:


Tests

Install development dependencies and run the complete suite:

python -m pip install -e ".[dev]"
python -m pytest -q

Current 4.0.0 verification result:

417 passed, 13 skipped

The skipped cases in the recorded verification environment are legacy/auth compatibility cases and do not affect Tight/RRE tests.

The suite includes coverage for:

Syntax/bytecode check

python -m compileall -q src tests

Verification

Run the full suite:

python -m pip install -e ".[dev]"
python -m pytest -q
python -m compileall -q src tests

The v4.0 package retains the adaptive/TLS/RFB/operations coverage from 3.7 and adds architecture-boundary checks, public facade imports and real capture/encoding/security plugin integration tests.


GitHub Actions

The repository currently contains these workflow definitions:

Workflow Purpose
CI Tests Python 3.11–3.13, includes a Windows test job, checks bytecode compilation, coverage and package builds
Documentation Strictly builds MkDocs documentation on PRs and deploys GitHub Pages from main

Development workflow

python -m venv .venv

Activate it:

Windows PowerShell

.\.venv\Scripts\Activate.ps1

Linux / macOS

source .venv/bin/activate

Install the project:

python -m pip install -U pip
python -m pip install -e ".[dev,performance]"

Run verification before committing:

python -m compileall -q src tests
python -m pytest -q

Build distributions:

python -m pip install build twine
python -m build
python -m twine check dist/*

Known limitations / roadmap


**PyVNCServer 4.0.0** · Python 3.11+ · RFB 3.8 · [Documentation](https://xulek.github.io/PyVNCServer/)