Jump to content

Webtop on PMSS

From Pulsed Media Wiki


Webtop gives a Pulsed Media seedbox a graphical Linux desktop that opens in a browser tab, a replacement for a VNC server with no VNC client to install and no public port to open. In the test the container used about 800 MiB of RAM. It is a LinuxServer.io container that runs in your own account with rootless Docker, which every PMSS seedbox includes, and it is reached over HTTPS at your account's address behind a login of its own. Tested on a Pulsed Media seedbox on 24 October 2026 with lscr.io/linuxserver/webtop:alpine-i3; the steps below pin the digest that tag pointed to that day, and the test itself ran the tag.

A black desktop with a dialog titled "i3: first configuration"
Webtop in a browser tab on a test account, 24 October 2026, at its first start: i3 asks whether to write its config file.

Privacy

Privacy level: LOW (the levels used on these guides are HIGH, MEDIUM, LOW and NONE). Webtop needs no outside account. Programs you run on the desktop connect to the internet from the server's address, not from yours. Whether the image or its browser-streaming software sends usage data was not checked.

The desktop is reachable from the internet at your account's address, protected only by its own user name and password. LinuxServer.io's README warns that the container "provides privileged access to the host system" and should not be exposed to the internet unless it is secured properly, and it calls this login suitable only for a trusted local network. On a seedbox your panel login cannot sit in front of it as a second lock: both use HTTP basic authentication, so the fragment has to hand the login to Webtop (Lighttpd Custom Configuration explains why). A browser that is logged into your panel therefore sends your panel login, your account password, to Webtop until you sign in to Webtop and its login replaces it, so open Webtop in a separate browser profile. According to LinuxServer.io, anyone who gets past the Webtop login has a terminal with passwordless sudo inside the container. Under rootless Docker that root is your own account on the server, not the server's root, but it can read and change everything mounted into the container and make network connections from the server. Keep the long random password the steps below generate, and mount only the folders you need.

