XFCE dark theme for qubes which are based on minimal templates

Since Qubes OS default is XFCE I thought it would be nice to switch my all my qubes from Gnome to XFCE (also using xfce-terminal, mousepad, thunar … instead of gnome apps).

I already had a very easy and straight forward implementation for Gnome ( gsettings set org.gnome.desktop.interface gtk-theme <THEME-NAME> …) but for weeks now, I cannot get XFCE based on debian-13-minimal to look like this:


(based on debian-13-xfce; just as reference)

Instead, I always end up with the default (fallback) light theme:

I test (almost!) everthing related to xfconf-query and xfconfd

XFCE alternatives to ‘Adwaita-dark’: numix-gtk-theme and arc-theme

My packages, I am currently using for testing:

dbus-x11
at-spi2-core

tumbler
ffmpegthumbnailer
gvfs-backends

xfconf
xfce4
gtk2-engines

arc-theme
#numix-gtk-theme

papirus-icon-theme
librsvg2-common
hicolor-icon-theme
adwaita-icon-theme
gnome-themes-extra
gtk-update-icon-cache

xfce4-terminal
thunar
mousepad
fonts-jetbrains-mono
fonts-atkinson-hyperlegible
Some notes about listed packages.
  • dbus-x11: Starts a per‑user D‑Bus session (eval $(dbus-launch …)) that XFCE, AT‑SPI and thumbnail services require.
  • at-spi2-core: Supplies the “accessibility bus” that eliminates the dbind‑WARNING about org.a11y.Bus.
  • tumbler: Registers the thumbnailer D‑Bus service that Thunar queries; without it you get “Failed to retrieve supported types”.
  • ffmpegthumbnailer: Gives Tumbler the ability to generate thumbnails for video files.
  • gvfs-backends: Extends GVFS so Tumbler can expose the thumbnailer service correctly; also prevents missing‑service warnings.
  • gnome-themes-extra is required for ‘Adwaita-dark’ and for any legacy GTK 2 applications (e.g., Thunar, Mousepad).
    -Papirus (and many other icon packs) are derived themes – they contain only the icons that differ from the base set and declare Inherits=hicolor. Therefore, ‘hicolor-icon-theme’ is required.

Beside the xfconf-query I have also tested to use directly *.xml configuration files - both without success.

Qubes OS configuration (for XFCE dark theme testing)
dom0 terminal:

dxy=$(qvm-template list --available | grep -Eo "debian.*minimal" | tail -n 1 | grep -Eo "[0-9]+")
src="debian-${dxy}-minimal"
tpl="d${dxy}m-xfce"
app="xfce-dark"
qvm-template install ${src} && qvm-clone ${src} ${tpl}
# ... installing mandatory XFCE packages for dark theme ...
# ... using 'apt install --no-recommended' ...
# ... configure XFCE theme (dark theme, icons, fonts)
qvm-create ${app} --template ${tpl} --label red
The XFCE configuration code section I am currently using
qvm-run --pass-io --no-gui --user user "${tpl}" bash -c '
    set -euo pipefail

    # make sure the xfconf daemon is alive
    if ! pgrep -x xfconfd >/dev/null 2>&1; then
        echo "[info] starting xfconfd …"
        /usr/lib/x86_64-linux-gnu/xfce4/xfconf/xfconfd &
        sleep 1   # give it a moment to initialise
    fi

    # set the wanted keys (ignore errors on a per-key basis)
    echo "[info] setting xsettings keys …"
    xfconf-query -c xsettings -p /Net/IconThemeName     -t string -s "Papirus"                  --create
    xfconf-query -c xsettings -p /Net/ThemeName         -t string -s "Arc-Dark"                 --create
    xfconf-query -c xfwm4     -p /general/theme         -t string -s "Arc-Dark"                 --create
    xfconf-query -c xsettings -p /Gtk/FontName          -t string -s "Atkinson Hyperlegible 13" --create
    xfconf-query -c xsettings -p /Gtk/MonospaceFontName -t string -s "JetBrains Mono 13"        --create

    # verify what is actually stored (ignore non-zero status)
    echo "[info] current values (xfconf-query -c xsettings -lv) …"
    xfconf-query -c xsettings -lv

    echo "[info] full key list for channel xsettings …"
    xfconf-query -c xsettings -l

    # refresh the icon caches (optional - uncomment if you need)
    #gtk-update-icon-cache -f /usr/share/icons/Papirus   || echo "[warn] Papirus cache failed"
    #gtk-update-icon-cache -f /usr/share/icons/Arc-Dark   || echo "[warn] Arc-Dark cache failed"
    #gtk-update-icon-cache -f /usr/share/icons/hicolor   || echo "[warn] hicolor cache failed"

    echo "[done] xfce config finished"
    '

