Dockerfile 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. # syntax=docker/dockerfile:1.7
  2. # Ubuntu 24.04 + NVIDIA GLX + XFCE + PipeWire + Firefox.
  3. #
  4. # Important: the Selkies GStreamer runtime, Python wheel, web client and
  5. # joystick interposer all come from the SAME published Selkies release.
  6. # Do not mix these assets with py-build:main or a frontend built from main.
  7. ARG DISTRIB_RELEASE=24.04
  8. ARG SELKIES_VERSION=1.6.2
  9. ARG NVIDIA_VAAPI_DRIVER_VERSION=latest
  10. # -----------------------------------------------------------------------------
  11. # Build nvidia-vaapi-driver without retaining its build dependencies
  12. # -----------------------------------------------------------------------------
  13. FROM docker.io/library/ubuntu:${DISTRIB_RELEASE} AS nvidia-vaapi-builder
  14. ARG DEBIAN_FRONTEND=noninteractive
  15. ARG NVIDIA_VAAPI_DRIVER_VERSION
  16. SHELL ["/bin/bash", "-o", "pipefail", "-c"]
  17. RUN apt-get update \
  18. && apt-get install --no-install-recommends -y \
  19. ca-certificates \
  20. curl \
  21. gcc \
  22. jq \
  23. meson \
  24. ninja-build \
  25. pkg-config \
  26. libdrm-dev \
  27. libegl-dev \
  28. libffmpeg-nvenc-dev \
  29. libgstreamer-plugins-bad1.0-dev \
  30. libva-dev \
  31. && rm -rf /var/lib/apt/lists/*
  32. RUN set -eux; \
  33. version="${NVIDIA_VAAPI_DRIVER_VERSION}"; \
  34. if [[ "${version}" == "latest" ]]; then \
  35. version="$(curl -fsSL https://api.github.com/repos/elFarto/nvidia-vaapi-driver/releases/latest \
  36. | jq -r '.tag_name' \
  37. | sed 's/^v//')"; \
  38. fi; \
  39. curl -fsSL \
  40. "https://github.com/elFarto/nvidia-vaapi-driver/archive/refs/tags/v${version}.tar.gz" \
  41. -o /tmp/nvidia-vaapi-driver.tar.gz; \
  42. install -d -m 0755 /tmp/nvidia-vaapi-driver; \
  43. tar -xzf /tmp/nvidia-vaapi-driver.tar.gz \
  44. --strip-components=1 \
  45. -C /tmp/nvidia-vaapi-driver; \
  46. cd /tmp/nvidia-vaapi-driver; \
  47. meson setup build --prefix=/usr --buildtype=release; \
  48. meson compile -C build; \
  49. DESTDIR=/out meson install -C build
  50. # -----------------------------------------------------------------------------
  51. # Final image
  52. # -----------------------------------------------------------------------------
  53. FROM docker.io/library/ubuntu:${DISTRIB_RELEASE}
  54. ARG DEBIAN_FRONTEND=noninteractive
  55. ARG DISTRIB_RELEASE
  56. ARG SELKIES_VERSION
  57. ARG TZ=Europe/Paris
  58. ARG USER_NAME=ubuntu
  59. ARG USER_UID=1000
  60. ARG USER_GID=1000
  61. LABEL org.opencontainers.image.title="Selkies NVIDIA XFCE Desktop" \
  62. org.opencontainers.image.description="XFCE remote desktop using the stable Selkies GStreamer release stack" \
  63. org.opencontainers.image.source="https://github.com/selkies-project/docker-selkies-glx-desktop"
  64. SHELL ["/bin/bash", "-o", "pipefail", "-c"]
  65. ENV TZ="${TZ}" \
  66. LANG="en_US.UTF-8" \
  67. LANGUAGE="en_US:en" \
  68. LC_ALL="en_US.UTF-8" \
  69. PASSWD="mypasswd" \
  70. DISPLAY=":20" \
  71. DISPLAY_SIZEW="1920" \
  72. DISPLAY_SIZEH="1080" \
  73. DISPLAY_REFRESH="60" \
  74. DISPLAY_DPI="96" \
  75. DISPLAY_CDEPTH="24" \
  76. VIDEO_PORT="DFP" \
  77. DESKTOP_SESSION="xfce" \
  78. XDG_SESSION_DESKTOP="xfce" \
  79. XDG_CURRENT_DESKTOP="XFCE" \
  80. XDG_SESSION_TYPE="x11" \
  81. NGINX_PORT="8080" \
  82. SELKIES_PORT="8081" \
  83. SELKIES_ENCODER="nvh264enc" \
  84. SELKIES_ENABLE_HTTPS="false" \
  85. SELKIES_ENABLE_RESIZE="false" \
  86. SELKIES_ENABLE_BASIC_AUTH="true" \
  87. NVIDIA_VISIBLE_DEVICES="all" \
  88. NVIDIA_DRIVER_CAPABILITIES="all" \
  89. __GL_SYNC_TO_VBLANK="0" \
  90. __GLX_VENDOR_LIBRARY_NAME="nvidia" \
  91. LIBVA_DRIVER_NAME="nvidia" \
  92. NVD_BACKEND="direct" \
  93. MOZ_DISABLE_RDD_SANDBOX="1" \
  94. MOZ_X11_EGL="1" \
  95. PIPEWIRE_LATENCY="128/48000" \
  96. XDG_RUNTIME_DIR="/tmp/runtime-ubuntu" \
  97. PIPEWIRE_RUNTIME_DIR="/tmp/runtime-ubuntu" \
  98. PULSE_RUNTIME_PATH="/tmp/runtime-ubuntu/pulse" \
  99. PULSE_SERVER="unix:/tmp/runtime-ubuntu/pulse/native" \
  100. DBUS_SYSTEM_BUS_ADDRESS="unix:path=/tmp/runtime-ubuntu/dbus-system-bus" \
  101. APPIMAGE_EXTRACT_AND_RUN="1" \
  102. SUDO_EDITOR="mousepad" \
  103. PIP_BREAK_SYSTEM_PACKAGES="1"
  104. # Bootstrap APT and locales.
  105. RUN apt-get update \
  106. && apt-get install --no-install-recommends -y \
  107. ca-certificates \
  108. curl \
  109. gnupg \
  110. locales \
  111. ssl-cert \
  112. tzdata \
  113. && locale-gen en_US.UTF-8 fr_FR.UTF-8 \
  114. && ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime \
  115. && echo "${TZ}" > /etc/timezone \
  116. && rm -rf /var/lib/apt/lists/*
  117. # Firefox DEB and the same PipeWire/WirePlumber PPAs as the original image.
  118. RUN install -d -m 0755 \
  119. /etc/apt/preferences.d \
  120. /etc/apt/sources.list.d \
  121. /etc/apt/trusted.gpg.d \
  122. && printf '%s\n' \
  123. 'Package: firefox*' \
  124. 'Pin: version 1:1snap*' \
  125. 'Pin-Priority: -1' \
  126. > /etc/apt/preferences.d/firefox-nosnap \
  127. && curl -fsSL \
  128. 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x738BEB9321D1AAEC13EA9391AEBDF4819BE21867' \
  129. | gpg --dearmor \
  130. > /etc/apt/trusted.gpg.d/mozillateam-ubuntu-ppa.gpg \
  131. && echo \
  132. "deb https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu noble main" \
  133. > /etc/apt/sources.list.d/mozillateam-ubuntu-ppa.list \
  134. && curl -fsSL \
  135. 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xFC43B7352BCC0EC8AF2EEB8B25088A0359807596' \
  136. | gpg --dearmor \
  137. > /etc/apt/trusted.gpg.d/pipewire-debian-ubuntu.gpg \
  138. && echo \
  139. "deb https://ppa.launchpadcontent.net/pipewire-debian/pipewire-upstream/ubuntu noble main" \
  140. > /etc/apt/sources.list.d/pipewire-upstream.list \
  141. && echo \
  142. "deb https://ppa.launchpadcontent.net/pipewire-debian/wireplumber-upstream/ubuntu noble main" \
  143. > /etc/apt/sources.list.d/wireplumber-upstream.list
  144. # Desktop, Xorg, NVIDIA interfaces, PipeWire and the dependencies used by the
  145. # original Selkies GStreamer release. No KDE, Chrome, Wine, KasmVNC, RustDesk,
  146. # i386 stack, Intel VA-API stack or AMD/Mesa Vulkan stack.
  147. RUN apt-get update \
  148. && apt-get install --no-install-recommends -y \
  149. apache2-utils \
  150. apt-utils \
  151. alsa-utils \
  152. aom-tools \
  153. bash-completion \
  154. binutils \
  155. btop \
  156. bzip2 \
  157. clinfo \
  158. coturn \
  159. dbus-user-session \
  160. dbus-x11 \
  161. desktop-file-utils \
  162. dnsutils \
  163. file \
  164. firefox \
  165. fonts-dejavu \
  166. fonts-liberation \
  167. fonts-noto \
  168. fonts-noto-cjk \
  169. fonts-noto-color-emoji \
  170. fonts-noto-mono \
  171. fonts-ubuntu \
  172. fuse \
  173. git \
  174. glib-networking \
  175. gstreamer1.0-libcamera \
  176. gstreamer1.0-pipewire \
  177. gstreamer1.0-plugins-bad \
  178. gvfs \
  179. jackd2 \
  180. jq \
  181. kmod \
  182. less \
  183. libdrm2 \
  184. libegl1 \
  185. libelf-dev \
  186. libgcrypt20 \
  187. libgirepository-1.0-1 \
  188. libgl1 \
  189. libgles1 \
  190. libgles2 \
  191. libglib2.0-0 \
  192. libglvnd-dev \
  193. libglvnd0 \
  194. libglx0 \
  195. libgstreamer-plugins-bad1.0-0 \
  196. libgudev-1.0-0 \
  197. libjack-jackd2-0 \
  198. libopenh264-dev \
  199. libopengl0 \
  200. libopus0 \
  201. libpci3 \
  202. libpipewire-0.3-modules \
  203. libpipewire-module-x11-bell \
  204. libpulse0 \
  205. libsm6 \
  206. libspa-0.2-bluetooth \
  207. libspa-0.2-jack \
  208. libspa-0.2-modules \
  209. libva-drm2 \
  210. libva-x11-2 \
  211. libva2 \
  212. libvpx-dev \
  213. libvulkan1 \
  214. libwayland-dev \
  215. libwayland-egl1 \
  216. libx11-6 \
  217. libx11-xcb1 \
  218. libxau6 \
  219. libxcb-dri3-0 \
  220. libxcb1 \
  221. libxdamage1 \
  222. libxdmcp6 \
  223. libxext6 \
  224. libxfixes3 \
  225. libxkbcommon0 \
  226. libxtst6 \
  227. libxv1 \
  228. mousepad \
  229. nano \
  230. neofetch \
  231. net-tools \
  232. netcat-openbsd \
  233. nginx \
  234. ocl-icd-libopencl1 \
  235. pavucontrol \
  236. pciutils \
  237. pipewire \
  238. pipewire-alsa \
  239. pipewire-audio-client-libraries \
  240. pipewire-jack \
  241. pipewire-libcamera \
  242. pipewire-locales \
  243. pipewire-v4l2 \
  244. pipewire-vulkan \
  245. procps \
  246. psmisc \
  247. python3 \
  248. python3-dev \
  249. python3-gi \
  250. python3-pip \
  251. python3-setuptools \
  252. python3-venv \
  253. python3-wheel \
  254. ristretto \
  255. sudo \
  256. supervisor \
  257. svt-av1 \
  258. thunar \
  259. tumbler \
  260. udev \
  261. unzip \
  262. vainfo \
  263. vim \
  264. vulkan-tools \
  265. wayland-protocols \
  266. wget \
  267. wireplumber \
  268. wireplumber-locales \
  269. wmctrl \
  270. x11-apps \
  271. x11-utils \
  272. x11-xkb-utils \
  273. x11-xserver-utils \
  274. x264 \
  275. x265 \
  276. xauth \
  277. xbitmaps \
  278. xclip \
  279. xcvt \
  280. xdg-user-dirs \
  281. xdg-utils \
  282. xfce4 \
  283. xfce4-goodies \
  284. xfce4-notifyd \
  285. xfce4-pulseaudio-plugin \
  286. xfce4-terminal \
  287. xfonts-base \
  288. xfonts-scalable \
  289. xinit \
  290. xkb-data \
  291. xsel \
  292. xserver-xorg-core \
  293. xserver-xorg-input-libinput \
  294. xserver-xorg-legacy \
  295. xsettingsd \
  296. xterm \
  297. xdotool \
  298. xz-utils \
  299. zip \
  300. zstd \
  301. gir1.2-wp-0.5 \
  302. && sed -i \
  303. -e 's#/var/log/nginx/access\.log#/dev/stdout#g' \
  304. -e 's#/var/log/nginx/error\.log#/dev/stderr#g' \
  305. -e 's#/run/nginx\.pid#/tmp/nginx.pid#g' \
  306. /etc/nginx/nginx.conf \
  307. && printf '\nerror_log /dev/stderr;\n' >> /etc/nginx/nginx.conf \
  308. && apt-get clean \
  309. && rm -rf \
  310. /var/lib/apt/lists/* \
  311. /var/cache/apt/* \
  312. /var/cache/debconf/* \
  313. /var/log/* \
  314. /tmp/* \
  315. /var/tmp/*
  316. # Keep only the compiled NVIDIA VA-API runtime.
  317. COPY --from=nvidia-vaapi-builder /out/usr/ /usr/
  318. # NVIDIA libraries are injected by the NVIDIA GPU Operator / Container Toolkit.
  319. RUN printf '%s\n' \
  320. '/usr/local/nvidia/lib' \
  321. '/usr/local/nvidia/lib64' \
  322. > /etc/ld.so.conf.d/nvidia.conf \
  323. && install -d -m 0755 \
  324. /etc/OpenCL/vendors \
  325. /etc/vulkan/icd.d \
  326. /usr/share/glvnd/egl_vendor.d \
  327. && echo 'libnvidia-opencl.so.1' \
  328. > /etc/OpenCL/vendors/nvidia.icd \
  329. && cat > /etc/vulkan/icd.d/nvidia_icd.json <<'JSON'
  330. {
  331. "file_format_version": "1.0.0",
  332. "ICD": {
  333. "library_path": "libGLX_nvidia.so.0",
  334. "api_version": "1.3.0"
  335. }
  336. }
  337. JSON
  338. RUN cat > /usr/share/glvnd/egl_vendor.d/10_nvidia.json <<'JSON'
  339. {
  340. "file_format_version": "1.0.0",
  341. "ICD": {
  342. "library_path": "libEGL_nvidia.so.0"
  343. }
  344. }
  345. JSON
  346. ENV PATH="/usr/local/nvidia/bin:${PATH}" \
  347. LD_LIBRARY_PATH="/usr/local/nvidia/lib:/usr/local/nvidia/lib64"
  348. # Install every Selkies component from the same stable v1.6.2 release.
  349. # evdev is built from source on Python 3.12, so gcc is needed only for this step.
  350. RUN set -eux; \
  351. apt-get update; \
  352. apt-get install --no-install-recommends -y gcc; \
  353. ubuntu_version="$(. /etc/os-release; printf '%s' "${VERSION_ID}")"; \
  354. arch="$(dpkg --print-architecture)"; \
  355. release_url="https://github.com/selkies-project/selkies/releases/download/v${SELKIES_VERSION}"; \
  356. curl -fsSL \
  357. "${release_url}/gstreamer-selkies_gpl_v${SELKIES_VERSION}_ubuntu${ubuntu_version}_${arch}.tar.gz" \
  358. | tar -xzf - -C /opt; \
  359. wheel_path="/tmp/selkies_gstreamer-${SELKIES_VERSION}-py3-none-any.whl"; \
  360. curl -fsSL \
  361. "${release_url}/selkies_gstreamer-${SELKIES_VERSION}-py3-none-any.whl" \
  362. -o "${wheel_path}"; \
  363. python3 -m pip install \
  364. --break-system-packages \
  365. --no-cache-dir \
  366. --force-reinstall \
  367. "${wheel_path}" \
  368. 'websockets<14.0'; \
  369. curl -fsSL \
  370. "${release_url}/selkies-gstreamer-web_v${SELKIES_VERSION}.tar.gz" \
  371. | tar -xzf - -C /opt; \
  372. curl -fsSL \
  373. "${release_url}/selkies-js-interposer_v${SELKIES_VERSION}_ubuntu${ubuntu_version}_${arch}.deb" \
  374. -o /tmp/selkies-js-interposer.deb; \
  375. apt-get update; \
  376. apt-get install --no-install-recommends -y \
  377. /tmp/selkies-js-interposer.deb; \
  378. test -f /opt/gst-web/index.html; \
  379. test -f /opt/gstreamer/gst-env; \
  380. command -v selkies-gstreamer; \
  381. rm -f \
  382. "${wheel_path}" \
  383. /tmp/selkies-js-interposer.deb; \
  384. apt-get purge -y gcc; \
  385. apt-get autoremove -y; \
  386. apt-get clean; \
  387. rm -rf /var/lib/apt/lists/* /var/cache/apt/* /tmp/* /var/tmp/*
  388. # Regular desktop user and the permissions required by the original entrypoints.
  389. RUN set -eux; \
  390. if ! getent group "${USER_NAME}" >/dev/null; then \
  391. groupadd --gid "${USER_GID}" "${USER_NAME}"; \
  392. fi; \
  393. if ! id -u "${USER_NAME}" >/dev/null 2>&1; then \
  394. useradd \
  395. --uid "${USER_UID}" \
  396. --gid "${USER_NAME}" \
  397. --create-home \
  398. --shell /bin/bash \
  399. "${USER_NAME}"; \
  400. else \
  401. usermod --shell /bin/bash "${USER_NAME}"; \
  402. fi; \
  403. for group in \
  404. adm audio cdrom dialout dip fax floppy games input lp plugdev render \
  405. ssl-cert sudo tape tty video voice; \
  406. do \
  407. getent group "${group}" >/dev/null \
  408. && usermod -aG "${group}" "${USER_NAME}" \
  409. || true; \
  410. done; \
  411. echo "${USER_NAME} ALL=(ALL:ALL) NOPASSWD: ALL" \
  412. > "/etc/sudoers.d/${USER_NAME}"; \
  413. chmod 0440 "/etc/sudoers.d/${USER_NAME}"; \
  414. echo "${USER_NAME}:${PASSWD}" | chpasswd; \
  415. cp -a /usr/bin/sudo /usr/bin/sudo-root; \
  416. chown root:root /usr/bin/sudo-root; \
  417. chmod 4755 /usr/bin/sudo-root; \
  418. install -d -o "${USER_UID}" -g "${USER_GID}" -m 0700 \
  419. /tmp/runtime-ubuntu; \
  420. chown -R \
  421. "${USER_UID}:${USER_GID}" \
  422. "/home/${USER_NAME}" \
  423. /etc/X11 \
  424. /etc/nginx \
  425. /opt/gstreamer \
  426. /opt/gst-web \
  427. /var/lib/nginx
  428. # XFCE defaults for a permanently streamed desktop.
  429. RUN install -d -m 0755 \
  430. /etc/xdg/xfce4/xfconf/xfce-perchannel-xml \
  431. /etc/firefox/policies \
  432. && cat > /etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml <<'XML'
  433. <?xml version="1.0" encoding="UTF-8"?>
  434. <channel name="xfce4-power-manager" version="1.0">
  435. <property name="xfce4-power-manager" type="empty">
  436. <property name="blank-on-ac" type="int" value="0"/>
  437. <property name="dpms-enabled" type="bool" value="false"/>
  438. <property name="lock-screen-suspend-hibernate" type="bool" value="false"/>
  439. </property>
  440. </channel>
  441. XML
  442. RUN cat > /etc/firefox/policies/policies.json <<'JSON'
  443. {
  444. "policies": {
  445. "Preferences": {
  446. "gfx.x11-egl.force-enabled": {
  447. "Value": true,
  448. "Status": "default"
  449. },
  450. "media.ffmpeg.vaapi.enabled": {
  451. "Value": true,
  452. "Status": "default"
  453. },
  454. "media.hardware-video-decoding.force-enabled": {
  455. "Value": true,
  456. "Status": "default"
  457. },
  458. "media.rdd-ffmpeg.enabled": {
  459. "Value": true,
  460. "Status": "default"
  461. }
  462. }
  463. }
  464. }
  465. JSON
  466. RUN update-alternatives --set x-www-browser /usr/bin/firefox || true
  467. # Keep the ORIGINAL entrypoint and Selkies GStreamer entrypoint.
  468. # Replace supervisord.conf with the cleaned version supplied with this file.
  469. COPY --chown=${USER_UID}:${USER_GID} entrypoint.sh /etc/entrypoint.sh
  470. COPY --chown=${USER_UID}:${USER_GID} selkies-gstreamer-entrypoint.sh /etc/selkies-gstreamer-entrypoint.sh
  471. COPY --chown=${USER_UID}:${USER_GID} supervisord.conf /etc/supervisord.conf
  472. RUN sed -i 's/\r$//' \
  473. /etc/entrypoint.sh \
  474. /etc/selkies-gstreamer-entrypoint.sh \
  475. /etc/supervisord.conf \
  476. && chmod 0755 \
  477. /etc/entrypoint.sh \
  478. /etc/selkies-gstreamer-entrypoint.sh \
  479. /etc/supervisord.conf
  480. # Original coTURN helper used by selkies-gstreamer-entrypoint.sh.
  481. RUN cat > /etc/start-turnserver.sh <<'EOF_TURN'
  482. #!/bin/bash
  483. set -e
  484. exec turnserver \
  485. --verbose \
  486. --listening-ip="0.0.0.0" \
  487. --listening-ip="::" \
  488. --listening-port="${SELKIES_TURN_PORT:-3478}" \
  489. --realm="${TURN_REALM:-example.com}" \
  490. --external-ip="${TURN_EXTERNAL_IP:-127.0.0.1}" \
  491. --min-port="${TURN_MIN_PORT:-49152}" \
  492. --max-port="${TURN_MAX_PORT:-65535}" \
  493. --channel-lifetime="${TURN_CHANNEL_LIFETIME:--1}" \
  494. --lt-cred-mech \
  495. --user="selkies:${TURN_RANDOM_PASSWORD}" \
  496. --no-cli \
  497. --cli-password="${TURN_RANDOM_PASSWORD}" \
  498. --userdb="${XDG_RUNTIME_DIR:-/tmp}/turnserver-turndb" \
  499. --pidfile="${XDG_RUNTIME_DIR:-/tmp}/turnserver.pid" \
  500. --log-file="stdout" \
  501. --allow-loopback-peers \
  502. ${TURN_EXTRA_ARGS} "$@"
  503. EOF_TURN
  504. RUN chmod 0755 /etc/start-turnserver.sh
  505. USER ${USER_UID}:${USER_GID}
  506. ENV USER="${USER_NAME}" \
  507. HOME="/home/${USER_NAME}" \
  508. SHELL="/bin/bash"
  509. WORKDIR /home/${USER_NAME}
  510. EXPOSE 8080
  511. ENTRYPOINT ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]