Requirements

  • Memory: plan on about 800 MiB (Docker reported 798 MiB in the test, a few minutes after start; the container's processes added up to about 746 MiB). Programs you open on the desktop add to that. Seedbox and Storage Box Resource Tiers lists each plan's memory, and the torrent client and every container share it. On the 1 GB seedbox plan Webtop alone takes most of that; the 2 GB and 4 GB plans leave room for it. Storage boxes, with 250 MB or 500 MB, have less memory than Webtop uses. The test did not check how the plan's limit applies to the container.
  • Disk: the image is 2.9 GB. Your file quota is 500 files per GiB of disk quota (Pikkumies on PMSS gives the rule); check what you have left with quota -s before you start. On the test account (100,000 files soft limit, 125,000 hard) the latest image failed to unpack with "disk quota exceeded" while the alpine-i3 image was already present; the file count of the alpine-i3 image was not measured.
  • Desktop: alpine-i3 is Alpine Linux with i3, a keyboard-driven tiling window manager. The tester chose it as the lightest option and compared no other tag; there is no alpine-xfce tag.
  • Own login: yes, a user name and password written to a file before the first start (HTTP basic auth, checked by the container itself).
  • Address: https://SERVER.pulsedmedia.com/user-USERNAME/webtop/, set with the container's SUBFOLDER setting.
  • Other apps: none.

Pulsed Media seedbox plans all include rootless Docker. Running and updating Webtop is yours to do; this page pins one build.

Install

In the commands below, replace USERNAME with your account name, SERVER with your server's name as it appears in your panel address, and PORT with a free port between 2000 and 38000: run ss -tln and pick a number that does not appear in its output. Run everything over SSH as your own account.

1. Create the folder and close it to other accounts:

mkdir -p ~/docker/webtop/config
chmod 700 ~/docker/webtop

2. Write the login before the first start. This creates the user webtop with a random password in a file only you can read. Other accounts on the server can connect to 127.0.0.1, so the login has to exist before the port does:

printf 'CUSTOM_USER=webtop\nPASSWORD=%s\n' "$(head -c 24 /dev/urandom | base64 | tr -d '\n')" > ~/docker/webtop/webtop.env
chmod 600 ~/docker/webtop/webtop.env

Read the password later with grep '^PASSWORD=' ~/docker/webtop/webtop.env.

3. Start the container on the loopback address only. PUID=0 and PGID=0 make most of the desktop's files belong to your own account:

docker run -d --name webtop --restart unless-stopped --shm-size=1gb \
  --env-file ~/docker/webtop/webtop.env \
  -e PUID=0 -e PGID=0 -e TZ=Etc/UTC -e SUBFOLDER=/user-USERNAME/webtop/ \
  -p 127.0.0.1:PORT:3000 -v ~/docker/webtop/config:/config \
  lscr.io/linuxserver/webtop@sha256:06d9fa1a7d6334699eff46b7d79a4a6f640a9db182c8c32a2fe08917c8505c99

Publish only port 3000. The image also serves HTTPS on 3001, which is not needed here: your web server already provides HTTPS. --shm-size=1gb sets the size of the container's shared-memory area, as upstream recommends.

4. Add the proxy fragment. auth.require = () hands the login to Webtop, and "upgrade" => "enable" carries the WebSocket the desktop is drawn over. There is no map-urlpath, because SUBFOLDER already makes Webtop answer under your path:

cat > ~/.lighttpd/custom.d/webtop.conf << 'EOF'
$HTTP["url"] =~ "^/user-USERNAME/webtop/" {
  auth.require = ()
  proxy.server = ( "" => ( ( "host" => "127.0.0.1", "port" => PORT ) ) )
  proxy.header = ( "upgrade" => "enable" )
}
EOF
/usr/sbin/lighttpd -t -f ~/.lighttpd.conf

The last command checks the syntax and prints Syntax OK. If it prints an error instead, fix or delete webtop.conf straight away: a broken fragment keeps your web panel down at the next restart. Your web server picks the file up within a minute or two, and the panel and proxied apps are unavailable for a moment while it restarts.

5. Open https://SERVER.pulsedmedia.com/user-USERNAME/webtop/, with the trailing slash, and sign in as webtop with the password from webtop.env. In the test the desktop first loaded about 90 seconds after the container started.

Configure

  • First start: i3 asks whether to generate its configuration file; press Enter to accept. The file is written to ~/docker/webtop/config, the mounted folder outside the container. i3 has no menus or taskbar; it is driven by keyboard shortcuts, described in the i3 user guide. The test saw this first screen and went no further.
  • Files: the desktop sees only what is mounted into it, here ~/docker/webtop/config as its home folder. To add another folder, run docker rm -f webtop and step 3 again with an extra -v mount; anyone who passes the Webtop login can then read and change those files too. Extra mounts were not tested.
  • Apps panel: Webtop's built-in Apps panel stays hidden on Pulsed Media servers, because the tool it relies on (proot) is not allowed to trace processes there.
  • Updating: the digest in step 3 keeps you on the build from 24 October 2026. To move to a newer build, check quota -s first, because the old and the new image both take space until the old one is removed. Find the digest the tag points to now (commands below), run docker rm -f webtop and step 3 again with that digest, then remove the old image with docker rmi and the old digest. ~/docker/webtop/config keeps your settings. Updating was not tested.
docker pull lscr.io/linuxserver/webtop:alpine-i3
docker image inspect lscr.io/linuxserver/webtop:alpine-i3 --format '{{index .RepoDigests 0}}'

Integrate

LinuxServer.io's README describes no link between Webtop and Sonarr, Radarr, Prowlarr or Jellyfin. Reaching them from a program on the desktop was not tested.

Test

  • docker ps lists webtop as Up.
  • https://SERVER.pulsedmedia.com/user-USERNAME/webtop/ asks for the Webtop login and, with it, shows the desktop. Your panel login alone is refused.
  • A request with no login straight to the port is refused, which is what another account on the server would see: curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:PORT/ prints 401.
  • stat -c '%a %n' ~/docker/webtop ~/docker/webtop/webtop.env shows 700 and 600.

Uninstall

This removes the container and its web address and leaves your files and the image alone:

docker rm -f webtop
rm ~/.lighttpd/custom.d/webtop.conf
touch ~/.lighttpd/custom

The touch makes your web server restart without the removed fragment (deleting or disabling a fragment explains why).

Removing the data is your choice. It deletes the desktop's home folder, everything saved on the desktop, and the login file. Some files in it are owned by a container user ID, so a plain rm fails; delete them from inside a container first. That step needs the image, so the image is removed last, which frees its 2.9 GB:

docker run --rm --entrypoint /bin/sh -v ~/docker/webtop:/srv/cleanup \
  lscr.io/linuxserver/webtop@sha256:06d9fa1a7d6334699eff46b7d79a4a6f640a9db182c8c32a2fe08917c8505c99 \
  -c 'find /srv/cleanup/config -delete'
rm -r ~/docker/webtop
docker rmi lscr.io/linuxserver/webtop@sha256:06d9fa1a7d6334699eff46b7d79a4a6f640a9db182c8c32a2fe08917c8505c99

To keep the data but free the disk space, run only the last line. If you also pulled the alpine-i3 tag for updating, remove it as well with docker rmi lscr.io/linuxserver/webtop:alpine-i3.

Troubleshooting

  • docker run fails with "disk quota exceeded" while unpacking: the account ran out of files, not space. Check quota -s and remove images you no longer use (docker images, then docker rmi). On the test account the alpine-i3 image unpacked; the latest image failed there after alpine-i3 was already present.
  • The login prompt keeps coming back: use the user webtop and the password from webtop.env, not your panel login, and check that the fragment has auth.require = () and no setenv.set-request-header line.
  • The page loads but the desktop never appears: the fragment needs "upgrade" => "enable"; the desktop is drawn over a WebSocket. Give a fresh container about 90 seconds before the first load.
  • rm says "Permission denied" on uninstall: run the container-side delete shown in Uninstall first. The test needed it for a folder owned by a container user ID.
  • 502 Bad Gateway: the container is not running or the port in the fragment is wrong; see the 502 section of Lighttpd Custom Configuration. A 502 for a few seconds right after you save a fragment is the web server restarting.

See also