Objective
At the end, it should be put into a dom0 script to auto-generate template and application qubes. The entire XFCE configuration should be done in dom0 (bash scirpt).

I hope someone here has already a working XFCE dark theme based on minimal Debian templates, points me to the missing package or the proper configuration coding.
Thanks for your support!

1 Like

Setting up a dark theme for a minimal qube might seem straightforward knowing this central configuration apps from a classical Linux OS or Windows. But the Qubes OS reality for a minimal qube might be challenging to keep minimalism but functionality.

Objective

Create a Xfce application qube with:

  • Minimal package footprint (no unnecessary packages and daemons)
  • (grey) Dark theme: Arc-Dark theme (GTK)
  • Set new icons: Papirus icons
  • Set new default fonts: JetBrains and Atkinson Hyperlegible (was not able to find a solution to set JetBrains as default for mousepad; bug: [mousepad] Completely ignores configured font settings)
  • Xfce4-terminal with a hidden menu bar (I only found a solution by modifing *.desktop)
  • Thunar with thumbnails
  • Make all configuration within the template qube

Note: Not touched now, but should be simple: Extend to QT apps.

Architecture overview

Isolated qubes for testing purpose:

debian-13-minimal       # source qube
 └─ d13m-xfce-arc-dark  # template qube
     └─ xfce-arc-dark   # application qube

All-in-one script

Feel free to reuse, share and modify.
You must review and trust it, afterwards copy and run in dom0.

2026-03-15 | Qubes OS 4.3 | minimal Debian 13 | Xfce Arc-Dark
#!/usr/bin/env bash
set -o errexit   # abort script on non-zero exit status
set -o nounset   # abort script on unbound variable
set -o pipefail  # do not hide errors within pipes

qube_src="debian-13-minimal"    # minimal qube template
qube_tpl="d13m-xfce-arc-dark"   # baseline template qube for Xfce Arc-Dark
qube_app="xfce-arc-dark"        # final Xfce Arc-Dark application qube
dom0_dir="/home/user/tmp-xfce"  # staging area on dom0 (feel free to change)

mkdir --parents "${dom0_dir}"

# ────────────────────────────────────────────────────────────────────────
#  Helper function: copy file from dom0 to qube
# ────────────────────────────────────────────────────────────────────────
copy_to_qube() {
  local qube_nam="${1}"
  local file_src="${2}"
  local file_tgt="${3}"

  cat "${file_src}" | qvm-run --pass-io --no-gui --user root "${qube_nam}" \
    "mkdir --parents '$(dirname "${file_tgt}")' && cat > '${file_tgt}'"
}

# ────────────────────────────────────────────────────────────────────────
#  Clean any previous qubes with the same names (useful for testing)
# ────────────────────────────────────────────────────────────────────────
if [[ -d "/var/lib/qubes/appvms/${qube_app}" ]]; then
  qvm-shutdown --force --wait "${qube_app}"
  qvm-remove   --force "${qube_app}"
fi
if [[ -d "/var/lib/qubes/vm-templates/${qube_tpl}" ]]; then
  qvm-shutdown --force --wait "${qube_tpl}"
  qvm-remove   --force "${qube_tpl}"
fi

# ────────────────────────────────────────────────────────────────────────
#  Create (clone) template qube
# ────────────────────────────────────────────────────────────────────────
qvm-clone "${qube_src}" "${qube_tpl}"
qvm-prefs "${qube_tpl}" netvm none
qvm-prefs "${qube_tpl}" label black

