# Selkies: an HTML5 remote desktop and GPU streaming platform for containers and HPC

> A Linux streaming server that puts a low-latency desktop, and whatever OpenGL or Vulkan work you point it at, into a browser tab over WebSockets, built for environments where you cannot install anything on the client.

**selkies-project/selkies** — Low-Latency Accelerated Web Remote Desktop Streaming Platform for Self-Hosting, Containers, Kubernetes, or Cloud/HPC

- Repository: https://github.com/selkies-project/selkies
- Website: https://docs.selkies.io/
- Stars: 2,289 · Forks: 204
- Language: Python
- License: MPL-2.0
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/selkies-project-selkies

## The problem it solves is the client you do not control

The positioning line is unusually concrete: Moonlight, Google Stadia or GeForce NOW in noVNC form factor for Linux X11 and Wayland, in any HTML5 interface you care to embed inside, with at least 60 frames per second on Full HD. Each phrase is a constraint worth unpacking. The noVNC comparison names the thing it is replacing, which is a browser VNC client that streams pixels and input. The stated frame rate and resolution are a performance target rather than a hope, and the README goes further, saying that any performance issue problematic for cloud gaming platforms is also considered a bug.

The intended audience list explains why the design is what it is. It names researchers working on agentic AI, graphical AI, robotics, autonomous driving and drug discovery, plus SLURM and HPC administrators, Jupyter users, Kubernetes and Docker operators, Coder infrastructure administrators, and Linux cloud gaming enthusiasts. That is a list where the client is frequently a locked down managed notebook environment, and where installing a native streaming client on the user's laptop is not on the table. The same paragraph notes the project was started by Google engineers, then open sourced and developed by academic researchers, LinuxServer.io and community contributors, which is a useful account of why the code reads like infrastructure software rather than a product.

On the browser side, the client is stated to run on Chromium, Firefox and Safari, with two-way clipboard for text and images, low-latency zero-copy video rendering, automatic GPU selection, resilient keyboard, mouse and gamepad input, and microphone and webcam forwarding into the session. Zero-copy rendering and automatic GPU selection are the two claims that matter most for the 3D use cases, since both are where naive streaming implementations lose their frame budget.

## Version 2.0 dropped GStreamer for two Rust extensions

The 2.0 release is a rewrite of the runtime, and the release notes are the clearest statement of what the project now is. Selkies is a single Python application serving one port with the HTML5 client bundled into it, and GStreamer is gone. Screen capture and video encoding are done by `pixelflux`, and audio capture and encoding by `pcmflux`, two Rust extensions that install with the wheel. Everything in that sentence is a dependency you no longer have to provision, and a pipeline you no longer have to debug.

The transport change is the other half. WebSockets is the default, with one TCP port, 8080 by default, carrying video, audio, input, clipboard and file transfers, decoded in the browser through WebCodecs, with a striped JPEG path for browsers that lack WebCodecs. The release notes are explicit that nothing about this needs STUN or TURN, which in practice removes the single most common cause of a WebRTC stream that will not connect. WebRTC remains available as an opt-in transport via a mode flag, on a vendored `aiortc` fork, and it can be confined to a port range, to one shared UDP and/or TCP port, or run ICE-lite for a restrictive firewall. A dual mode flag lets the page switch transports while the session runs, which is a sensible way to let a browser behind a difficult network fall back.

The input path also moved. 2.0 injects input through `pixelflux` rather than through the previous mechanism, and both an X11 and a headless Wayland backend are supported, with X11 the default. The release candidates show the scope of the change before the final cut: H.265, VP8, VP9 and AV1 streaming over both WebSockets and WebRTC, formatted clipboard content moved out of the panel, an optional best-effort webhook for auditing clipboard and file events, and publishing the session keyboard and pointer as devices so applications see real input devices rather than synthetic events. For a platform meant to host real 3D and scientific software, that last item is the difference between a demo and something usable.

## Four ways to run it, one of them is a container

The README is direct about the fastest route. The desktop image bundles a desktop, a browser and an audio stack, so on a machine without a GPU this single command is the whole setup:

```bash
docker run --name selkies -it -d --rm --shm-size=2g -p 8080:8080 \
    ghcr.io/selkies-project/selkies/desktop:latest-ubuntu26.04
```

Then you open the port, accept the container's self-signed certificate, and log in as `ubuntu` with a password that the README gives you, which you should override with an environment variable before anyone else can reach the session. The `--shm-size=2g` flag is not decoration, since shared memory sizing is what makes rendering work in a container. The tag names the distribution inside the image, `ubuntu26.04` or `debiantrixie`, following `latest` for the newest release or `main` for the newest commit.

