index

How I configured my Debian linux desktop computer.

Latest update: March 28, 2021

Contents

  1. X
  2. Wi-Fi
  3. Bluetooth
  4. i3 window manager
  5. Picom compositor
  6. High refresh rate
  7. Mouse
  8. Power commands
  9. Chrome
  10. VSCode
  11. Node.js
  12. Fonts
  13. Staying updated

X

Install the xorg package and run startx. With the right drivers, it should just work without any configuration.

Since I have an Nvidia GPU, I installed the nvidia-driver package to get the closed-source drivers with hardware acceleration. To install this package, you must first add the non-free component to your APT sources.list:

sudo apt edit-sources
deb http://deb.debian.org/debian/ buster main contrib non-free
deb-src http://deb.debian.org/debian/ buster main contrib non-free

deb http://security.debian.org/debian-security buster/updates main contrib non-free
deb-src http://security.debian.org/debian-security buster/updates main contrib non-free

I like my CAPSLOCK key to act like ESCAPE:

echo "setxkbmap -option caps:escape" >> ~/.xsessionrc
source ~/.xsessionrc

In xterm, typing ALT inserted a character instead of the META modifier:

echo "xterm*metaSendsEscape: true" >> ~/.Xresources
xrdb -l ~/.Xresources

My mouse cursor was too big:

echo "Xcursor.size: 16" >> ~/.Xresources
xrdb -l ~/.Xresources

For high DPI with 1.5 scaling:

~/.Xresources
Xft.dpi: 144
Xcursor.size: 32
URxvt.font: xft:Ubuntu Mono:pixelsize=24,style=regular

~/.profile
export GDK_SCALE=1.5
export QT_SCALE_FACTOR=1.5

Wi-Fi

Find the Wi-Fi adapter chipset:

lspci | grep -i wifi
04:00.0 Network controller: Realtek Semiconductor Co., Ltd. RTL8822BE 802.11a/b/g/n/ac WiFi adapter

Install the appropriate firmware package for this chipset (see wiki.debian.org/WiFi for a list), in my case firmware-realtek, and reboot.

Find the Wi-Fi network interface name:

ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s31f6: >NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
    link/ether b0:6e:bf:5f:cf:e1 brd ff:ff:ff:ff:ff:ff
3: wlp4s0: >BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
    link/ether f8:28:19:a2:5f:77 brd ff:ff:ff:ff:ff:ff

iwd (iwd.wiki.kernel.org) is a daemon for managing Wi-Fi connections that aims to be more efficient and easier to use than its precursor wpa_supplicant. Install the iwd package, which creates the iwd systemd service which launches the daemon on boot. Use the iwctl command to interact with the daemon:

iwctl
[iwd]# device list
                                    Devices
--------------------------------------------------------------------------------
  Name                Address             Powered   Adapter   Mode
--------------------------------------------------------------------------------
  wlp4s0              f8:28:19:a2:5f:77   on        phy0      station

[iwd]# station wlp4s0 scan
[iwd]# station wlp4s0 get-networks
                               Available networks
--------------------------------------------------------------------------------
    Network name                    Security  Signal
--------------------------------------------------------------------------------
    mynetwork                       psk       ****
    anothernetwork                  open      ***

[iwd]# station wlp4s0 connect mynetwork
Type the network passphrase for mynetwork psk.
Passphrase: **********

iwd stores network passwords in plain text at /var/lib/iwd/mynetwork.psk, and will automatically connect on next boot.

Since version 0.19, iwd can also perform network configuration like managing DHCP leases, but the version in Debian 10 is 0.14 so this isn't yet available without getting a newer version some other way. For now it's simple enough to get a DHCP lease manually:

sudo dhclient wlp4s0

I haven't yet figured out the best way to get a DHCP lease automatically on boot.

Bluetooth

Install the firmware for the Realtek Bluetooth controller on my motherboard:

sudo apt install firmware-realtek
reboot

Install the bluetooth package:

sudo apt install bluetooth

Disable the "SIM Access Profile" plugin, because it doesn't work:

sudo vim /etc/systemd/system/bluetooth.target.wants/bluetooth.service
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=sap
sudo systemctl daemon-reload
sudo service bluetooth reload

Use bluetoothctrl to scan for devices:

bluetoothctl
power on
scan on
pair MAC
connect MAC

If there are connection errors for an audio device, A2DP support may need to be installed:

sudo apt install pulseaudio-module-bluetooth
killall pulseaudio

To list paired devices:

bluetoothctl devices

To connect/disconnect to a particular device:

bluetoothctl connect MAC
bluetoothctl disconnect MAC

i3 window manager

I use the i3 tiling window manager (i3wm.org) provided by the i3 package.

If i3 is the only window manager you install, Debian will automatically launch it with X, because it will already be configured as the x-window-manager alternative (see the Debian Wiki). Otherwise, you can run sudo update-alternatives --config x-window-manager to pick it manually.