# ────────────────────────────────────────────────────────────────────────
#  Minimal list of packages that are mandatory
# ────────────────────────────────────────────────────────────────────────
xfce_pkg=(
  # Theming & appearance
  arc-theme                    # Core dark GTK theme providing the visual style
  gtk2-engines-murrine         # Required engine for Arc-Dark to render GTK2 apps correctly
  
  # Fonts
  fonts-jetbrains-mono         # Monospaced font for terminal and code readability
  fonts-atkinson-hyperlegible  # High-legibility sans-serif font for UI elements
  fonts-noto-color-emoji       # Color emoji support for modern applications

  # Icons
  papirus-icon-theme           # Primary icon set matching the Arc-Dark aesthetic
  adwaita-icon-theme           # Fallback icons for GTK apps lacking Papirus variants
  hicolor-icon-theme           # Standard fallback for any missing icons
  librsvg2-common              # Enables rendering of scalable vector (SVG) icons

  # Thumbnails (tumbler + ffmpegthumbnailer)
  tumbler                      # Xfce thumbnail service (replaces manual D-Bus setup)
  tumbler-plugins-extra        # Adds support for video, archive, and document thumbnails
  ffmpegthumbnailer            # Backend engine specifically for generating video thumbnails

  # Thunar enhancements
  thunar-archive-plugin        # Enables right-click archive actions (zip/tar) in Thunar
  thunar-media-tags-plugin     # Displays media metadata (ID3 tags, EXIF) in Thunar
  gvfs                         # Virtual filesystem backend required for Thunar network/removable drives

  # D-Bus & policykit
  dbus-x11                     # X11 D-Bus support essential for GUI service communication
  dbus-user-session            # Per-user D-Bus session required for thumbnailers and settings
  polkitd                      # PolicyKit daemon for handling authentication prompts

  # Xfce applications
  thunar                       # Default lightweight file manager for Xfce
  mousepad                     # Lightweight text editor integrated with Xfce
  xfce4-terminal               # Default terminal emulator for Xfce

  # Qubes OS integration
  qubes-core-agent-passwordless-root # Allows Qubes agents to run without sudo password prompts
  qubes-core-agent-thunar            # Enables seamless file sharing between Qubes via Thunar
  #qubes-pdf-converter
  #qubes-img-converter
  #qubes-app-shutdown-idle
)

# Update, upgrade, and install all packages
echo "Installing packages..."
qvm-run --pass-io --no-gui --user root "${qube_tpl}" \
  "DEBIAN_FRONTEND=noninteractive apt-get update --quiet && \
   DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes --quiet && \
   DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet --no-install-recommends ${xfce_pkg[*]}" &> /dev/null

# ────────────────────────────────────────────────────────────────────────
#  Set locale in template qube
# ────────────────────────────────────────────────────────────────────────
cat > "${dom0_dir}/locale.conf" <<'EOF'
LANG='en_US.UTF-8'
LANGUAGE=
LC_CTYPE='en_US.UTF-8'
LC_NUMERIC='en_US.UTF-8'
LC_TIME='en_US.UTF-8'
LC_COLLATE='en_US.UTF-8'
LC_MONETARY='en_US.UTF-8'
LC_MESSAGES='en_US.UTF-8'
LC_PAPER='en_US.UTF-8'
LC_NAME='en_US.UTF-8'
LC_ADDRESS='en_US.UTF-8'
LC_TELEPHONE='en_US.UTF-8'
LC_MEASUREMENT='en_US.UTF-8'
LC_IDENTIFICATION='en_US.UTF-8'
LC_ALL=
EOF

qvm-run --pass-io --no-gui --user root "${qube_tpl}" \
  "sed -i 's/^# en_US.UTF-8/en_US.UTF-8/' /etc/locale.gen"

qvm-run --pass-io --no-gui --user root "${qube_tpl}" \
  "locale-gen > /dev/null"