The other three routes cover the cases a single container image cannot. `pip install selkies` on Python 3.9 or newer brings the server, its web client and the capture extensions, and a `selkies-session` command then runs the desktop the host already has, bringing up what it lacks for a session such as a display and a sound server. That is the path Jupyter, Coder and Open OnDemand integrations use. Native packages cover `.deb`, `.rpm`, `.apk` and Arch, which attach Selkies to a display and sound server you run yourself, and the AppImage installs nothing and starts both where they are not already running.

The last route is the base container, which the README calls the whole session without a desktop and the image to build your own FROM on top of. Two community images are named for KDE Plasma desktops built on it, one EGL and one GLX. The development compose file makes the layering explicit in its own header comments:

```bash
docker compose build dist                      # wheel image
docker compose build base desktop              # session, then the desktop
docker compose up desktop                      # http://localhost:8080
docker compose --profile gpu up desktop-gpu    # NVIDIA
```

Those comments also record two things that will bite you otherwise: `desktop` builds FROM the image `base` produces so they must be built in that order, because Compose has no build-time dependency to express it, and Compose V2 is required, with no `version:` key, no `extends` and no `develop`.

## MPL-2.0 code, GPL components underneath

Selkies itself is MPL-2.0, which the badge, the GitHub metadata and the header comment in `pyproject.toml` all agree on. That is the weak copyleft licence, so modifications to Selkies files must be published, while linking it into a larger work does not force that work open. The `pyproject.toml` header carries the MPL 2.0 notice directly, and the same notice appears at the top of the Dockerfile and the compose file.

What is worth knowing is that an installation is not purely MPL. The README points to a licensing page that inventories the third-party components of an installation with their licences and, in its words, where the GPL pieces come from. A streaming server that forwards a desktop will contain components under various copyleft terms, and if you are redistributing an image rather than running one internally, that inventory is the document you need. The open issue count on the repository is very low, in single digits, which for a project this size reads as either unusually well triaged or unusually quiet.

Two details about the packaging are odd enough to mention. The project version in `pyproject.toml` is `0.0.0.dev0`, a placeholder, with the compose file passing a `PACKAGE_VERSION` build argument to fill it in, so the real version comes from the build rather than the manifest. And the repository carries `AGENTS.md` and `CLAUDE.md` at the root alongside a `.devcontainer/` directory and a `.pre-commit-config.yaml`, which tells you the project is developed in a fairly formal, containerised, agent-assisted setup. The release history shows a fast cadence around the 2.0 milestone, with `2.0.0rc0` on 2026-09-12, `2.0.0rc1` on 2026-09-20, and `2.0.0` on 2026-09-23, and the repository was pushed on 2026-09-28. The rc0 notes make the intent explicit, asking people to run it on their own deployments and report what breaks before 2.0.0 is cut.

## Conclusion

Selkies is for a specific and increasingly common situation: the machine with the GPU or the licensed software is yours, the machine in front of the person is not, and the only thing you can assume about that other machine is that it has a browser. Under that constraint the WebSockets default is a genuinely better design than the WebRTC-first architecture the genre settled on, because it needs no STUN or TURN, survives restrictive firewalls, and can share a single TCP port with everything else. The 2.0 rewrite removed GStreamer from the runtime in favour of two Rust extensions for video and audio, which is the change that most affects whether you can hit 60fps on Full HD. The honest caveat is the README's own: the project says outright that it needs maintainers. The container path is the fastest way in; the Settings Reference on the documentation site is where the real configuration surface is.

## FAQ

### How do I run Selkies for the first time?

Run the desktop container with docker run, publishing port 8080 and giving it 2g of shared memory, then open the port, accept the self-signed certificate and log in as ubuntu with the default password, which you should change with the PASSWD environment variable.

### Does Selkies need WebRTC or a STUN or TURN server?

Not by default. Since 2.0 the default transport is WebSockets over a single TCP port, decoded in the browser with WebCodecs. WebRTC is opt in through a mode flag on a vendored aiortc fork, and can be restricted to a port range or run ICE-lite.

### What GPU vendors does Selkies support?

The documentation covers Intel, AMD and NVIDIA paths, including a GPU profile for the compose based desktop image. The client also handles automatic GPU selection, and the topics cover OpenGL, Vulkan and NVIDIA specifically.

### What licence is Selkies released under?

The Selkies code is MPL-2.0. An installation also includes third-party components under other licences including GPL pieces, and the documentation has a licensing page inventorying them.

## Sources

- [License: MPL-2.0](https://github.com/selkies-project/selkies/blob/main/LICENSE)
- [Project website](https://docs.selkies.io/)
- [README](https://github.com/selkies-project/selkies/blob/main/README.md)
- [Releases](https://github.com/selkies-project/selkies/releases)
- [selkies-project/selkies on GitHub](https://github.com/selkies-project/selkies)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/selkies-project-selkies
