docker-steam-headless: what the image sets up, and what it leaves on you
A Headless Steam Docker image supporting NVIDIA GPU and accessible via Web UI
At a glance
- What is it?
- Steam-Headless/docker-steam-headless bakes the Steam client, a Xfce4 desktop and a Moonlight server into one Debian Trixie image. The access paths are clear, while root access, the storage rules and a missing image tag are the parts to read twice.
- Who is it for?
- docker-steam-headless fits a homelab builder who wants a Steam client reachable from a browser or a second Steam client and accepts root access in exchange. It is a poor fit for a shared host or any network you do not control.
- Can I use it commercially?
- Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 40 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- Steam-Headless/docker-steam-headless5,003 ★ · +188
| Repository | 2026-09-23 | 2026-11-13 |
|---|---|---|
| Steam-Headless/docker-steam-headless | 4.8k | 5k |
188 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Root access and a browser desktop, with hardening still on the TODO list
Two entries in the feature list decide how you should read the rest: `Root access`, and `Full video/audio noVNC web access to a Xfce4 Desktop`. Together they mean the WebUI is not a settings panel for a background daemon. It is a complete desktop session, driven with video and audio over a web connection, in an image that lists root access as a feature. Steam is configured to start inside that session on its own, and the same menu installs other launchers, so anything that reaches that endpoint reaches the whole machine rather than one process.
The project knows this. Its TODO list names two hardening items: Remove SSH, and Require user to enter password for sudo. Both are still listed, which tells you what state the image ships in. The access control on a root desktop is whatever your network provides, plus anything a reverse proxy in front of it adds.
Anything outside the home directory is wiped when the image updates
Persistence is the sharpest constraint in the project, and it is written as a rule rather than a default. Anything worth keeping belongs in the home directory or in a container mount you declared yourself. The README states that files stored outside your home directory are not persistent and will be wiped if the container is updated, or if you change something in the template. A template edit counts, so recreating the container from a modified template destroys exactly as much as pulling a new build.
That rule decides where your games live. The recommended layout is a library mounted at `/mnt/games`, with Steam configured to add that path. It also shapes software installs: a launcher installed into a system directory is wiped when the container is updated, while a script ending in `.sh` placed in `~/init.d` runs again on the next container startup. Plan the mounts before the first launch, because retrofitting them means migrating data out of a filesystem that is already gone.
Steam Remote Play leaves your network unless the container gets its own IP
One access path carries a routing requirement the others do not. To use the container as a Steam Remote Play host (previously called In Home Streaming), the README asks for a custom Docker network with an address assigned to the container itself. Without that, Steam concludes you are on a different network and sends the traffic through the internet.
Decide which clients you intend to serve before you publish anything, because the fix is a network change and a redeploy, not a setting inside the desktop.
Reusing the host X server takes four values, two of them optional
If the machine running the container already has an X server, the container can be told to use it rather than starting a second one. Four values are involved. DISPLAY and MODE are required; HOST_DBUS and the dbus mount are marked optional in the README.
DISPLAY=:0
MODE=secondary
HOST_DBUS=true
/run/dbus:/run/dbus:roDISPLAY has to name whatever the host is actually using, so `:0` is a starting point rather than a fact. MODE=secondary is the value that stops the container from launching its own X server. The optional pair exposes the host dbus process into the container, mounted read only, for applications that expect to talk to it. Leave the pair out on a host without dbus running, and set MODE only when you are certain the host display is the one you want on screen.
Three install guides, two Dockerfiles, and no image tag to pin
The README inlines no compose file and no run command. It links three guides, each of which stands alone, so the first real step is opening the one that matches your host:
docs/docker-compose.md
docs/unraid.md
docs/ubuntu-server.mdThe repository layout shows the shape of the build. Two Dockerfiles sit at the root, `Dockerfile.arch` and `Dockerfile.debian`, and the feature list names Debian Trixie as the base. Alongside them are `overlay/`, `images/` and a `.dockerignore` at the root; what those directories contain is not described. Default branch is master.
What is missing matters more. No image name, version or digest appears anywhere in the README, and the only published tags are 0.1.0 and 0.2.0, both released on 2023-09-29. You are tracking whatever the image reference in your guide resolves to on the day you pull it. Resolve the digest and record it before the first deploy if you want a host you can rebuild later.
Software and autostart entries are menu paths, not files you can diff
Configuration lives in the WebUI in two places. Steam is configured to autostart inside the container, and any additional service belongs under Applications > Settings > Session and Startup. Applications go in through Applications > System > Software, where the README names Lutris, Heroic and EmuDeck as examples, and both Flatpak and AppImage installs are supported.
The cost is reproducibility. A service added through a menu lives in the container's desktop session, and no file in the repository records it, so there is nothing to version and nothing to compare between two hosts. The `~/init.d` script route is the version you can keep in your own dotfiles and reapply on every rebuild, because a file ending in `.sh` there executes at container startup. If you rebuild often, or run more than one of these containers, use the script path and treat the menu as the place for one-off changes.
Recent pushes, no tag since 2023, and documented gaps in setup
The project has two clocks and they disagree. The last push was on 2026-10-05, so the image is still being changed. The release list has not moved since 0.2.0 on 2023-09-29, so there is no tagged artifact to point at when something regresses and no release notes describing what changed between builds. The repository is not archived, and it is licensed GPL-2.0, which leaves the Dockerfiles and overlay contents readable and forkable.
The open items are written down rather than implied. Beyond the security TODO, the list asks for instructions on other server operating systems and on TrueNAS Scale, so anyone on either is outside the written path. Two further gaps are worth knowing before you deploy: there is no rollback procedure, and there is no port table, which leaves you to work out what the WebUI, Moonlight and Remote Play each need opened. One mismatch also sits in the text. The README sends local development to a script in the devops directory, and devops is not among the repository's top-level entries.
Editorial conclusion
docker-steam-headless fits a homelab builder who wants a Steam client reachable from a browser or a second Steam client and accepts root access in exchange. It is a poor fit for a shared host or any network you do not control. Before deploying, open the docs file for your platform, mount your library to /mnt/games, and decide where the noVNC endpoint is reachable from, because the image ships root access and SSH is still on the project TODO list.
Frequently asked questions
What does steam headless do?
It packages the Steam client, configured to run on Linux with Proton, into a Docker image that streams the desktop with audio over noVNC in a browser, over Moonlight, and to another Steam client through Steam Remote Play.
Can I use Unraid with Steam headless?
Yes. The repository ships a dedicated guide for it at ./docs/unraid.md, next to separate guides for Docker Compose and Ubuntu Server.
Where should a games library be mounted in docker-steam-headless?
At /mnt/games. The README recommends mounting the library to that path and then configuring Steam to add it, and says anything worth saving belongs in the home directory or a mount you specified.
Do files outside the home directory survive a container update?
No. Files stored outside the home directory or a declared mount are not persistent and are wiped when the container is updated or when you change something in the template.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/steam-headless-docker-steam-headless)Star history chart for your README
[](https://hysenlabs.com/projects/steam-headless-docker-steam-headless)Was this useful?