copy_to_qube "${qube_tpl}" "${dom0_dir}/locale.conf" "/etc/locale.conf"

# ────────────────────────────────────────────────────────────────────────
# Seed 'mimeapps.list' (app qube inherits via /etc/skel)
# ────────────────────────────────────────────────────────────────────────
cat > "${dom0_dir}/mimeapps.list" <<'EOF'
[Default Applications]
inode/directory=thunar.desktop
text/plain=org.xfce.mousepad.desktop
EOF

copy_to_qube "${qube_tpl}" \
  "${dom0_dir}/mimeapps.list" \
  "/etc/skel/.config/mimeapps.list"

# ────────────────────────────────────────────────────────────────────────
#  Copy configuration files into the application qube
# ────────────────────────────────────────────────────────────────────────

# xfce, GTK 2,3 and 4 settings
# System-wide defaults (primary method for GTK settings)

cat > "${dom0_dir}/environment" <<'EOF'
GTK_THEME=Arc-Dark
GTK_ICON_THEME=Papirus-Dark
NO_AT_BRIDGE=1
XDG_CURRENT_DESKTOP=XFCE
EOF

cat > "${dom0_dir}/.gtkrc-2.0" <<'EOF'
include "/usr/share/themes/Arc-Dark/gtk-2.0/gtkrc"
style "user-font"
{
  font_name="Atkinson Hyperlegible 13"
}
widget_class "*" style "user-font"
gtk-font-name=Atkinson Hyperlegible 13
gtk-theme-name=Arc-Dark
gtk-icon-theme-name=Papirus-Dark
EOF

cat > "${dom0_dir}/settings.ini" <<'EOF'
[Settings]
gtk-font-name=Atkinson Hyperlegible 13
gtk-theme-name=Arc-Dark
gtk-icon-theme-name=Papirus-Dark
EOF

# xfce4-terminal configuration file
cat > "${dom0_dir}/terminalrc" <<'EOF'
[Configuration]
FontName=JetBrains Mono 13
ColorBackground=#2f2f34
ColorForeground=#fafafa
EOF

# Create xfconf XML files for Mousepad
# NOTE: There is a bug in Mousepad!
#       https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=794330
cat > "${dom0_dir}/mousepad.xml" <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<channel name="mousepad" version="1.0">
  <property name="view" type="empty">
    <property name="font-name" type="string" value="JetBrains Mono 13"/>
  </property>
</channel>
EOF

# Create xfconf XML files for Thunar
cat > "${dom0_dir}/thunar.xml" <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<channel name="thunar" version="1.0">
  <property name="last-view" type="string" value="ThunarIconView"/>
  <property name="misc-thumbnail-mode" type="int" value="1"/>
</channel>
EOF

echo "Copy files to template qube..."
copy_to_qube "${qube_tpl}" "${dom0_dir}/environment"  "/etc/environment"
copy_to_qube "${qube_tpl}" "${dom0_dir}/.gtkrc-2.0"   "/etc/skel/.gtkrc-2.0"
copy_to_qube "${qube_tpl}" "${dom0_dir}/settings.ini" "/etc/skel/.config/gtk-3.0/settings.ini"
copy_to_qube "${qube_tpl}" "${dom0_dir}/settings.ini" "/etc/skel/.config/gtk-4.0/settings.ini"
copy_to_qube "${qube_tpl}" "${dom0_dir}/terminalrc"   "/etc/skel/.config/xfce4/terminal/terminalrc"
copy_to_qube "${qube_tpl}" "${dom0_dir}/mousepad.xml" "/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/mousepad.xml"
copy_to_qube "${qube_tpl}" "${dom0_dir}/thunar.xml"   "/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/thunar.xml"

# ────────────────────────────────────────────────────────────────────────
#  Create Xfce application qube and make final configuration
# ────────────────────────────────────────────────────────────────────────
qvm-shutdown --force --wait "${qube_tpl}"
qvm-create "${qube_app}" --template "${qube_tpl}" --label red
qvm-shutdown --wait --force "${qube_app}"
qvm-start "${qube_app}"
echo "Enjoy!"

Improvements are welcome! :handshake:

2 Likes

My solution, not too minimal

#!/bin/bash

# Enable Global Dark Theme for Debian minimal
# Run with sudo for system-wide changes or without for user-only
# Reference https://forum.qubes-os.org/t/universal-darkmode-without-gui-tools-files-and-pkgs-only/29926


set -e

# Install required packages
install_packages() {
  echo "Installing required packages..."

  # Install packages
  sudo apt install -y --no-install-recommends \
    qt5-gtk-platformtheme \
    adwaita-qt \
    qt5ct \
    adwaita-qt6 \
    qgnomeplatform-qt5 \
    gnome-themes-extra \
    gnome-themes-extra-data

  echo "Packages installed successfully"
}

# Create user GTK configuration
setup_user_gtk() {
  echo "Setting up user GTK configuration..."

  # Create GTK3 directory and config
  mkdir -p ~/.config/gtk-3.0
  cat > ~/.config/gtk-3.0/settings.ini << 'EOF'
[Settings]
gtk-font-name=Adwaita Sans 12
gtk-theme-name=Adwaita-dark
gtk-icon-theme-name=Adwaita
gtk-decoration-layout=menu:
gtk-application-prefer-dark-theme=1
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintslight
gtk-xft-rgba=rgb
EOF

  # Create GTK4 directory and config
  mkdir -p ~/.config/gtk-4.0
  cp ~/.config/gtk-3.0/settings.ini ~/.config/gtk-4.0/

  # Add AdwStyleManager section for GTK4
  cat >> ~/.config/gtk-4.0/settings.ini << 'EOF'

[AdwStyleManager]
color-scheme=ADW_COLOR_SCHEME_PREFER_DARK
EOF

  echo "User GTK configuration created"
}

# Create system-wide GTK configuration (requires root)
setup_system_gtk() {
    echo "Setting up system-wide GTK configuration..."

    # Create GTK2 config
    mkdir -p /etc/skel
    cat > /etc/skel/.gtkrc-2.0 << 'EOF'
include "/usr/share/themes/Adwaita-dark/gtk-2.0/gtkrc"
style "user-font"
{
  font_name="Adwaita Sans"
}
widget_class "*" style "user-font"
gtk-font-name="Adwaita Sans 12"
gtk-theme-name="Adwaita-dark"
gtk-icon-theme-name="Adwaita"
EOF

    # Create GTK3 and GTK4 configs for new users
    mkdir -p /etc/skel/.config/gtk-3.0
    mkdir -p /etc/skel/.config/gtk-4.0

    cat > /etc/skel/.config/gtk-3.0/settings.ini << 'EOF'
[Settings]
gtk-font-name=Adwaita Sans 12
gtk-theme-name=Adwaita-dark
gtk-icon-theme-name=Adwaita
gtk-decoration-layout=menu:
gtk-application-prefer-dark-theme=1
gtk-xft-antialias=1
gtk-xft-hinting=1
gtk-xft-hintstyle=hintslight
gtk-xft-rgba=rgb
EOF

  cp /etc/skel/.config/gtk-3.0/settings.ini /etc/skel/.config/gtk-4.0/

  cat >> /etc/skel/.config/gtk-4.0/settings.ini << 'EOF'

[AdwStyleManager]
color-scheme=ADW_COLOR_SCHEME_PREFER_DARK
EOF

    echo "System-wide GTK configuration created"
}

# Setup QT configuration
setup_qt_config() {
  echo "Setting up QT configuration..."

  if [ "$EUID" -eq 0 ]; then
    # Create QT configuration directory
    mkdir -p /etc/environment.d

    # Create 62-qt.conf for QT settings
    cat > /etc/environment.d/62-qt.conf << 'EOF'
QT_STYLE_OVERRIDE=Adwaita-dark
QT_QPA_PLATFORMTHEME=gtk2
EOF
  else
    # For user-only setup, add to bashrc
    cat >> ~/.bashrc << 'EOF'

# QT Dark Theme
export QT_STYLE_OVERRIDE=Adwaita-dark
export QT_QPA_PLATFORMTHEME=gtk2
EOF
    source ~/.bashrc
  fi

  echo "QT configuration created"
}

