Fresh installation (shell) script: minimal templates, app qubes, default settings

Qubes OS - Fresh install - Minimal templates


:warning: Caution:
Minimal templates are intended for advanced users.

qubes-os.org/doc/templates/minimal/#important

Introduction


:warning: Warning:
This script IS NOT made to be launch as it.
You must configure it to your hardware and preferences.

As an advanced topic, some knowledge is assumed.

e.g. usage of the manual pages.
[user@dom0 ~]$ man man
[user@dom0 ~]$ man qvm-template
[user@dom0 ~]$ grep --help
[user@dom0 ~]$ qvm-clone -h
e.g. these bash concepts and commands.
  • for loop and if then
  • function ( $#, $1, $2, $@, ${@:3} )
  • recursive function ( function that calls itself )
  • sed ( 's/foo/bar/g', '/^foo.*bar/ d' )

Description

A customizable shell script to automate your installation.

This is a recipe, not a tutorial / course.
Take some or all of it. Modify it to your needs.

This script is a one-file format (single .sh file).

Initial setup

qubes-os.org/doc/installation-guide/#initial-setup

Select these checkboxes.
  • Fedora XX
  • Create default system qubes (sys-net, sys-firewall, default DispVM)
  • Use a qube to hold all USB controllers (create a new qube called sys-usb by default)
Do not select these checkboxes.
  • Make sys-firewall and sys-usb disposable
  • Make sys-net disposable
  • Create default application qubes (personal, work, untrusted, vault)

Usage

  • disable hibernate and suspend during the script execution.
    apps menu > system tools > power manager

  • in dom0 terminal.

    • enable unlimited scrollback (to check if an error occured after the installation).
      menu > edit > preferences > general > unlimited scrollback

    • copy the script to dom0, make it executable, then launch it.
      qubes-os.org/doc/how-to-copy-from-dom0/#copying-to-dom0

      :warning: Caution:
      The code you run in dom0 MUST be understood.

      file_name=qubes_fresh_install.sh
      qvm-run --pass-io sys-usb "
          cat /home/user/$file_name" > $HOME/$file_name
      chmod +x $HOME/$file_name
      $HOME/$file_name
      
  • after reboot and working internet.

    :bulb: Keeping the full template can be useful in many situations.

    Remove the default qubes.
    qvm-remove --force \
        default-mgmt-dvm \
        sys-usb \
        sys-firewall \
        sys-net
    
    Remove the full template.
    qvm-remove --force \
       fedora-XX-dvm \
       fedora-XX
    

Configuration


#!/usr/bin/bash

set -eu -o pipefail

fresh_install=true

os_name=fedora
os_release=$(qvm-template list --available \
    | grep -Eo "$os_name.*minimal" \
    | tail -n 1 \
    | grep -Eo '[0-9]+')

install_cmd='dnf -y --setopt=install_weak_deps=false install'

Updates


qubes-os.org/doc/how-to-update/#command-line-interface

if [[ $fresh_install == true ]]; then ## updates
echo 'updating templates ...'
qubes-vm-update --templates

qvm-shutdown --all --wait
qvm-start sys-net sys-firewall
qvm-start sys-usb

echo 'updating dom0 ...'
sudo qubesctl --show-output state.sls update.qubes-dom0
fi ## end: updates

Packages


:warning: Caution:
Before adding a third-party repository, make sure it is a known and trusted source.

System

Use lspci and dnf search to find your wifi driver.
e.g. dnf search wireless or dnf search CARD_MANUFACTURER

wifi_driver='
    iwl7260-firmware
    iwlax2xx-firmware'

passwordless_root='
    qubes-core-agent-passwordless-root'
networking='
    qubes-core-agent-networking'
usb_proxy='
    qubes-usb-proxy'

default_mgmt="
    $passwordless_root
    qubes-mgmt-salt-vm-connector"
sys_network="
    $networking
    qubes-core-agent-network-manager
    network-manager-applet
    $wifi_driver
    NetworkManager-wifi"
sys_firewall="
    $networking
    qubes-core-agent-dom0-updates"
sys_usb="
    $usb_proxy
    qubes-input-proxy-sender"
Common
audio='
    pipewire-qubes'
file_manager='
    qubes-core-agent-thunar'
password_manager='
    keepassxc'
text_editor='
    mousepad'
image_viewer='
    eog'
pdf_viewer='
    qpdfview-qt5'
email_client='
    thunderbird'
office_suite='
    libreoffice-calc
    libreoffice-draw
    libreoffice-writer
    libreoffice-gtk4'

videolan.org/vlc/download-fedora.html
Do not use the $(rpm -E %fedora) command, it will return the dom0 fedora version.

fedora_rpm_free=rpmfusion-free-release-$os_release.noarch.rpm
media_player="\
    https://download1.rpmfusion.org/free/fedora/$fedora_rpm_free \
    && $install_cmd vlc"

brave.com/linux/#fedora-centos-streamrhel
Add --httpproxy 127.0.0.1 --httpport 8082 to the rpm command.
qubes-os.org/doc/how-to-install-software/#updates-proxy

brave_rpm_www=https://brave-browser-rpm-release.s3.brave.com
web_browser="\
    dnf-plugins-core \
    && dnf config-manager \
        --add-repo $brave_rpm_www/brave-browser.repo \
    && rpm --httpproxy 127.0.0.1 --httpport 8082 \
        --import $brave_rpm_www/brave-core.asc \
    && $install_cmd brave-browser"
Work

vscodium.com - Free/Libre Open Source distribution of VSCode.
github.com/VSCodium/vscodium#install-with-package-manager-gnulinux

vscodium_repo='
[gitlab.com_paulcarroty_vscodium_repo]
name=...
baseurl=...
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=...
metadata_expire=1h'
# --version is used to skip $install_cmd
code_editor="--version > /dev/null \
    && echo \"$vscodium_repo\" > /etc/yum.repos.d/vscodium.repo \
    && $install_cmd codium"
dev_tools='
    git
    vim
    zsh'
Misc
printer='
    system-config-printer
    gutenprint-cups'
qt_theme='
    adwaita-qt5'
qubes_converter_tools='
    qubes-pdf-converter
    qubes-img-converter'

Templates


echo 'creating templates ...'
run_cmd ()
run_cmd ()
{
    qvm-run --pass-io --nogui --user $1 $2 "$3"
}

Base

Install the minimal template, clone it to a custom name and update it.

base_tpl=$os_name-$os_release-min
min_tpl=$os_name-$os_release-minimal

qvm-template install $min_tpl
qvm-clone $min_tpl $base_tpl
qvm-remove --force $min_tpl

qvm-prefs $base_tpl memory 512
qvm-prefs $base_tpl maxmem 4096

echo "updating $base_tpl ..."
qubes-vm-update --targets $base_tpl
theme & icons

Copy your favorite theme & icons from dom0 to the base template.

themes_dir=/usr/share/themes/
icons_dir=/usr/share/icons/
qvm-copy-to-vm $base_tpl \
    $themes_dir/Arc-Dark \
    $icons_dir/gnome

qubes_incoming=/home/user/QubesIncoming/
run_cmd root $base_tpl "
    mv $qubes_incoming/dom0/Arc-Dark $themes_dir
    mv $qubes_incoming/dom0/gnome $icons_dir
    rm -r $qubes_incoming"
filetype association

Disable the mimetype for qvm-open-in-dvm (comment the mimetype line).
Disable the default mimeapps.list (rename the file).

system_apps_dir=/usr/share/applications/
open_in_dvm_entry=$system_apps_dir/qvm-open-in-dvm.desktop
default_mimeapps=$system_apps_dir/mimeapps.list
run_cmd root $base_tpl "
    sed -i 's/^MimeType=/#&/' $open_in_dvm_entry
    mv $default_mimeapps $default_mimeapps.bak"

Clone the base template as a clean base for future use.

qvm-shutdown --wait $base_tpl
qvm-clone $base_tpl $base_tpl-bak

Creation

install_packages ()
install_packages ()
{
    for pkg in "${@:2}"
    do
        echo "$1: $install_cmd $pkg"
        run_cmd root $1 "$install_cmd $pkg"
    done
}
install_base_tpl_pkgs ()
install_base_tpl_pkgs ()
{
    install_packages $base_tpl "$@"
    qvm-shutdown --wait $base_tpl
}
create_template ()
create_template ()
{
    qvm-clone $base_tpl $1
    install_packages "$@"
    qvm-shutdown --wait $1
}
system_tpl=$base_tpl-sys
apps_tpl=$base_tpl-apps
print_tpl=$base_tpl-print
vault_tpl=$base_tpl-vault
web_tpl=$base_tpl-web
work_tpl=$base_tpl-work

Adapt the order and creation to your needs.
i.e.
$passwordless_root is in all templates.
$file_manager is not in $system_tpl, but is in all others.

install_base_tpl_pkgs \
    $passwordless_root

create_template $system_tpl \
    $default_mgmt \
    $sys_network \
    $sys_firewall \
    $sys_usb

install_base_tpl_pkgs \
    $file_manager

create_template $vault_tpl \
    $password_manager

install_base_tpl_pkgs \
    $networking \
    $usb_proxy \
    $text_editor \
    $qubes_converter_tools

create_template $web_tpl \
    $audio \
    "$web_browser"

install_base_tpl_pkgs \
    $image_viewer \
    $pdf_viewer \
    $qt_theme

create_template $apps_tpl \
    $audio \
    "$media_player" \
    $office_suite \
    $email_client

create_template $print_tpl \
    $printer

create_template $work_tpl \
    "$code_editor" \
    $dev_tools

Rename the base template backup and set the “default template”.

qvm-remove --force $base_tpl
qvm-clone $base_tpl-bak $base_tpl
qvm-remove --force $base_tpl-bak

qubes-prefs default_template $apps_tpl

Template settings


echo 'setting templates ...'

:information_source: Note:
All these settings, despite belonging to a fresh install script, are not Qubes OS specific.
There are already many resources about all of them across the web.

Settings

gnome mimeapps
set_disable_gnome_mimeapps ()
{
    gnome_mimeapps=/usr/share/applications/gnome-mimeapps.list
    mv $gnome_mimeapps $gnome_mimeapps.bak
}
filetype association
set_default_mimetype ()
{
    echo '
    [Default Applications]
    text/plain=org.xfce.mousepad.desktop
    application/pdf=qpdfview-qt5.desktop' | cut -c 5- \
        >> /etc/skel/.config/mimeapps.list
}
dnf
set_dnf ()
{
    echo 'install_weak_deps=false' >> /etc/dnf/dnf.conf
}
qt theme
set_qt_theme ()
{
    echo 'QT_STYLE_OVERRIDE=adwaita-dark' >> /etc/environment
}
gtk
set_gtk ()
{
    local cfg_dir=/etc/skel/.config/
    local gtk3_dir=$cfg_dir/gtk-3.0/
    local gtk4_dir=$cfg_dir/gtk-4.0/

    mkdir -p $gtk3_dir $gtk4_dir
    echo '
    [Settings]
    gtk-theme-name=Arc-Dark
    gtk-icon-theme-name=gnome
    gtk-decoration-layout=menu:
    gtk-titlebar-right-click=none' | cut -c 5- \
        | tee {$gtk3_dir,$gtk4_dir}/settings.ini > /dev/null

    # darker inactive tabs
    echo '
    .chromium menubar {
        background-color: #252A32
    }

    notebook header {
        background-color: #2F343F
    }' | cut -c 5- | tee {$gtk3_dir,$gtk4_dir}/gtk.css > /dev/null

    dconf update
}
default terminal
set_default_terminal ()
{
    local xfce_dir=/etc/skel/.config/xfce4/

    mkdir -p $xfce_dir
    echo 'TerminalEmulator=xterm' > $xfce_dir/helpers.rc
}
xterm
set_xterm ()
{
    echo '
    xterm*background: black
    xterm*foreground: white
    xterm*faceName: monospace
    xterm*faceSize: 12
    xterm*geometry: 80x40
    xterm*saveLines: 100000
    xterm*selectToClipboard: true' | cut -c 5- \
        | tee -a {/etc/skel/,/home/user/}/.Xresources > /dev/null
}
bash
set_bash ()
{
    echo '
    export PS1="\[\e[1;31m\]$PS1\[\e[m\]"

    alias ls="ls --color=auto -F"
    alias ll="ls -lh"
    alias la="ls -lah"
    alias sudo="sudo "' | cut -c 5- \
        | tee -a {/etc/skel/,/home/user/,/root/}/.bashrc > /dev/null
}

file chooser
set_file_chooser ()
{
    local dconf_local_dir=/etc/dconf/db/local.d/

    mkdir -p $dconf_local_dir
    echo '
    [org/gtk/settings/file-chooser]
    sort-directories-first=true
    window-position=(0, 0)
    window-size=(850, 550)' | cut -c 5- > $dconf_local_dir/file-chooser
    dconf update
}
file manager
set_file_manager ()
{
    local xfce_cfg_dir=/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/

    mkdir -p $xfce_cfg_dir
    echo '<?xml version="1.0" encoding="UTF-8"?>
    <channel name="thunar" version="1.0">
      <property name="last-location-bar"       type="string" value="ThunarLocationButtons"/>
      <property name="last-window-width"       type="int"    value="900"/>
      <property name="last-window-height"      type="int"    value="600"/>
      <property name="misc-show-delete-action" type="bool"   value="true"/>
    </channel>' > $xfce_cfg_dir/thunar.xml
}
password manager
set_password_manager ()
{
    local keepassxc_dir=/etc/skel/.config/keepassxc/

    mkdir -p $keepassxc_dir
    echo '
    [GUI]
    ApplicationTheme=dark' | cut -c 5- > $keepassxc_dir/keepassxc.ini
}
text editor
set_text_editor ()
{
    local dconf_local_dir=/etc/dconf/db/local.d/

    mkdir -p $dconf_local_dir
    echo "
    [org/xfce/mousepad/preferences/view]
    auto-indent=true
    color-scheme='cobalt'
    insert-spaces=true
    match-braces=true
    smart-backspace=true
    tab-width=uint32 4

    [org/xfce/mousepad/preferences/window]
    statusbar-visible=true

    [org/xfce/mousepad/state/window]
    height=uint32 900
    width=uint32 800" | cut -c 5- > $dconf_local_dir/mousepad
    dconf update
}
image viewer
set_image_viewer ()
{
    local dconf_local_dir=/etc/dconf/db/local.d/

    mkdir -p $dconf_local_dir
    echo "
    [org/gnome/eog/plugins]
    active-plugins=['statusbar-date', 'fullscreen']

    [org/gnome/eog/ui]
    image-gallery=true
    sidebar=false
    statusbar=true" | cut -c 5- > $dconf_local_dir/eog
    dconf update
}
pdf viewer
set_pdf_viewer ()
{
    local qpdfview_dir=/etc/skel/.config/qpdfview/

    mkdir -p $qpdfview_dir
    # background color: #383C4A
    # paper color : #999999
    echo '
    [documentView]
    highlightCurrentThumbnail=true
    prefetch=true
    prefetchDistance=2
    scaleMode=1

    [pageItem]
    backgroundColor=@Variant(\0\0\0\x43\x1\xff\xff\x38\x38<<JJ\0\0)
    paperColor=@Variant(\0\0\0\x43\x1\xff\xff\x99\x99\x99\x99\x99\x99\0\0)' \
        | cut -c 5- > $qpdfview_dir/qpdfview.conf
}
media player
set_media_player ()
{
    local vlc_dir=/etc/skel/.config/vlc/

    mkdir -p $vlc_dir
    echo '
    [qt]
    qt-privacy-ask=0
    qt-recentplay=0
    qt-system-tray=0
    qt-video-autorezise=0

    [core]
    metadata-network-access=0' | cut -c 5- > $vlc_dir/vlcrc
}
office suite
set_office_suite ()
{
    local lo_org=org.openoffice
    local lo_dir=/etc/skel/.config/libreoffice/4/user/
    local lo_cfg=$lo_dir/registrymodifications.xcu
    local lo_java_cfg=$lo_dir/config/javasettings_Linux_X86_64.xml

    mkdir -p $lo_dir/config/
    add_key_value ()
    {
        echo '<item oor:path="'$1'">' \
                 '<prop oor:name="'$2'" oor:op="fuse">' \
                     '<value>'$3'</value>' \
                 '</prop>' \
             '</item>' >> $lo_cfg
    }
    enable_ribbon_toolbar ()
    {
        for app in $@
        do
            local tb_mode=$lo_org.Office.UI.ToolbarMode
            local tb_app="/$tb_mode/Applications/$tb_mode:Application['$app']"
            local tabbed_mode="/Modes/$tb_mode:ModeEntry['Tabbed']"
            add_key_value $tb_app Active notebookbar.ui
            add_key_value $tb_app$tabbed_mode HasMenubar false
        done
    }
    set_default_window_size ()
    {
        if [[ $# -ne 0 ]]
        then
            local setup=$lo_org.Setup
            local app=com.sun.star.$1
            local app_cfg="/$setup/Office/Factories/$setup:Factory['$app']"
            local window_opt=ooSetupFactoryWindowAttributes
            local window_size="0,0,$2,$3;5;0,0,$2,$3;"
            add_key_value $app_cfg $window_opt $window_size
            $FUNCNAME ${@:4}
        fi
    }

    echo '<?xml version="1.0" encoding="UTF-8"?>
    <oor:items
        xmlns:oor="http://openoffice.org/2001/registry"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' > $lo_cfg

    local lo_linguistic=/$lo_org.Office.Linguistic
    add_key_value $lo_linguistic/GrammarChecking IsAutoCheck      false
    add_key_value $lo_linguistic/Hyphenation     IsHyphSpecial    false
    add_key_value $lo_linguistic/SpellChecking   IsSpellAuto      false
    add_key_value $lo_linguistic/SpellChecking   IsSpellSpecial   false
    add_key_value $lo_linguistic/SpellChecking   IsSpellUpperCase false

    # disable hardware acceleration
    local lo_canvas=/$lo_org.Office.Canvas
    add_key_value $lo_canvas ForceSafeServiceImpl true

    local lo_help=/$lo_org.Office.Common/Help
    add_key_value $lo_help BuiltInHelpNotInstalledPopUp false

    local lo_misc=/$lo_org.Office.Common/Misc
    add_key_value $lo_misc FirstRun        false
    add_key_value $lo_misc ShowTipOfTheDay false
    add_key_value $lo_misc SymbolStyle     colibre_dark_svg

    local lo_ui=$lo_org.Office.UI
    local lo_color=/$lo_ui/ColorScheme/ColorSchemes/$lo_ui
    local lo_doc_bg="$lo_color:ColorScheme['LibreOffice']/DocColor"
    add_key_value $lo_doc_bg Color 10066329 # light gray 1 (#999999)

    enable_ribbon_toolbar Calc Draw Writer

    set_default_window_size \
        frame.StartModule          1400 900 \
        sheet.SpreadsheetDocument  1400 900 \
        drawing.DrawingDocument    1400 900 \
        text.TextDocument          1400 900

    local lo_product=/$lo_org.Setup/Product
    add_key_value $lo_product ooSetupLastVersion       42.0
    add_key_value $lo_product LastTimeDonateShown      4200000000
    add_key_value $lo_product LastTimeGetInvolvedShown 4200000000

    echo '</oor:items>' >> $lo_cfg


    # disable java
    echo '<?xml version="1.0" encoding="UTF-8"?>
    <!--This is a generated file. Do not alter this file!-->
    <java
        xmlns="http://openoffice.org/2004/java/framework/1.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <enabled xsi:nil="false">false</enabled>
    <javaInfo xsi:nil="false" vendorUpdate="" autoSelect="false">
    </javaInfo>
    </java>' > $lo_java_cfg
}
email client
set_email_client ()
{
    local default_prefs_dir=/usr/lib64/thunderbird/defaults/pref/
    local prefs_cfg=$default_prefs_dir/user.js

    echo '
    /*** menu > edit > settings ***/

    /* # General */
    pref("mailnews.start_page.enabled", false);

    /* # Language & Appearance */
    /* ## font & colors */
    pref("browser.display.use_system_colors", true);
    pref("browser.display.document_color_use", 2); // override: always
    /* ## plain text messages */
    pref("mail.display_glyph", false); // emoticons as graphics

    /* # Incoming Mails */
    pref("mail.biff.alert.show_preview", false);
    pref("mail.biff.play_sound", false);

    /* # Files & Attachments */
    pref("pdfjs.disabled", true);
    pref("browser.download.useDownloadDir", true);
    pref("browser.download.dir", "/home/user");
    pref("browser.download.downloadDir", "/home/user");

    /* # Reading & Display */
    pref("mail.showCondensedAddresses", false);

    /* # Network & Disk Space */
    /* ## offline */
    pref("offline.download.download_messages", 2); // disable

    /* # Composition */
    /* ## spelling */
    pref("mail.SpellCheckBeforeSend", false);
    pref("mail.spellcheck.inline", false); // when typing
    /* ## html style */
    pref("msgcompose.default_colors", true);
    /* ## sending format */
    pref("mail.default_send_format", 1); // only plain text

    /* # Attachments */
    pref("mail.compose.attachment_reminder", false);
    pref("mail.compose.big_attachments.notify", false);

    /* # Privacy */
    /* ## mail content */
    pref("mailnews.message_display.disable_remote_image", true);
    /* ## web content */
    pref("network.cookie.cookieBehavior", 2); // disable
    pref("places.history.enabled", false);

    /* # Thunderbird Data Collection */
    pref("datareporting.healthreport.uploadEnabled", false);

    /* # Security */
    pref("mail.phishing.detection.enabled", false);

    /* # Chat */
    pref("messenger.startup.action", 0); // offline


    /*** menu > view ***/

    /* menu > view > message body as > plain text */
    pref("mailnews.display.disallow_mime_handlers", 1);
    pref("mailnews.display.html_as", 1);
    pref("mailnews.display.prefer_plaintext", true);

    /* menu > view > headers > all */
    pref("mail.show_headers", 2);


    /*** config editor ***/

    pref("network.protocol-handler.external-default", false) # disable links

    pref("mail.chat.enabled", false);
    pref("geo.enabled", false);
    pref("browser.region.update.enabled", false);

    pref("toolkit.telemetry.unified", false);
    pref("toolkit.telemetry.server", "");
    pref("toolkit.telemetry.newProfilePing.enabled", false);
    pref("toolkit.telemetry.updatePing.enabled", false);
    pref("toolkit.telemetry.shutdownPingSender.enabled", false);
    pref("toolkit.telemetry.bhrPing.enabled", false);
    pref("toolkit.telemetry.firstShutdownPing.enabled", false);
    pref("toolkit.coverage.opt-out", true);

    pref("breakpad.reportURL", ""); // crash report
    pref("captivedetect.canonicalURL", ""); // captive portal
    pref("network.connectivity-service.enabled", false); // network check
    pref("mail.rights.version", 1); // disable "know rights" notif' \
        | cut -c 5- > $prefs_cfg
}
web browser
set_web_browser ()
{
    local cfg_dir=/etc/skel/.config/
    local brave_dir=$cfg_dir/BraveSoftware/Brave-Browser/
    local brave_state_cfg="$brave_dir/Local State"
    local brave_prefs_cfg=$brave_dir/Default/Preferences
    local brave_entry=brave-browser.desktop

    mkdir -p $brave_dir/Default/

    # remove welcome splash screen on first launch
    touch "$brave_dir/First Run"

    echo "
    [Default Applications]
    text/html=$brave_entry
    x-scheme-handler/http=$brave_entry
    x-scheme-handler/https=$brave_entry
    x-scheme-handler/about=$brave_entry
    x-scheme-handler/unknown=$brave_entry" | cut -c 5- \
        >> $cfg_dir/mimeapps.list

    add_value_to_key ()
    {
        if [[ $1 == root ]]
        then
            echo "$2" >> "$3"
        else
            sed -i "/$1/ r"<(echo "$2") "$3"
        fi
    }
    state_add_value_to_key ()
    {
        add_value_to_key "$1" "$2" "$brave_state_cfg"
    }
    prefs_add_value_to_key ()
    {
        add_value_to_key "$1" "$2" $brave_prefs_cfg
    }

    echo '{' | tee "$brave_state_cfg" $brave_prefs_cfg > /dev/null

    ## brave://flags
    #------
    state_add_value_to_key 'root' '
        "browser": {
            "enabled_labs_experiments": [
                "enable-force-dark@1", # web content night mode
                "translate@2" # disable
            ]
        },'

    ## Get started
    #------
    prefs_add_value_to_key 'root' '
        "session": {
            "restore_on_startup": 5 # open new tab page
        },'
    ### New Tab Page
    prefs_add_value_to_key 'root' '
        "brave": {
            "new_tab_page": {
                "hide_all_widgets": true, # cards
                "show_background_image": true,
                "show_branded_background_image": false,
                "show_clock": false,
                "show_stats": false,
                "show_together": false, # news
                "shows_options": 0 # new tab page: dashboard
            }
        },
        "ntp": {
            "shortcust_visible": false # top sites
        },'

    ## Appearance
    #------
    state_add_value_to_key 'root' '
        "brave": {
            "dark_mode": 1 # enable
        },'
    prefs_add_value_to_key 'root' '
        "extensions": {
            "theme": {
                "system_theme": 1 # GTK
            }
        },
        "browser": {
            "custom_chrome_frame": false # use system title bar (enable)
        },'
    ### Toolbar
    prefs_add_value_to_key '"browser": {' '
            "show_home_button": false,'
    prefs_add_value_to_key 'root' '
        "bookmark_bar": {
            "show_on_all_tabs": true
        },'
    prefs_add_value_to_key '"brave": {' '
            "show_bookmarks_button": true,
            "today": {
                "should_show_toolbar_button": false # brave news button
            },
            "wallet": {
                "show_wallet_icon_on_toolbar": false
            },
            "show_side_panel_button": true,
            "location_bar_is_wide": false,
            "autocomplete_enabled": true,
            "omnibox": {
                "prevent_url_elisions": false, # show full URL
                "bookmark_suggestions_enabled": true,
                "history_suggestions_enabled": true
            },
            "top_site_suggestions_enabled": true,
            "ai_chat": {
                "autocomplete_provider_enabled": true
            },'
    ### Tabs
    prefs_add_value_to_key '"brave": {' '
            "tabs": {
                "mute_indicator_not_clickable": false,
                "vertical_tabs_enabled": false,
                "hover_mode": 1 # card
            },
            "tabs_search_show": true,'
    ### Sidebar
    prefs_add_value_to_key '"brave": {' '
            "sidebar": {
                "hidden_built_in_items": [
                    1, # Brave Talk
                    2, # Brave Wallet
                    3, # Bookmarks
                    4  # Reading List
                ],
                "side_panel_width": 500,
                "sidebar_show_option": 3 # never
            },'
    prefs_add_value_to_key 'root' '
        "side_panel": {
            "is_right_aligned": true
        },'
    ### Content
    prefs_add_value_to_key '"brave": {' '
            "speedreader": {
                "enabled": false
            },
            "mru_cycling_enabled": false, # cycle most recently tabs'

    ## Shields
    #------
    prefs_add_value_to_key '"brave": {' '
            "shields": {
                "stats_badge_visible": false # number on icon
            },
            "de_amp": {
                "enabled": true # auto-redirect AMP
            },
            "debounce": {
                "enabled": true # auto redirect tracking urls
            },
            "reduce_language": true, # prevent fingerprinting
            "no_script_default": false,'
    prefs_add_value_to_key 'root' '
        "profile": {
            "content_settings": { # agressive / strict
                "exceptions": {
                    "fingerprintingV2": {
                        "*,*": {
                            "setting": 2
                        }
                    },
                    "cosmeticFiltering": { # tackers & ads
                        "*,*": {
                            "setting": 2
                        },
                        "*,https://firstparty": {
                            "setting": 2
                        }
                    },
                    "shieldsAds": { # tackers & ads
                        "*,*": {
                            "setting": 2
                        }
                    },
                    "trackers": { # tackers & ads
                        "*,*": {
                            "setting": 2
                        }
                    }
                }
            },
            "cookie_controls_mode": 1, # block cross-site
            "default_content_setting_values": {
                "brave_remember_1p_storage": 2, # forget when close site
                "httpsUpgrades": 2 # strict
            }
        },'
    ### Content Filtering
    state_add_value_to_key '"brave": {' '
            "ad_block": {
                "cookie_list_opt_in_shown": true,
                "regional_filters": {
                    "319A754E-065A-465F-B09D-3F2C7BF1E67B": {
                        "enabled": true # ublock annoyances list
                    },
                    "67E792D4-AE03-4D1A-9EDE-80E01C81F9B8": {
                        "enabled": true # fanboy annoyances list
                    }
                }
            },'
    ### Social media blocking
    prefs_add_value_to_key '"brave": {' '
            "google_login_default": false,
            "fb_embed_default": false,
            "twitter_embed_default": false,
            "linkedin_embed_default": false,'

    ## Privacy and security
    #------
    prefs_add_value_to_key 'root' '
        "webrtc": {
            "ip_handling_policy": "disable_non_proxied_udp"
        },'
    prefs_add_value_to_key '"brave": {' '
            "gcm": { # google for push messaging
                "channel_status": false
            },'
    state_add_value_to_key '"brave": {' '
            "p3a": {
                "enabled": false # product analytics
            },
            "stats": {
                "reporting_enabled": false # daily ping
            },'
    state_add_value_to_key 'root' '
        "user_experience_metrics": {
            "reporting_enabled": false # diagnostic reports
        },'
    ### Clear Browsing data
    prefs_add_value_to_key '"browser": {' '
            "clear_data": {
                "browsing_history_on_exit": true,
                "cache_on_exit": true,
                "cookies_on_exit": true,
                "download_history_on_exit": true,
                "form_data_on_exit": true,
                "hosted_apps_data_on_exit": true,
                "passwords_on_exit": true,
                "site_settings_on_exit": false
            },'
    ### Cookies and other site data
    prefs_add_value_to_key '"default_content_setting_values": {' '
                "cookies": 4, # clear cookies/site data, block third-party'
    prefs_add_value_to_key 'root' '
        "enable_do_not_track": false,'
    ### Security
    prefs_add_value_to_key 'root' '
        "safebrowsing": {
            "enabled": false
        },
        "https_only_mode_enabled": true,'
    state_add_value_to_key 'root' '
        "dns_over_https": {
            "mode": "secure",
            "templates": "https://dns.adguard-dns.com/dns-query"
        },'
    ### Site and Shields Settings
    prefs_add_value_to_key '"default_content_setting_values": {' '
                "ar": 2,
                "automatic_downloads": 1,
                "autoplay": 1,
                "brave_ethereum": 2,
                "brave_google_sign_in": 2,
                "brave_solana": 2,
                "clipboard": 2,
                "file_system_write_guard": 2,
                "geolocation": 2,
                "hid_guard": 2,
                "images": 1,
                "local_fonts": 2,
                "media_stream_camera": 2,
                "media_stream_mic": 2,
                "midi_sysex": 2,
                "notifications": 2,
                "payment_handler": 2,
                "popups": 2,
                "sensors": 2,
                "serial_guard": 2,
                "sound": 1,
                "usb_guard": 2,
                "vr": 2,
                "window_placement": 2,'
    prefs_add_value_to_key 'root' '
        "custom_handlers": {
            "enabled": false # protocol handlers
        },
        "plugins": {
            "always_open_pdf_externally": true # download pdf
        },
        "webkit": {
            "webprefs": {
                "encrypted_media_enabled": false # protected content
            }
        },'
    ### Tor windows
    state_add_value_to_key 'root' '
        "tor": {
            "tor_disabled": true
        },'

    ## Brave Rewards
    #------
    prefs_add_value_to_key '"brave": {' '
            "rewards": {
                "inline_tip_buttons_enabled": false,
                "show_brave_rewards_button_in_location_bar": false
            },'

    ## Web3
    #------
    ### Wallet
    prefs_add_value_to_key '"wallet": {' '
                "default_solana_wallet": 1, # no fallback
                "default_wallet2": 1, # eth: no fallback
                "nft_discovery_enabled": false,
                "auto_pin_enabled": false,'
    ### IPFS
    prefs_add_value_to_key '"brave": {' '
            "ipfs": {
                "resolve_method": 3 # disabled
            },'
    ### Web3 domains
    state_add_value_to_key '"brave": {' '
            "ens": {
                "resolve_method": 1 # disabled
            },
            "sns": {
                "resolve_method": 1 # disabled
            },
            "unstoppable_domains": {
                "resolve_method": 1 # disabled
            },'

    ## Leo
    #------
    prefs_add_value_to_key '"sidebar": {' '
                "sidebar_items": [
                    {
                        "built_in_item_type": 7 # show Leo icon
                    }
                ],'
    prefs_add_value_to_key '"ai_chat": {' '
                "auto_generate_questions": false, # suggested prompts
                "default_model_key": "chat-default", # llama2 13b
                "user_dismissed_premium_prompt": true,'

    ## Search engine
    #------
    prefs_add_value_to_key 'root' '
        "default_search_provider": {
            "synced_guid": "485bf7d3-0215-45af-87dc-538868000510"
        },
        "default_search_provider_data": {
            "template_url_data": {
                "keyword": ":sp",
                "prepopulate_id": 510,
                "short_name": "Startpage",
                "synced_guid": "485bf7d3-0215-45af-87dc-538868000510",
                "url": ""
            }
        },
        "search": {
            "suggest_enabled": false # improve search
        },'
    prefs_add_value_to_key '"brave": {' '
            "other_search_engines_enabled": false,
            "web_discovery_enabled": false,'

    ## Extensions
    #------
    prefs_add_value_to_key '"brave": {' '
            "hangouts_enabled": false,
            "webtorrent_enabled": false,'
    prefs_add_value_to_key 'root' '
        "signin": {
            "allowed": false # google login
        },
        "media_router": {
            "enable_media_router": false
        },'
    state_add_value_to_key '"brave": {' '
            "widevine_opted_in": false,'

    ## Autofill and passwords
    #------
    prefs_add_value_to_key 'root' '
        "autofill": {
            "credit_card_enabled": false, # save payment
            "profile_enabled": false # addresses
        },
        "credentials_enable_autosignin": false,
        "credentials_enable_service": false, # save password
        "payments": {
            "can_make_payment_enabled": false # allow sites to check
        },'
    prefs_add_value_to_key '"brave": {' '
            "autofill_private_windows": false,'

    ## Languages
    #------
    prefs_add_value_to_key 'root' '
        "translate": {
            "enabled": false
        },'
    prefs_add_value_to_key '"browser": {' '
            "enable_spellchecking": false,'

    ## Downloads
    #------
    prefs_add_value_to_key 'root' '
        "download": {
            "prompt_for_download": false
        },
        "download_bubble": {
            "partial_view_enabled": true # show when done
        }'

    ## Help tips
    #------
    prefs_add_value_to_key '"brave": {' '
            "wayback_machine_enabled": false,
            "enable_window_closing_confirm": true,'

    ## System
    #------
    state_add_value_to_key 'root' '
        "background_mode": {
            "enabled": false
        },
        "hardware_acceleration_mode": {
            "enabled": false
        },'
    prefs_add_value_to_key '"brave": {' '
            "enable_closing_last_tab": true,'
    state_add_value_to_key 'root' '
        "performance_tuning": {
            "high_efficiency_mode": {
                "enabled": false # memory saver
            }
        }'

    ## Misc
    #------
    prefs_add_value_to_key '"browser": {' '
            "has_seen_welcome_page": true,'
    state_add_value_to_key '"brave": {' '
            "dont_ask_for_crash_reporting": true,'
    prefs_add_value_to_key '"shields": {' '
                "advanced_view_enabled": true,'

    echo '}' | tee -a "$brave_state_cfg" $brave_prefs_cfg > /dev/null
    sed -i 's/ #.*//' "$brave_state_cfg" $brave_prefs_cfg
}
web browser cache (tmpfs)
set_web_browser_cache_tmpfs ()
{
    local mount_opt=rw,nosuid,nodev,noexec,nodiratime,size=128M
    echo "tmpfs   /home/user/.cache/BraveSoftware   tmpfs   $mount_opt   0 0" \
        >> /etc/fstab
}

code editor
set_code_editor ()
{
    local codium_dir=/etc/skel/.config/VSCodium/User/

    mkdir -p $codium_dir
    echo '
    {
    }' | cut -c 5- > $codium_dir/settings.json
}
git
set_git ()
{
    echo '
    [user]
        name   "John Doe"
        email  johndoe@example.com
    [core]
        editor vim' | cut -c 5- > /etc/skel/.gitconfig
}
vim
set_vim ()
{
    echo '
    source $VIMRUNTIME/defaults.vim
    set expandtab
    set smarttab
    set shiftwidth=4
    set tabstop=4' | cut -c 5- > /etc/skel/.vimrc
}
zsh
set_zsh ()
{
    sed -i 's/bash/zsh/' /etc/passwd
    echo '
    git_branch ()
    {
        git branch 2> /dev/null \
            | sed -e "/^[^*]/ d" \
                  -Ee "s/* (.*)/(\1)/"
    }

    setopt PROMPT_SUBST
    autoload colors
    colors
    export PS1="%B%{$fg[black]%}$(git_branch)%{$fg[red]%}$PS1%b%{$reset_color%}"

    autoload -U compinit
    compinit
    zstyle ":completion:*" matcher-list "m:{a-z}={A-Z}"

    alias ls="ls --color=auto -F"
    alias ll="ls -lh"
    alias la="ls -lah"
    alias sudo="sudo "' | cut -c 5- > /etc/skel/.zshrc
}

Customizations

handle_custom_settings ()
handle_custom_settings ()
{
    for qube in $2
    do
        echo "    $qube ..."
        run_cmd $1 $qube "
            $(declare -f ${@:3})
            $(echo ${@:3} | sed 's/ set_/;&/g')"
        qvm-shutdown --wait $qube
    done
}
custom_settings ()
custom_settings ()
{
    handle_custom_settings root "$@"
}
custom_settings_user ()
custom_settings_user ()
{
    handle_custom_settings user "$@"
}

set_common
set_common='
    set_dnf
    set_gtk
    set_default_terminal
    set_xterm
    set_bash'
set_file_management
set_file_management='
    set_file_chooser
    set_file_manager'
set_text_img_pdf
set_text_img_pdf='
    set_text_editor
    set_image_viewer
    set_pdf_viewer'
set_dev_tools
set_dev_tools='
    set_git
    set_vim
    set_zsh'

Set the settings in their corresponding templates.

custom_settings "$base_tpl $system_tpl" \
    $set_common

custom_settings $apps_tpl \
    $set_common \
    $set_file_management \
    $set_text_img_pdf \
    set_disable_gnome_mimeapps \
    set_qt_theme \
    set_media_player \
    set_office_suite \
    set_default_mimetype \
    set_email_client

custom_settings $print_tpl \
    $set_common \
    $set_file_management \
    $set_text_img_pdf \
    set_disable_gnome_mimeapps \
    set_qt_theme

custom_settings $vault_tpl \
    $set_common \
    $set_file_management \
    set_password_manager

custom_settings $web_tpl \
    $set_common \
    $set_file_management \
    set_text_editor \
    set_web_browser \
    set_web_browser_cache_tmpfs

custom_settings $work_tpl \
    $set_common \
    $set_file_management \
    $set_text_img_pdf \
    set_disable_gnome_mimeapps \
    set_qt_theme \
    set_code_editor \
    $set_dev_tools

Template management


qubes-os.org/doc/templates/#switching

:information_source: Note:
Do not enable for a fresh installation.
Only for new template release (e.g. fedora XX → fedora XX + 1).

if [[ $fresh_install != true ]]; then ## new tpl release
echo 'switching old templates with new ones ...'

Switch templates

qvm-shutdown --all --wait
Change the templates.

If you only have a USB keyboard/mouse, you may want to switch sys-dvm
after confirming that the new sys-usb-dvm works as expected.

change_template ()
{
    if [[ $# -ne 0 ]]
    then
        qvm-prefs $1 template $2
        $FUNCNAME ${@:3}
    fi
}
change_template $(qvm-ls --field class,name,template \
    | sed -e '/^AppVM/ !d' \
          -e "/$os_name/ !d" \
          -e 's/^AppVM//' \
          -Ee "s/$os_name-[0-9]+/$os_name-$os_release/")
Remove the old templates.

Keep the old system template until confirming working internet.

remove_old_template ()
{
    if [[ $# -ne 0 ]]
    then
        qvm-remove --force $1
        $FUNCNAME ${@:2}
    fi
}
remove_old_template $(qvm-ls --field class,name \
    | sed -e '/^TemplateVM/ !d' \
          -e "/$os_name/ !d" \
          -e "/$os_name-$os_release/ d" \
          -e '/min-sys/ d' \
          -e 's/^TemplateVM//')
qvm-start \
    sys-usb-dvm \
    sys-net-dvm \
    sys-firewall-dvm

Update settings

Update the settings if / where needed.

set_new_web_browser_settings
set_new_web_browser_settings ()
{
    local brave_dir=.config/BraveSoftware/Brave-Browser/
    local brave_state_cfg="$brave_dir/Local State"
    local brave_prefs_cfg=$brave_dir/Default/Preferences

    cp /etc/skel/$brave_state_cfg /home/user/$brave_state_cfg
    cp /etc/skel/$brave_prefs_cfg /home/user/$brave_prefs_cfg
}
custom_settings_user web-dvm \
    set_new_web_browser_settings

fi ## end: new tpl release

Disposable templates


qubes-os.org/doc/disposable-customization/

if [[ $fresh_install == true ]]; then ## fresh install
echo 'creating disposable templates ...'
create_dvm_template ()
create_dvm_template ()
{
    if [[ $# -ne 0 ]]
    then
        echo "    $1 ..."
        qvm-create $1 --template $2 --label $3 \
            --property maxmem=$4 \
            --property memory=$5 \
            --property netvm='' \
            --property template_for_dispvms=true \
            --property vcpus=1
        qvm-features $1 appmenus-dispvm $6
        $FUNCNAME "${@:7}"
    fi
}
mgmt_dvm=mgmt-dvm
sys_dvm=sys-dvm
apps_dvm=apps-dvm
print_dvm=print-dvm
web_dvm=web-dvm
create_dvm_template \
    $mgmt_dvm  $system_tpl black 4096 512 '' \
    $sys_dvm   $system_tpl red   4096 512 '' \
    $apps_dvm  $apps_tpl   red   2048 512 1  \
    $print_dvm $print_tpl  red   4096 512 '' \
    $web_dvm   $web_tpl    red   2048 512 1
Set the "default management" and "default disposable" qubes.
qvm-features $mgmt_dvm internal 1
qubes-prefs management_dispvm $mgmt_dvm

qubes-prefs default_dispvm $apps_dvm

Named disposables


qubes-os.org/doc/disposable-customization/#using-named-disposables-for-sys-

echo 'creating named disposables ...'
create_named_dvm ()
create_named_dvm ()
{
    if [[ $# -ne 0 ]]
    then
        echo "    $1 ..."
        qvm-create $1 --template $2 --class DispVM --label $3 \
            --property autostart=$4 \
            --property maxmem=$5 \
            --property memory=$6 \
            --property netvm=$7 \
            --property provides_network=$8 \
            --property vcpus=1
        qvm-features $1 appmenus-dispvm ''
        [[ $9 == hvm ]] && qvm-prefs $1 virt_mode hvm
        $FUNCNAME "${@:10}"
    fi
}
net_dvm=sys-net-dvm
fw_dvm=sys-firewall-dvm
usb_dvm=sys-usb-dvm
printer_dvm=printer-dvm
create_named_dvm \
    $net_dvm      $sys_dvm   red    'true'  0    768 ''       'true'  hvm \
    $fw_dvm       $sys_dvm   green  'true'  1280 768 $net_dvm 'true'  ''  \
    $usb_dvm      $sys_dvm   red    'true'  0    512 ''       'false' hvm \
    banking-dvm   $web_dvm   gray   'false' 2048 512 $fw_dvm  'false' ''  \
    mail-web-dvm  $web_dvm   purple 'false' 2048 512 $fw_dvm  'false' ''  \
    $printer_dvm  $print_dvm red    'false' 2048 512 $fw_dvm  'false' ''
Set the "default clock" and "default update" qubes.
qubes-prefs clockvm  $net_dvm
qubes-prefs updatevm $fw_dvm
Set the "default net" qube.

It cannot be changed to a domain that is not running.

qvm-start $net_dvm $fw_dvm
qubes-prefs default_netvm $fw_dvm
qvm-shutdown --wait $fw_dvm $net_dvm

Set the “qubes.UpdatesProxy” policy to use $net_dvm.
qubes-os.org/doc/rpc-policy/

echo "qubes.UpdatesProxy *  @type:TemplateVM  @default  allow target=$net_dvm" \
    | sudo tee -a /etc/qubes/policy.d/50-config-updates.policy > /dev/null
Disable the non-disposable system qubes.
qvm-prefs sys-usb      autostart false
qvm-prefs sys-net      autostart false
qvm-prefs sys-firewall autostart false

Regular app qubes


qubes-os.org/doc/how-to-organize-your-qubes/#conclusion

echo 'creating regular appvms ...'
create_regular_appvm ()
create_regular_appvm ()
{
    if [[ $# -ne 0 ]]
    then
        echo "    $1 ..."
        qvm-create $1 --template $2 --label $3 \
            --property autostart=$4 \
            --property maxmem=$5 \
            --property memory=$6 \
            --property netvm=$7 \
            --property vcpus=$8
        qvm-features $1 appmenus-dispvm ''
        $FUNCNAME "${@:9}"
    fi
}
create_regular_appvm \
    daily-web $web_tpl   orange 'true'  4096 512 $fw_dvm 2 \
    mail      $apps_tpl  purple 'false' 2048 512 $fw_dvm 1 \
    media     $apps_tpl  yellow 'false' 4096 512 ''      2 \
    personal  $apps_tpl  yellow 'false' 4096 512 ''      2 \
    vault     $vault_tpl black  'false' 2048 512 ''      1 \
    work      $work_tpl  blue   'false' 4096 512 ''      2 \
    work-web  $web_tpl   blue   'false' 2048 512 $fw_dvm 1
Set the fullscreen mode.

qubes-os.org/doc/how-to-enter-fullscreen-mode/

qvm-features daily-web gui-allow-fullscreen 1
qvm-features media     gui-allow-fullscreen 1
Resize the private storage.

qubes-os.org/doc/resize-disk-image/

qvm-volume resize daily-web:private 10737418240 # 10GiB (2^30 * 10)

Network


dev.qubes-os.org/projects/core-admin-client/en/latest/manpages/qvm-firewall.html

Default Firewall rules:

Allow all outgoing connections
qvm-firewall [VMNAME] reset
Limit outgoing connections to ...
qvm-firewall [VMNAME] reset
qvm-firewall [VMNAME] del accept
qvm-firewall [VMNAME] add accept specialtarget=dns 
qvm-firewall [VMNAME] add accept proto=icmp
qvm-firewall [VMNAME] add drop
echo 'setting network ...'

qvm-prefs $web_dvm netvm $fw_dvm

Set the printer qube to your printer IP only.

qvm-firewall $printer_dvm del accept
qvm-firewall $printer_dvm add accept dsthost=192.168.1.42
qvm-firewall $printer_dvm add accept specialtarget=dns 
qvm-firewall $printer_dvm add accept proto=icmp
qvm-firewall $printer_dvm add drop

USB devices


echo 'setting usb devices ...'

Automatically accept USB mice (not recommended).
qubes-os.org/doc/usb-qubes/#usb-mice

echo "
qubes.InputMouse *  $usb_dvm  @adminvm  allow" \
    | sudo tee -a /etc/qubes/policy.d/50-config-input.policy > /dev/null

Setup for USB keyboards depends on your hardware.
qubes-os.org/doc/usb-qubes/#manual-setup-for-usb-keyboards

e.g. only 1 usb controller, only usb keyboard, FDE (LUKS).
echo "
qubes.InputKeyboard *  $usb_dvm  @adminvm  allow" \
    | sudo tee -a /etc/qubes/policy.d/50-config-input.policy > /dev/null

:warning: Caution:
Hiding your USB controllers from dom0 could lock you out of your system.

# rd.qubes.hide_all_usb
# rd.qubes.dom0_usb=XX:XX.X
echo 'GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX usbcore.authorized_default=0"' \
    | sudo tee -a /etc/default/grub > /dev/null

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

PCI devices


qubes-os.org/doc/how-to-use-pci-devices/#qvm-pci-usage

echo 'setting pci devices ...'
get_pci_id ()
get_pci_id ()
{
    qvm-pci | grep "$1" | cut -d ' ' -f 1
}
pci_attach ()
pci_attach ()
{
    for device in $2
    do
        qvm-pci attach $1 $device ${@:3}
    done
}
ethernet_devid=$(get_pci_id 'Ethernet')
wifi_devid=$(get_pci_id 'Network')
usb_devid=$(get_pci_id 'USB')

Attach all pci devices (ethernet, wireless, usb controllers) to the sys-* qubes.

pci_attach $net_dvm "$ethernet_devid" --persistent
pci_attach $net_dvm "$wifi_devid" --persistent
pci_attach $usb_dvm "$usb_devid" --persistent --option no-strict-reset=True

App qube settings


echo 'setting app qubes ...'
Desktop files entries
xterm_entry='
    xterm.desktop'
file_manager_entry='
    thunar.desktop'
text_editor_entry='
    org.xfce.mousepad.desktop'
image_viewer_entry='
    org.gnome.eog.desktop'
pdf_viewer_entry='
    qpdfview-qt5.desktop'
password_manager_entry='
    org.keepassxc.KeePassXC.desktop'
office_suite_startcenter_entry='
    libreoffice-startcenter.desktop'
office_suite_calc_entry='
    libreoffice-calc.desktop'
office_suite_draw_entry='
    libreoffice-draw.desktop'
office_suite_writer_entry='
    libreoffice-writer.desktop'
email_client_entry='
    mozilla-thunderbird.desktop'
media_player_entry='
    vlc.desktop'
web_browser_entry='
    brave-browser.desktop'
printer_entry='
    system-config-printer.desktop'
code_editor_entry='
    codium.desktop'

Settings

filetype association
set_disable_mimetype ()
{
    local user_apps_dir=/home/user/.local/share/applications/

    mkdir -p $user_apps_dir
    for file in $@
    do
        touch $user_apps_dir/$file
    done
}
file persistent (bind-dirs)

qubes-os.org/doc/bind-dirs/

set_file_persistent ()
{
    local qubes_bind_dirs=/rw/config/qubes-bind-dirs.d/

    mkdir -p $qubes_bind_dirs
    for file in $@
    do
        echo "binds+=( '$file' )" >> $qubes_bind_dirs/50_user.conf
    done
}
wifi connection

Automatically connect the wifi connection.
Disposable sys-net: Automatically connect wifi (config file or RPC service)

set_wifi_connection ()
{
    local wifi_dir=/rw/config/NM-system-connections/
    local wifi_cfg=$wifi_dir/home.nmconnection

    mkdir -p $wifi_dir
    echo '
    [wifi]
    ssid=MY_SSID_NAME

    [wifi-security]
    key-mgmt=wpa-psk
    psk=MY_PASSWORD' | cut -c 5- > $wifi_cfg
    chmod 600 $wifi_cfg
}
printer (not Qubes OS specific)

www.cups.org/doc/admin.html

set_printer ()
{
    local driver_model=$(lpinfo -m \
        | grep 'MY_PRINTER_NAME' \
        | grep simple \
        | cut -d ' ' -f 1)

    lpadmin -p 'MY_CUSTOM_PRINTER_NAME' -E \
        -v lpd://192.168.1.42/PASSTHRU \
        -m $driver_model \
        -o printer-error-policy=retry-current-job \
        -o printer-is-shared=false \
        -o Resolution=301x300dpi \
        -o ColorModel=Gray \
        -o print-quality-default=3
}

Customizations

text_img_pdf_entries
text_img_pdf_entries="
    $text_editor_entry
    $image_viewer_entry
    $pdf_viewer_entry"
office_suite_entries
office_suite_entries="
    $office_suite_startcenter_entry
    $office_suite_calc_entry
    $office_suite_draw_entry
    $office_suite_writer_entry"
custom_settings $sys_dvm \
    set_wifi_connection

custom_settings_user mail \
    set_disable_mimetype \
        $text_img_pdf_entries \
        $office_suite_entries \
        $media_player_entry

custom_settings_user media \
    set_disable_mimetype \
        $text_img_pdf_entries \
        $office_suite_entries \
        $email_client_entry

custom_settings_user personal \
    set_disable_mimetype \
        $email_client_entry

custom_settings $print_dvm \
    set_file_persistent \
        /etc/cups/

custom_settings $print_dvm \
    set_printer

Menu entries


echo 'setting menu entries ...'
set_appmenus ()
set_appmenus ()
{
    for appvm in $1
    do
        echo "    $appvm ..."
        echo ${@:2} | qvm-appmenus --set-whitelist - $appvm
        qvm-appmenus --update $appvm
    done
}
set_appmenus $apps_dvm \
    $file_manager_entry \
    $xterm_entry

set_appmenus mail \
    $file_manager_entry \
    $email_client_entry

set_appmenus media \
    $file_manager_entry \
    $media_player_entry

set_appmenus personal \
    $file_manager_entry \
    $text_editor_entry \
    $office_suite_entry1

set_appmenus $print_dvm \
    $file_manager_entry \
    $printer_entry

set_appmenus vault \
    $password_manager_entry \
    $file_manager_entry \
    $xterm_entry

set_appmenus "$web_dvm daily-web work-web" \
    $file_manager_entry \
    $web_browser_entry

set_appmenus work \
    $file_manager_entry \
    $text_editor_entry \
    $code_editor_entry \
    $xterm_entry

Dom0 settings


echo 'setting dom0 ...'

:information_source: Note:
All these settings, except Qubes tools, are not Qubes OS specific.

cfg_dir=$HOME/.config/
xfce_cfg_dir=$cfg_dir/xfce4/xfconf/xfce-perchannel-xml/
memory & swap
echo '
GRUB_CMDLINE_XEN_DEFAULT="$GRUB_CMDLINE_XEN_DEFAULT dom0_mem=min:2048M dom0_mem=max:3072M"' \
    | sudo tee -a /etc/default/grub > /dev/null
sudo grub2-mkconfig -o /boot/grub2/grub.cfg

dom0_swap=/dev/qubes_dom0/swap
sudo swapoff $dom0_swap
sudo lvresize --yes --size 2G $dom0_swap
sudo mkswap $dom0_swap
sudo swapon $dom0_swap
sudo udevadm trigger --action=change
auto-login (not recommended)
sudo sed -i -E -e "s/^#(autologin-user=)/\1$USER/" \
               -e 's/^#(autologin-user-timeout=0)/\1/' \
         /etc/lightdm/lightdm.conf
intel screen tearing (if needed)
echo '
Section "Device"
    Identifier "Intel Graphics"
    Driver "Intel"
EndSection' | sudo tee /etc/X11/xorg.conf.d/20-intel.conf

disposable app qube
qvm-prefs dom0 default_dispvm $web_dvm
qubes update
qvm-features dom0 qubes-vm-update-max-concurrency 4
qvm-features dom0 qubes-vm-update-restart-system  ''
qvm-features dom0 qubes-vm-update-update-if-stale 5
qube manager

qube-manager dark mode (built-in method)

echo '[General]
window_size=@Size(755 800)

[columns]
Backup=false
Default DispVM=false
Internal=false
IP=false
Is DVM Template=false
Last backup=false
Virt Mode=false' > "$cfg_dir/The Qubes Project/qubes-qube-manager.conf"
gtk
gtk3_dir=$cfg_dir/gtk-3.0/

mkdir -p $gtk3_dir
echo '[Settings]
gtk-theme-name=Arc-Dark
gtk-icon-theme-name=gnome' > $gtk3_dir/settings.ini

echo 'notebook header {
    background-color: #2F343F
}' > $gtk3_dir/gtk.css
bash
echo '
export EDITOR=/usr/bin/vim
export SUDO_EDITOR=$EDITOR

export PS1="\[\e[1;31m\]$PS1\[\e[m\]"

alias ls="ls --color=auto -F"
alias ll="ls -lh"
alias la="ls -lah"
alias sudo="sudo "' >> $HOME/.bashrc
vim
echo 'source $VIMRUNTIME/defaults.vim
set expandtab
set smarttab
set shiftwidth=4
set tabstop=4' > $HOME/.vimrc
file chooser
gsettings set org.gtk.Settings.FileChooser sort-directories-first true
gsettings set org.gtk.Settings.FileChooser window-position '(0, 0)'
gsettings set org.gtk.Settings.FileChooser window-size '(800, 500)'

appearance
echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="xsettings" version="1.0">
  <property name="Net" type="empty">
    <property name="ThemeName"     type="string" value="Arc-Dark"/>
    <property name="IconThemeName" type="string" value="gnome"/>
  </property>
</channel>' > $xfce_cfg_dir/xsettings.xml
file manager
echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="thunar" version="1.0">
  <property name="last-location-bar"  type="string" value="ThunarLocationButtons"/>
  <property name="last-show-hidden"   type="bool"   value="true"/>
  <property name="last-view"          type="string" value="ThunarCompactView"/>
  <property name="last-window-height" type="int"    value="550"/>
  <property name="last-window-width"  type="int"    value="850"/>
</channel>' > $xfce_cfg_dir/thunar.xml
mouse and touchpad
touchpad_name=$(xinput list --name-only \
    | grep -i touchpad \
    | sed -e '/Synaptics/ d' \
          -e 's/://g' \
          -e 's/ /_/g')

echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="pointers" version="1.0">
  <property name="'$touchpad_name'" type="empty">
    <property name="Properties" type="empty">
      <property name="libinput_Tapping_Enabled" type="int" value="1"/>
    </property>
  </property>
</channel>' > $xfce_cfg_dir/pointers.xml
notifications
echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfce4-notifyd" version="1.0">
  <property name="expire-timeout" type="int" value="5"/>
</channel>' > $xfce_cfg_dir/xfce4-notifyd.xml
panel
panel_cfg=$xfce_cfg_dir/xfce4-panel.xml

add_panel_launcher ()
{
    if [[ $# -ne 0 ]]
    then
        local launcher_dir=$HOME/.local/share/applications/
        local launcher_file=org.qubes-os.vm.$1.$2
        xfce4-panel --add=launcher $launcher_dir/$launcher_file
        $FUNCNAME ${@:3}
    fi
}
add_panel_launcher \
    vault     $password_manager_entry \
    personal  $file_manager_entry \
    daily-web $web_browser_entry \
    daily-web $file_manager_entry \
    work      $code_editor_entry \
    work      $file_manager_entry

sleep 7
# move new launchers to the left
ids_begin=$(($(sed -n '/plugin-ids/ =' $panel_cfg) + 1))
ids_count=$(($(sed -n '/plugin-ids/,/property/ p' $panel_cfg | wc -l) - 2))
launcher_end=$(($ids_begin + $ids_count - 1))
launcher_begin=$(($launcher_end - 5))

launcher_saved=$(sed -n "$launcher_begin,$launcher_end p" $panel_cfg)
sed -i -e "$launcher_begin,$launcher_end d" \
       -e "$ids_begin r"<(echo "$launcher_saved") \
    $panel_cfg
power manager
echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfce4-power-manager" version="1.0">
  <property name="xfce4-power-manager" type="empty">
    <property name="inactivity-on-ac" type="uint" value="90"/>
  </property>
</channel>' > $xfce_cfg_dir/xfce4-power-manager.xml
pulseaudio volume control
# switch off microphone
amixer set Capture nocap
screensaver
echo 'mode: off' > $HOME/.xscreensaver

echo '
[Desktop Entry]
Hidden=true' > $HOME/.config/autostart/xscreensaver.desktop
session and startup
echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfce4-session" version="1.0">
  <property name="general" type="empty">
    <property name="AutoSave"   type="bool" value="false"/>
    <property name="SaveOnExit" type="bool" value="false"/>
  </property>
</channel>' > $xfce_cfg_dir/xfce4-session.xml
window manager
echo '<?xml version="1.0" encoding="UTF-8"?>
<channel name="xfwm4" version="1.0">
  <property name="general" type="empty">
    <property name="theme"             type="string" value="Arc-Dark"/>
    <property name="button_layout"     type="string" value="O|HMC"/>
    <property name="mousewheel_rollup" type="bool"   value="false"/>
    <property name="workspace_count"   type="int"    value="2"/>
  </property>
</channel>' > $xfce_cfg_dir/xfwm4.xml
xfce terminal
echo '[Configuration]
MiscDefaultGeometry=80x50
ScrollingUnlimited=TRUE
MiscShowUnsafePasteDialog=FALSE' > $cfg_dir/xfce4/terminal/terminalrc
xterm
echo 'xterm*background: black
xterm*foreground: white
xterm*faceName: monospace
xterm*faceSize: 12
xterm*geometry: 80x40
xterm*saveLines: 100000
xterm*selectToClipboard: true' > $HOME/.Xresources
fi ## end: fresh install

Remarks


Please, use your search engine for Qubes OS unspecific questions.
All the answers you are looking for can be found on many websites, really.

This complete example should help you to start your own script.
The Qubes way is to use Salt. qubes-os.org/doc/salt/

Don’t hesitate to ask if something is not clear.
Good luck.

Guide updated to R4.2 (see edit 18↔19 for R4.1 related changes).

latest edit
  • remove Brave welcome splash screen on first launch
9 Likes

This is incredibly helpful and serves as a great reference for a few different pieces of the Qubes/xfce “puzzle” I struggle with at times. Thanks for sharing this and I can see you’ve put a fair amount of time into making it a nice formatted guide, which is appreciated.

1 Like

Wow, It looks to me easier to switch to Qubes then to examine these scripts. Thank you.

Hi, can someone make my guide a wiki ? please.

There are small improvements I would want to make.
And I will probably need to update some things when final 4.2 will be release.

Done @szz9pza :slightly_smiling_face:

1 Like