Picom compositor

Unlike some other window managers (like GNOME and KDE), i3 does not include a compositor (Wikipedia). Among other things, a compositor can prevent tearing when scrolling text or watching videos in programs like Chrome.

Install the picom package (https://github.com/yshui/picom) for a standalone compositor that works well with i3. If you run picom directly it will immediately start compositing, and if you CTRL-C it will stop, so it's easy to experiment with the flags. The only flag I seemed to need was --backend=glx. The --vsync flag didn't seem to make any difference, maybe because Nvidia G-Sync was already providing that functionality.

picom --backend=glx

Then configure i3 to launch it automatically:

echo "exec picom --backend=glx" >> ~/.config/i3/config

High refresh rate

My monitor supports refresh rates up to 165Hz, but X was only running at 60Hz by default. The difference above 60Hz is subtle, but I can definitely tell the difference when I move the mouse cursor.

Use xrandr to see the current resolution and refresh rate. The * symbol to the right of the refresh rate indicates the current mode, and the + symbol indicates the "preferred" (default) mode:

xrandr
Screen 0: minimum 8 x 8, current 2560 x 1440, maximum 32767 x 3276
DP-1 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
DP-2 connected 2560x1440+0+0 (normal left inverted right x axis y axis) 598mm x 336mm
   2560x1440     59.95*+ 165.00  144.00   120.00    99.95    84.98    23.97
   1024x768      60.00
   800x600       60.32
   640x480       59.94
DP-3 disconnected (normal left inverted right x axis y axis)
DP-4 disconnected (normal left inverted right x axis y axis)
DP-5 disconnected (normal left inverted right x axis y axis)
USB-C-0 disconnected (normal left inverted right x axis y axis)

You can change the mode live with xrandr, and to make it persistent you can put that command in your ~/.xprofile file (which runs every time X starts).

xrandr --output DP-2 -r 165 --mode 2560x1440
xrandr
DP-2 connected 2560x1440+0+0 (normal left inverted right x axis y axis) 598mm x 336mm
    2560x1440     59.95 + 165.00*  144.00   120.00    99.95    84.98    23.97
    1024x768      60.00
    800x600       60.32
    640x480       59.94
echo "xrandr --output DP-2 -r 165 --mode 2560x1440" >> ~/.xprofile

Mouse

Find the mouse pointer device name and numeric id:

xinput --list
⎡ Virtual core pointer                    	id=2	[master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer              	id=4	[slave  pointer  (2)]
⎜   ↳ Logitech G603                           	id=9	[slave  pointer  (2)]
⎜   ↳ Logitech USB Receiver                   	id=10	[slave  pointer  (2)]
⎜   ↳ Logitech USB Receiver Consumer Control  	id=12	[slave  pointer  (2)]
⎣ Virtual core keyboard                   	id=3	[master keyboard (2)]
    ↳ Virtual core XTEST keyboard             	id=5	[slave  keyboard (3)]
    ↳ Power Button                            	id=6	[slave  keyboard (3)]
    ...

List the available properties. You can use the device name or numeric id:

xinput --list-props 9
Device 'Logitech G603':
	Device Enabled (155):	1
	Coordinate Transformation Matrix (157):	1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
	libinput Natural Scrolling Enabled (289):	0
	libinput Natural Scrolling Enabled Default (290):	0
	libinput Scroll Methods Available (291):	0, 0, 1
	libinput Scroll Method Enabled (292):	0, 0, 0
	libinput Scroll Method Enabled Default (293):	0, 0, 0
	libinput Button Scrolling Button (294):	2
	libinput Button Scrolling Button Default (295):	2
	libinput Button Scrolling Button Lock Enabled (296):	0
	libinput Button Scrolling Button Lock Enabled Default (297):	0
	libinput Middle Emulation Enabled (298):	0
	libinput Middle Emulation Enabled Default (299):	0
	libinput Accel Speed (300):	0.000000
	libinput Accel Speed Default (301):	0.000000
	libinput Accel Profiles Available (302):	1, 1
	libinput Accel Profile Enabled (303):	0, 1
	libinput Accel Profile Enabled Default (304):	1, 0
	libinput Left Handed Enabled (305):	0
	libinput Left Handed Enabled Default (306):	0
	libinput Send Events Modes Available (274):	1, 0
	libinput Send Events Mode Enabled (275):	0, 0
	libinput Send Events Mode Enabled Default (276):	0, 0
	Device Node (277):	"/dev/input/event4"
	Device Product ID (278):	1133, 16492
	libinput Drag Lock Buttons (307):	<no items>
	libinput Horizontal Scroll Enabled (308):	1

The Accel Profile Enabled property controls cursor acceleration. The value is two bits, where the left bit enables "adaptive" mode (with acceleration), and the right bit enables "flat" mode (no acceleration).

Turn on acceleration:

xinput --set-prop 9 303 1 0

Turn off acceleration:

xinput --set-prop 9 303 0 1

The Accel Speed property controls cursor speed, a number between -1 and 1, defaulting to 0.

Lower cursor speed:

xinput --set-prop 9 300 -0.7

To make this setting persistent, add these commands to ~/.xsessionrc.

Power commands

All of the systemctl power-related commands worked with no configuration: poweroff, reboot, hibernate, and suspend.

However, it can be annoying that these commands require typing your password. (Note for a server you do want to require a password).

One option is to create a sudoers file that allows you to run those specific commands without typing your password (though you will still use sudo). Always edit sudoers files using visudo (provided by the synonymous package), because it helps prevent you from entirely breaking your sudo access by checking that your syntax is correct before writing the file.

sudo visudo /etc/sudoers.d/power
yourusername ALL = \
  NOPASSWD:/usr/bin/systemctl poweroff,\
  NOPASSWD:/usr/bin/systemctl reboot,\
  NOPASSWD:/usr/bin/systemctl hibernate,\
  NOPASSWD:/usr/bin/systemctl suspend

You probably don't want to put much else in your sudoers file, since it could allow a malicious program running as your user to execute any of these commands without your permission or knowledge, plus typing your password makes it harder to accidentally to do bad things yourself. The best explanation of the sudoers file syntax I've found is toroid.org/sudoers-syntax.

Chrome

Google maintains an official APT repository for Chrome. Chrome is not in the official Debian APT repositories (though Chromium is).

Download the .deb package from google.com/chrome. This will both install Chrome, and configure your APT sources and GPG keys to receive updates when you apt update/upgrade.

VSCode

Microsoft maintains an official APT repository for VSCode. VSCode is not in the official Debian APT repositories.

Download the .deb package from code.visualstudio.com/download. This will both install VSCode, and configure your APT sources and GPG keys to receive updates when you apt update/upgrade.

Node.js

The version of Node.js provided by the nodejs package in the official Debian APT repositories is a Long Term Support (LTS) version. This is usually fine, but I like to have the latest version because I sometimes experiment with new features. See nodejs.org/en/about/releases for the release schedule.

I get the latest version of Node.js by downloading the .tar.xz binary release archive from nodejs.org/dist/latest, unpacking it to $HOME/node, and adding $HOME/node/bin to my $PATH.

To check if there's a new version to download, I use this script:

~/bin/check-node-version.sh
#!/usr/bin/env bash

set -e

NODE_INSTALLED=$(node --version)

NODE_LATEST=$(curl --silent --show-error --fail \
  https://nodejs.org/dist/latest/SHASUMS256.txt \
  | grep linux-x64.tar.xz | grep -Eo "v[0-9]+\.[0-9]+\.[0-9]+")

if [[ "$NODE_INSTALLED" == "$NODE_LATEST" ]]
then echo "Node.js is current ($NODE_INSTALLED)"
else echo "Node.js is outdated ($NODE_INSTALLED installed, $NODE_LATEST latest)"; exit 1
fi

Note that a company called NodeSource maintains Debian APT repositories for the latest versions, as mentioned at nodejs.org/en/download/package-manager. There's also a program called NVM (github.com/nvm-sh/nvm) which automatically downloads Node.js versions for you. Since neither of these options are managed by the Node.js maintainers, though, I prefer to manage installation myself.

Fonts

Render emojis in Chrome and other applications by installing an emoji font like Noto Color Emoji:

apt install fonts-noto

Staying updated

I use this script to check for any available updates to system APT packages, Node.js, and globally installed npm packages.

~/bin/check-updates.sh
#!/usr/bin/env bash

sudo apt update | tee /dev/stderr | grep -q "All packages are up to date"
APT_CODE=$?
apt list --upgradable
echo

check-node-version.sh
NODE_CODE=$?
echo

npm --global outdated
NPM_CODE=$?
echo

RED="\u001b[31m"
GREEN="\u001b[32m"
RESET="\u001b[0m"

EXITCODE=0

if [[ $APT_CODE == 0 ]]
then echo -e "[${GREEN}OK${RESET}] APT"
else echo -e "[${RED}!!${RESET}] APT (sudo apt upgrade)"; EXITCODE=1
fi

if [[ $NODE_CODE == 0 ]]
then echo -e "[${GREEN}OK${RESET}] Node.js"
else echo -e "[${RED}!!${RESET}] Node.js (https://nodejs.org/dist/latest/)"; EXITCODE=1
fi

if [[ $NPM_CODE == 0 ]]
then echo -e "[${GREEN}OK${RESET}] NPM"
else echo -e "[${RED}!!${RESET}] NPM (npm --global update)"; EXITCODE=1
fi

exit $EXITCODE