# Setup GTK3 fix for AT-SPI
setup_gtk3_fix() {
  echo "Setting up GTK3 AT-SPI fix..."

  if [ "$EUID" -eq 0 ]; then
    cat > /etc/environment.d/61-gtk3fix.conf << 'EOF'
GTK_THEME=Adwaita-dark
NO_AT_BRIDGE=1
EOF
  else
    cat >> ~/.bashrc << 'EOF'

# GTK3 AT-SPI fix
export GTK_THEME=Adwaita-dark
export NO_AT_BRIDGE=1
EOF
    source ~/.bashrc
  fi

  echo "GTK3 AT-SPI fix applied"
}

# Setup GDK configuration
setup_gdk_config() {
  echo "Setting up GDK configuration..."

  if [ "$EUID" -eq 0 ]; then
    cat > /etc/environment.d/60-gdk.conf << 'EOF'
GDK_CORE_DEVICE_EVENTS=1
EOF
  else
    cat >> ~/.bashrc << 'EOF'

# GDK configuration
export GDK_CORE_DEVICE_EVENTS=1
EOF
    source ~/.bashrc
  fi

  echo "GDK configuration created"
}

# Setup XDG configuration
setup_xdg_config() {
  echo "Setting up XDG configuration..."

  if [ "$EUID" -eq 0 ]; then
    cat > /etc/environment.d/64-xdg.conf << 'EOF'
XDG_CURRENT_DESKTOP=GNOME
EOF
  else
    cat >> ~/.bashrc << 'EOF'

# XDG configuration
export XDG_CURRENT_DESKTOP=GNOME
EOF
    source ~/.bashrc
  fi

  echo "XDG configuration created"
}

# Setup QT5CT configuration
setup_qt5ct_config() {
  echo "Setting up QT5CT configuration..."

  mkdir -p ~/.config/qt5ct
  cat > ~/.config/qt5ct/qt5ct.conf << 'EOF'
[Appearance]
color_scheme_path=/usr/share/qt5ct/colors/darker.conf
custom_palette=false
icon_theme=Adwaita
standard_dialogs=gtk2
style=Adwaita-dark

[Fonts]
fixed=@Variant(\0\0\0@\0\0\0\x12\0A\0\x64\0w\0\x61\0i\0t\0\x61\0 \0S\0\x61\0n\0s@$\0\0\0\0\0\0\xff\xff\xff\xff\x5\x1\0\x32\x10)
general=@Variant(\0\0\0@\0\0\0\x12\0A\0\x64\0w\0\x61\0i\0t\0\x61\0 \0S\0\x61\0n\0s@$\0\0\0\0\0\0\xff\xff\xff\xff\x5\x1\0\x32\x10)

[Interface]
activate_item_on_single_click=2
buttonbox_layout=0
cursor_flash_time=1000
dialog_buttons_have_icons=2
double_click_interval=400
gui_effects=@Invalid()
keyboard_scheme=2
menus_have_icons=true
show_shortcuts_in_context_menus=true
stylesheets=@Invalid()
toolbutton_style=4
underline_shortcut=1
wheel_scroll_lines=3

[SettingsWindow]
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\0\0\0\0\0\0\0\x2\xde\0\0\x2\xa7\0\0\0\0\0\0\0\0\0\0\x2\xde\0\0\x2\xa7\0\0\0\0\x2\0\0\0\a\x80\0\0\0\0\0\0\0\0\0\0\x2\xde\0\0\x2\xa7)
EOF

  echo "QT5CT configuration created"
}

# Main function
main() {
  echo "Starting setup..."

  install_packages
  setup_user_gtk

  # Only setup system GTK if running as root
  if [ "$EUID" -eq 0 ]; then
    setup_system_gtk
  fi

  setup_qt_config
  setup_gtk3_fix
  setup_gdk_config
  setup_xdg_config
  setup_qt5ct_config

  echo "Setup completed!"
}

# Run main function
main "$@"
1 Like