Dockerfile 16 KB

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