Arch Linux (in dual boot)

Since my consulting work relies heavily on Microsoft tools like Office and Power BI, I have primarily been using Windows as my operating system.

I still do a lot of programming, and to my surprise, developing on Windows has been very good. When I need to run something dependent on Linux like Airflow and Redmine, I can use WSL and Docker.

One of the issues with virtualizing Linux is the loss of performance. For this reason, I allocated about 60GB to install Arch in dual boot on my laptop.

I wrote on Twitter that I was installing Arch, and someone suggested updating my old guide because a lot has changed in 8 years. So, I decided to break the hiatus of over 3 years on this blog to document my new installation process.

When I wrote my post in 2012, the official wiki was not up to date. Nowadays, it is very comprehensive, and I recommend following it for possible updates and more details.

As in previous posts, I want to make it clear that these are the configurations I chose for my computers (a Dell Latitude 3480 and an Acer Predator Helios 300).
Some configurations listed here may be incompatible with your system or simply unnecessary depending on the Desktop Environment you will be using.

Do not copy and paste commands without understanding them first!

I will not go into many details about the commands used. More information about them can be found on the internet and in the official wiki.
If you have any questions or suggestions, feel free to leave a comment here or send me a DM on Twitter.

Update: Since May 2021, the Arch ISO image includes a guided installer called Archinstall. As I am already used to it, I will continue to perform the manual installation process.

Disk partitioning

I installed Windows first and, during the installation, I partitioned the 240GB SSD as follows:

  • 529 MB: Windows Recovery
  • 500 MB: EFI (larger than the default)
  • 150 GB: Windows
  • 60 GB: Arch Linux
  • Remaining: Encrypted NTFS partition with Veracrypt to access from both Linux and Windows

I did not create a partition for Swap. If I ever need it, I can create a swap file on the disk, in memory, or on the video card memory.

I also left /, /home, and /var on the same partition. /boot will be the 500 MB EFI partition. By default, Windows creates this partition with 100MB, but this may not be enough if you want to load many modules or have more than one kernel.

If I had not created the partitions through Windows, I could use some tool in Linux like cgdisk or parted.

Installation USB

I downloaded the ISO image via BitTorrent and created the bootable USB using the default options of Rufus Portable. I restarted the computer and kept pressing F12 until the list with the option to boot from the USB drive appeared. When selecting the option to install Arch, a message appeared waiting for a disk with LABEL=ARCH202008. I simply reconnected the USB drive to the USB port and the system loaded correctly.

Keyboard layout

When dropping into the terminal, the first step is to configure the keyboard. As I use a Brazilian keyboard, I ran the following command:

  • For Brazilian keyboard:

    loadkeys br-abnt2
  • For American keyboard:

    loadkeys us-acentos

Other options can be found at ls /usr/share/kbd/keymaps/**/*.map.gz.

System clock update

timedatectl set-ntp true

The Linux default is to keep the hardware clock in UTC, but Windows default is to use local time. I prefer to keep it in UTC and configure Windows through Regedit.

Disk encryption

lsblk                       # To find out the partition. /dev/sda5 in my case
cryptsetup -v luksFormat /dev/sda5

I found the default settings OK. More options can be found in the wiki.

If lsblk does not list your SSD (M.2 PCIe NVMe), check in the BIOS if the SATA Mode is set to AHCI. If Windows was installed in RAID mode, you will need to restart it in safe mode and change the SATA Mode in the BIOS.

Root partition formatting

cryptsetup luksOpen /dev/sda5 arch
mkfs.ext4 -L arch /dev/mapper/arch

Mounting partitions

mount /dev/mapper/arch /mnt
mkdir /mnt/boot
mount /dev/sda2 /mnt/boot

Internet connection

  • Wifi

    Connect to wifi using iwd

    iwctl --passphrase YOUR_PASSWORD station wlan0 connect YOUR_SSID
  • Ethernet

    Wired networks should work automatically. If not:

    • Set a static IP with Iproute2

    • Obtain a dynamic IP via DHCP: systemctl start dhcpcd@enp2s0.

      Use ip link to get the interface name.


Pacman reads the file /etc/pacman.d/mirrorlist to determine from which mirrors to download packages. It’s good to keep the most up-to-date and fastest mirrors at the top to avoid long download times.

The new Arch installer should do this automatically using Reflector as soon as the internet connection is detected. Just make sure everything is OK. Since I’m in São Paulo, the mirrors from UFSCar and UFPR were at the top.

Installation of main packages

In this step, I install the base system and all the packages I will need, such as network tools, text editor, manual pages, Xorg, and PulseAudio.

pacstrap /mnt \
    base{,-devel} \
    linux{,-firmware} \

    intel-ucode \

    iwd \
    dhcpcd \
    iputils \

    zsh \
    go \
    git \
    neovim \
    python{,-pip,-pynvim} \

    man-db \
    man-pages \
    texinfo \

    xorg-server \
    xorg-xinit \
    xf86-video-intel \
    xf86-input-synaptics \

    alsa-utils \
    # pulseaudio{,-alsa,-bluetooth} \
    # pavucontrol


genfstab -U -p /mnt >> /mnt/etc/fstab


arch-chroot /mnt


  • Brazil

    ln -sf /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime
  • Germany

    ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime

Save the time:

hwclock --systohc


I use the system in English, but I also enable pt_BR and de_DE. I tend to prefer en_GB over en_US, but that’s a matter of taste.

sed -i -e 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen
sed -i -e 's/#en_GB.UTF-8 UTF-8/en_GB.UTF-8 UTF-8/' /etc/locale.gen
sed -i -e 's/#pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/' /etc/locale.gen
sed -i -e 's/#de_DE.UTF-8 UTF-8/de_DE.UTF-8 UTF-8/' /etc/locale.gen
echo LANG=en_US.UTF-8 > /etc/locale.conf

Virtual Console

Configures the keyboard layout and font.

echo -e "KEYMAP=br-abnt2\nFONT=latarcyrheb-sun16" > /etc/vconsole.conf


echo -e "KEYMAP=us-acentos\nFONT=latarcyrheb-sun16" > /etc/vconsole.conf

Network Configuration

Adds the computer’s name to /etc/hosts and also blocks some malicious and advertising sites.

echo "computer_name" > /etc/hostname
curl --output /etc/hosts --url ""
echo -e "\tcomputer_name.localdomain\tcomputer_name" >> /etc/hosts


I added encrypt because the system partition is encrypted.

I also load the Nvidia modules, as explained below.

$ nvim /etc/mkinitcpio.conf
  HOOKS=(base udev autodetect modconf block encrypt filesystems keyboard keymap consolefont fsck)
mkinitcpio -p linux


Adds the repository multilib, colors pacman, and adds the Pac-Man animation.

sed -i -e '/^#\[multilib\]$/,+1s/#//' /etc/pacman.conf
sed -i -e '/# Misc options/a ILoveCandy' /etc/pacman.conf
sed -i -e 's/^# Color/Color/' /etc/pacman.conf

Root password


DNS and network

It is possible to use Iproute2, wpa_supplicant, or Iwd directly to connect to wired and wireless networks, but a connection manager simplifies the process.

Some configurations I have used:

  • NetworkManager

    NetworkManager was created by RedHat and is now part of the GNOME project.

    It allows configuring wired networks, wifi, modems, and VPNs both through the command line and through graphical interfaces (very well integrated with Gnome and KDE).

    sudo pacman -S networkmanager
    sudo systemctl enable NetworkManager.service
    $ nmcli device
    DEVICE             TYPE      STATE         CONNECTION
    enp7s0             ethernet  disconnected  --
    wlp0s20f3          wifi      disconnected  --
    p2p-dev-wlp0s20f3  wifi-p2p  disconnected  --
    lo                 loopback  unmanaged     --
    $ nmcli connection add type ethernet con-name Home ifname enp7s0
    Connection 'Home' (0acb09e3-40a4-479f-9532-a154d8d65dfc) successfully added.
    $ nmcli connection up Home
    Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/1)
    $ nmcli device disconnect enp7s0
    Device 'enp7s0' successfully disconnected.

    Note: It is possible to keep both the cable and wifi connected at the same time so that if you disconnect the cable, there will be no downtime.

    $ nmcli device wifi list
    IN-USE  BSSID              SSID       MODE   CHAN  RATE        SIGNAL  BARS  SECURITY
            7C:05:07:04:38:52  MEU_SSID   Infra  4     130 Mbit/s  80      ▂▄▆_  WPA2
            A4:33:D7:A7:EA:10  Vizinho 1  Infra  6     130 Mbit/s  70      ▂▄▆_  WPA2
            C0:3D:D9:88:1E:00  Vizinho 2  Infra  1     130 Mbit/s  65      ▂▄▆_  WPA2
            68:02:B8:28:9A:60  Vizinho 3  Infra  6     195 Mbit/s  60      ▂▄▆_  WPA1 WPA2
            00:26:F2:6B:D6:F2  Vizinho 4  Infra  11    54 Mbit/s   59      ▂▄▆_  WPA1 WPA2
    $ nmcli device wifi connect MY_SSID password MY_PASSWORD
    Device 'wlp0s20f3' successfully activated with '3eafc7d7-8a89-40ae-8b01-ade3ba474de0'.
    $ nmcli connection show
    NAME      UUID                                  TYPE      DEVICE
    MEU_SSID  3eafc7d7-8a89-40ae-8b01-ade3ba474de0  wifi      wlp0s20f3
    Casa      0acb09e3-40a4-479f-9532-a154d8d65dfc  ethernet  --
    ls /etc/NetworkManager/system-connections/
    Home.nmconnection  MY_SSID.nmconnection
  • systemd-networkd + systemd-resolved + iwd

    ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
    $ nvim /etc/iwd/main.conf

The network can be started when the computer is turned on:

sudo systemctl enable systemd-resolved iwd


~# useradd -m -g users -G wheel,storage,power,video,audio,rfkill -s /bin/zsh julio
~# passwd julio
~# EDITOR=nvim visudo
Descomente a linha `%wheel ALL=(ALL) ALL`


I am using systemd-boot. Since my processor is Intel, I also load its microcode.

~# bootctl install
~# nvim /boot/loader/entries/arch.conf
title   Arch Linux
linux   /vmlinuz-linux
initrd  /intel-ucode.img
initrd  /initramfs-linux.img
options cryptdevice=UUID=0a1b2c3d-…-4e5f6g:arch root=/dev/mapper/arch rootfstype=ext4 add_efi_memmap
sudo nvim /boot/loader/loader.conf
timeout 3

The UUID of the encrypted partition (/dev/sda5) can be seen by running the command lsblk -o +UUID.

With the bootloader configured, we can now restart the computer. If all goes well, a menu will appear to choose the operating system.

sudo umount /mnt/boot
sudo umount /mnt
sudo systemctl reboot

If no display manager was installed, Arch will start in the terminal. Log in with the user created in the previous step.


My dotfiles are available on Github. I clone the repository and create the appropriate symlinks. When I have time, I will use rcm, chezmoi, or GNU Stow to automate this.

~$ git clone
~$ ln -s dotfiles/.config ~/.config
~$ ln -s dotfiles/.zsh ~/.zsh
~$ ln -s dotfiles/.zshrc ~/.zshrc
~$ ln -s dotfiles/.gitconfig ~/.gitconfig
~$ ln -s dotfiles/.p10k_console.zsh ~/.p10k_console.zsh
~$ ln -s dotfiles/.p10k.zsh ~/.p10k.zsh
~$ ln -s dotfiles/.xinitrc ~/.xinitrc
~$ ln -s dotfiles/XCompose/.XCompose ~/.XCompose
~$ ln -s dotfiles/bin ~/bin


There are many options for command-line shells, such as Bash, Zsh, KornShell, and Fish. To list the installed shells, run the command chsh -l.

I have been using Zsh for years. It is POSIX compliant and becomes extremely friendly with some plugins (PowerLevel10k, etc.), which my dotfile installs and configures automatically with zplug.

I created my user with the parameter -s /bin/zsh, but it is possible to change the default shell with the command chsh -s full-path-to-shell.

Terminal Emulators

There are many terminal emulators available for Linux, each with its own advantages and disadvantages.

I used to use urxvt because startup time and memory usage were the main factors for me in the past. Now that I have more powerful computers, other features stand out:

  • Cross-platform (at least Linux and macOS)
  • Unicode
  • Truecolor
  • Ligatures
  • Tabs
  • GPU accelerated
  • Good documentation

Of all the ones I’ve tested, Kitty is the one with the most features. Unfortunately, it’s not perfect, it has phone home features, uses its own terminfo causing issues, and the developer often behaves rudely towards users.

Alacritty is good, but it doesn’t have tabs and may never have.

Terminal Multiplexers

Terminal emulators like Kitty allow splitting a single window into multiple terminals, but most terminal multiplexers do this and much more, such as allowing a session to continue running even after closing the terminal.

Some that I have used:

  • GNU Screen
    • It is the simplest and comes pre-installed in several distributions.
  • Tmux
  • Byobu
    • It is a layer on top of Screen or Tmux. I quite like it, but the project was discontinued in February 2020.
  • Zellij
    • My current favorite. It is a new project (in 2020 it was called Mosaic) written in Rust and with many interesting features.


Yay is an AUR helper/Pacman wrapper written in Go that greatly assists in package installation.

$ git clone && \
    cd yay && \
    makepkg -sri && \
    cd .. && \
    rm -rf yay

Some additional packages I install, including the Kitty terminal, Gimp image editor, VS Code, and various utilities:

$ yay -S \
    ack \
    aws-cli-v2-bin \
    bleachbit \
    bluez \
    bluez-utils \
    calibre \
    chromium \
    code \
    ctags \
    dbeaver \
    digikam \
    discord \
    docker-compose \
    duf \
    dust \
    fcitx5-chinese-addons \
    fcitx5-im \
    ffmpeg \
    firefox \
    gimp \
    gitahead \
    goldendict \
    gparted \
    htop \
    hugo \
    imagemagick \
    insomnia-bin \
    jq \
    kitty \
    lens-bin \
    libreoffice-fresh \
    make \
    meld \
    neofetch \
    nerd-fonts-hack \
    noto-fonts-emoji \
    npm \
    ntfs-3g \
    oath-toolkit \
    obs-studio \
    okular \
    openssh \
    p7zip \
    polkit \
    qalculate-gtk \
    renameutils \
    rsync \
    rust-analyzer \
    slack-desktop \
    spotify \
    sshfs \
    telegram-desktop \
    texlive-core \
    texlive-latexextra \
    thunderbird \
    transmission-remote-gtk \
    tree \
    ttf-joypixels \
    usbutils \
    veracrypt \
    vlc \
    wget \
    xclip \
    xorg-xrandr \
    # audacity \
    # awesome-git \
    # aws-cli \
    # blueman \
    # byobu \
    # copyq \
    # feh \
    # gnome-keyring \
    # graphviz \
    # hdparm \
    # ibus \
    # ibus-libpinyin \
    # kdenlive \
    # light-git \
    # macchanger \
    # maim \
    # picom-git \
    # postman-bin \
    # redshift \
    # rofi-git \
    # sshpass \
    # teams \
    # unrar \
    # unzip


sudo pacman -S nftables
sudo systemctl enable nftables.service
sudo nft list ruleset


Install the fonts available in the official repository or in the AUR:

$ yay -S \
    inter-font \
    nerd-fonts-fantasque-sans-mono \
    noto-fonts \
    noto-fonts-cjk \
    noto-fonts-emoji \
    otf-san-francisco \
    otf-sfmono-patched \
    ttf-dejavu \
    ttf-liberation \

Manual Installation

Fonts not available as packages can be installed manually. Just add the files to a subfolder of /usr/local/share/fonts (for all users) or ~/.local/share/fonts/ (just for your user).


sudo mkdir -p /usr/local/share/fonts/{otf,ttf}
sudo 7z x -o/usr/local/share/fonts/ttf/Fontin
sudo find /usr/local/share/fonts -type d -exec chmod 755 {} \;
sudo find /usr/local/share/fonts -type f -exec chmod 644 {} \;
$ tree /usr/local/share/fonts
├── otf
│   └── Example_Sans
│       ├── Example_Sans_B.otf
│       ├── Example_Sans_BI.otf
│       ├── Example_Sans_I.otf
│       └── Example_Sans_R.otf
└── ttf
    └── Fontin
        ├── Fontin-Bold.ttf
        ├── Fontin-Italic.ttf
        ├── Fontin-Regular.ttf
        ├── Fontin-SmallCaps.ttf
        └── ReadMe.txt

If you want to set specific fonts to be used, edit the file $XDG_CONFIG_HOME/fontconfig/fonts.conf.

Windows Fonts

You can obtain the fonts from the ISO or the C:\Windows\Fonts folder of an installed Windows.

Copy the fonts to /usr/share/fonts/WindowsFonts:

sudo mkdir /usr/share/fonts/WindowsFonts
sudo cp /c/Windows/Fonts/* /usr/share/fonts/WindowsFonts/
sudo chmod 644 /usr/share/fonts/WindowsFonts/*

Alternatively, use a package like ttf-ms-win10 or ttf-ms-win11.

git clone
cd ttf-ms-win11
cp /c/Windows/System32/Licenses/neutral/_Default/Core/license.rtf .
cp /c/Windows/Fonts/* .
makepkg -si


The keyboard on my laptop is ABNT2, but sometimes I connect keyboards with other layouts.

It is possible to configure each keyboard individually by device id or switch between pre-configured profiles.

  1. Get the keyboard device id

    sudo pacman -S xorg-xinput
    $ xinput -list | grep -i Keychron
    ⎜   ↳ Keychron Keychron K2  id=11  [slave pointer  (2)]
        ↳ Keychron Keychron K2  id=10  [slave keyboard (3)]
        ↳ Keychron Keychron K2  id=19  [slave keyboard (3)]
    $ xinput list-props 10
    Device 'Keychron Keychron K2':
        Device Enabled (189):    1
        Device Node (313):   "/dev/input/event5"
    $ ls -l /dev/input/by-id/usb-Keychron*
    … /dev/input/by-id/usb-Keychron_Keychron_K2-event-if01 -> ../event6
    … /dev/input/by-id/usb-Keychron_Keychron_K2-event-kbd -> ../event5
  2. Configure with setxkbmap

    $ setxkbmap -device `xinput -list | \
        grep "Keychron K2.*keyboard" | \
        sed -e 's/^.*id=\([0-9]\+\).*/\1/' | \
        head -1` \
               -layout us              \
               -variant intl           \
               -option lv3:ralt_switch \
               -option compose:rctrl    \
               -option caps:swapescape

    Use the commands localectl list-x11-keymap-layouts, localectl list-x11-keymap-variants us, localectl list-x11-keymap-options, and the file /usr/share/X11/xkb/rules/base.lst to set the appropriate configuration.

    My example swaps CapsLock with Esc, uses the right control as the compose key (Multi_Key), and the right Alt as the third level.

  3. Run the command at system startup

    $ nvim ~/bin/
    # Notebook keyboard
        xinput -list |
        grep "AT Translated Set 2 keyboard.*keyboard" |
        sed -e 's/^.*id=\([0-9]\+\).*/\1/' |
        head -1
    if [ ! -z "$keyboard_id" ]; then
        setxkbmap -device $keyboard_id    \
                  -layout br              \
                  -option lv3:ralt_switch \
                  -option compose:menu    \
                  -option caps:swapescape
    # Keychron K2
        xinput -list |
        grep "Keychron K2.*keyboard" |
        sed -e 's/^.*id=\([0-9]\+\).*/\1/' |
        head -1
    if [ ! -z "$keyboard_id" ]; then
        setxkbmap -device $keyboard_id    \
                  -layout us              \
                  -variant intl           \
                  -option lv3:ralt_switch \
                  -option compose:rctrl   \
                  -option caps:swapescape
    $ nvim ~/.config/autostart/
    [Desktop Entry]
  4. Compose: c-cedilla

    In the US International with Dead Keys layout, ç can be obtained with AltGr + ,, but I prefer to use ´ + c or compose + c.

    Create a ~/.XCompose (or ~/.config/ibus/Compose) based on /usr/share/X11/locale/en_US.UTF-8/Compose:

    # UTF-8 (Unicode) compose sequence
    include "%L"
    <Multi_key> <c>   : "ç"   ccedilla
    <Multi_key> <C>   : "Ç"   Ccedilla
    <dead_acute> <c>  : "ç"   ccedilla
    <dead_acute> <C>  : "Ç"   Ccedilla
    <Multi_key> <t> <o> <w> <e> <r>  : "🗼"

Oriental Characters

You can type characters not available on your keyboard through an input method framework like IBUS or Fcitx5.

I will show how to use ibus-libpinyin and fcitx5-chinese-addons to type in Chinese. Configuring other languages using other IMEs (input method editors) is similar.


IBus (Intelligent Input Bus) is quite popular. It is already integrated with GNOME, but also works well with other Desktop Environments.

  1. Install IBus

    sudo pacman -S ibus ibus-libpinyin
  2. Set the following environment variables:

    sudo nvim /etc/environment
  3. Configure IBus to start with the DE

    $ nvim ~/.config/autostart/ibus-daemon.desktop
    [Desktop Entry]
    Comment=IBus Daemon
    #Exec=ibus-daemon -drx --panel=/usr/lib/kimpanel-ibus-panel
    Exec=ibus-daemon -drx
    GenericName=IBus Daemon
    Name=IBus Daemon

    KDE has a widget called Input Method Panel, but I prefer to use the default IBus panel.

  4. Configure IBus


    Under Input Method, choose Portuguese (Brazil), Chinese-Intelligent Pinyin, and English (US, intl., with dead keys).

    Under General, set the shortcut <Super>space to switch between input methods.

    Under Advanced, check the option to use the system layout so that IBus does not override the layout set using setxkbmap.

  5. Change the panel color to match your taskbar

    gsettings set org.freedesktop.ibus.panel xkb-icon-rgba '#ffffff'


Fcitx (Flexible Context-aware Input Tool with eXtension support) is the input method framework I use.

It is very flexible, has many features, and integrates well with KDE.

  1. Install Fcitx5

    sudo pacman -S fcitx5-im fcitx5-chinese-addons
  2. Set the following environment variables:

    sudo nvim /etc/environment

    The last line is for Kitty. It remains as ibus.

  3. Configure Fcitx5 to start with the DE

    $ nvim ~/.config/autostart/fcitx5.desktop
    [Desktop Entry]
    GenericName=Fcitx5 Input Method
    Comment=Start Fcitx5
  4. Configure Fcitx5

    I recommend making the configurations through the graphical interface fcitx5-configtool (in KDE, it is integrated into the system settings at System Settings » Personalization » Regional Settings » Input Method).

    In my tests, manually edited files ended up being overwritten with the values configured by the graphical interface.

    • Config

      $ nvim ~/.config/fcitx5/config
    • Layouts

      $ nvim ~/.config/fcitx5/profile
      # Group Name
      Name="Group 1"
      # Layout
      Default Layout=us-intl
      # Default Input Method
      # Name
      # Layout
      # Name
      # Layout
      0="Group 1"

      Note: the last entry in the configtool will be the DefaultIM.

    • XCB

      $ nvim ~/.config/fcitx5/conf/xcb.conf
      Allow Overriding System XKB Settings=False
    • UI

      pacman -S fcitx5-material-color fcitx5-nord
      $ nvim ~/.config/fcitx5/conf/classicui.conf
      # Use Per Screen DPI
      # Font
      Font="Noto Sans Black 10"
      # Menu Font
      MenuFont="Sans 10"
      # Theme
  • Cloud Pinyin
$ nvim ~/.config/fcitx5/conf/pinyin.conf

Keychron Keyboard

To make the F1,…,F12 keys work correctly:

sudo nvim /etc/modprobe.d/hid_apple.conf
options hid_apple fnmode=2
sudo mkinitcpio -P

XDG user directories

Install xdg-user-dirs to create the folders.

sudo pacman -S xdg-user-dirs
$ xdg-user-dirs-update --force

Power Management

There are many tools for power management. I use the following:

sudo pacman -S tlp acpid acpi_call upower xfce4-power-manager
sudo systemctl enable tlp.service
sudo systemctl enable acpid.service


After installing bluez, start the bluetooth.service:

sudo systemctl start bluetooth.service

The Bluez configuration is in /etc/bluetooth/main.conf. I use the default settings.

Most desktop environments include some GUI to facilitate the connection, like Bluedevil in KDE, but I also like to install bluez-utils to connect Bluetooth devices via the command line with bluetoothctl.

Example connecting two keyboards and a headset:


Tip: Use FN+Z+J to reset the Keychron keyboard and reset the connections.


asdf has plugins for almost anything. It can replace pyenv, rbenv, goenv, and nvm. There is even a plugin to manage different versions of Poetry.

git clone ~/.asdf --branch v0.11.3

Use the oh-my-zsh plugin to enable and add completions.

  • python

    pacman -S --needed base-devel openssl zlib xz tk
    asdf plugin add python
    asdf install python latest:3
    asdf global python latest
    asdf install python latest:3.10
    asdf local python latest:3.10
    asdf uninstall python <python-version>
  • Ruby

    asdf plugin add ruby
    asdf install ruby latest
    asdf local ruby latest
  • Go

    asdf plugin add golang
    asdf install golang latest
    asdf global golang latest
  • Node.js

    asdf plugin add nodejs
    asdf install nodejs latest

Global versions are defined in the ~/.tool-versions file.


I had my first contact with Python over 10 years ago and today, besides being my favorite programming language, it is the language I use the most for work and personal projects.

The [Zen of Python][PEP 20] says:

“There should be one– and preferably only one –obvious way to do it.”

Which is valid for code, but for packaging, the story is quite different. My feelings are well portrayed in this sequence of xkcd comics: [xkcd 353], [xkcd 927], [xkcd 1987].

xkcd 927
xkcd 927

My current preferences are as follows, in order:

  • To manage different versions of Python: asdf or [Pyenv].

  • To manage virtual environments, control dependencies, and facilitate package publishing: PDM or [Poetry].

  • For more complex cases of machine learning or projects that depend on packages and binaries not available on [pypi]: [Mambaforge].

  • IDE: [VSCode] with the official extension or [PyCharm].

    The issue with [PyCharm] is that some important features (framework integration, SQL editing, Docker integration, etc.) are paid.


Pyenv allows you to install and use different versions of Python.

It is like asdf, but exclusive to Python. If you already use asdf for other languages, it may not make much sense to install Pyenv.

  1. Install Pyenv:

    sudo pacman -S pyenv
  2. Optionally, also install pyenv-virtualenv:

    yay -S pyenv-virtualenv
  3. Add the following lines to ~/.zshrc:

    if (( $+commands[pyenv] )); then eval "$(pyenv init --path)"; fi
    if (( $+commands[pyenv-virtualenv-init] )); then eval "$(pyenv virtualenv-init -)"; fi
  4. Check which versions are available and install the ones you will use:

    pyenv install --list
    pyenv install 3.8.12
    pyenv install mambaforge
  5. List the installed versions:

    pyenv versions
  6. In a project folder, set the version to be used:

    pyenv local 3.8.12

    A file named .python-version will be created:

    $ cat .python-version


Update: section updated for version 1.2.

  1. Install Poetry

    I prefer not to use the system package manager for Poetry, there are some good alternatives:

    • Script:

      wget -O
      python --preview

      Note: The old installer ( has been replaced by Do not pass the --preview parameter if you want the latest stable version.

    • asdf:

      asdf plugin add poetry
      asdf install poetry latest
      asdf global poetry latest
    • pipx:

      pipx install poetry
  2. Add to PATH

    The old installer used the folder $HOME/.poetry/bin. The new one uses $HOME/.local/bin.

    Using my function in .zshrc:

    addToPathStart $HOME/.local/bin
  3. Install completions

    poetry completions zsh > ~/.zprezto/modules/completion/external/src/_poetry

    The above command is because I use Prezto. The important thing is to install in a folder listed in $fpath.

  4. Create a new project

    poetry new my-project
    cd my-project
  5. Set the Python version using Pyenv or asdf

    pyenv local 3.8.12


    asdf local 3.8.12
    $ nvim pyproject.toml
    python = "^3.8"
    poetry env use -- /home/julio/.pyenv/versions/3.10.3/bin/python


    poetry env use -- /home/julio/.asdf/installs/python/3.10.3/bin/python
    poetry run python --version
    poetry debug info
  6. Add dependencies

    Development dependencies are passed with --dev.

    $ poetry add --group dev \
        bandit \
        black \
        codecov \
        coverage \
        flake8 \
        gitchangelog \
        isort \
        mkdocs \
        mypy \
        pydocstyle \
        pytest \
    poetry add typer\[all\]
  7. Update Poetry

    poetry self update

    Remember to generate the completions again.

  8. Install poetry-plugin-up

    poetry self add poetry-plugin-up
    poetry up --latest

    This plugin updates the packages in pyproject.toml to the latest version.

If Poetry gets stuck on “Resolving dependencies,” try the following:

find ~/.cache/pypoetry -name '*.lock' -type f -delete
poetry env remove --all
poetry cache clear --all .
rm -rf $(poetry config cache-dir)/artifacts


Mambaforge combines Mamba and Conda-Forge.

Mamba is a reimplementation of the Conda package manager in C++. It resolves dependencies very quickly and downloads in parallel.

Conda-Forge Channel contains recipes written by the community for Conda. It has many more packages than the official Conda repository.

  1. If you do not want to use Pyenv, it is possible to install Mambaforge manually:

  2. Update base packages

    mamba update mamba
    mamba update --all
  3. Create and activate a virtual environment

    mamba create -n env_name python=3.10 jupyterlab …
    conda activate env_name
  4. Install some additional packages

    $ mamba install \
          azure-identity \
          bandit \
          black \
          bokeh \
          dask \
          flake8 \
          ipywidgets \
          isort \
          jupyterlab_code_formatter \
          kafka-python \
          keras \
          koalas \
          matplotlib \
          mypy \
          numpy \
          openpyxl \
          pandas \
          plotly \
          pyarrow \
          pycodestyle \
          pydocstyle \
          pylama \
          pylint \
          pyspark \
          pytest \
          python-graphviz \
          python-wget \
          scikit-learn \
          seaborn \
          sqlalchemy \
          tenacity \
          tensorflow \
          unidecode \


PEP 582 was rejected by the Python Steering Council in March 2023.

Like Poetry, PDM is a package and dependency manager.

The difference is that it follows the new PEPs (PEP 582, PEP 517, and PEP 621).

You can use a virtualenv or follow PEP 582 and use a folder called __pypackages__. This folder is equivalent to node_modules, it is added to sys.path.


  • Via script:

    curl -sSL | python3 -
  • Via asdf:

    asdf plugin add pdm
    asdf install pdm latest
    asdf local pdm latest
  • Completions in ZSH

    pdm completion zsh > ~/.zprezto/modules/completion/external/src/_pdm


  • Create a project

    pdm init
  • Add dependencies

    pdm add requests flask
  • Add development dependencies

    pdm add -dG dev jupyterlab jupyterlab-code-formatter black isort pylint mypy
  • Remove dependencies

    pdm remove flask
  • List dependencies

    pdm list --graph
  • Install project

    pdm install
  • Install based on the .lock file

    pdm sync
  • Update dependencies

    • All packages

      pdm update
    • Specific package

      pdm update <package>
      • Packages from a group

        pdm update -G <group>
      • Force update pinned packages

        pdm update --update-eager
  • Remove unlisted packages

    pdm sync --clean
  • Run binary in the environment

    pdm run jupyter lab


pdm self update


The recommended way to develop in Rust is to use rustup, which can be installed in two ways:

  1. Official script:

    curl --proto '=https' --tlsv1.2 -sSf | sh
  2. Pacman:

    sudo pacman -S rustup
    rustup default stable

    This method is more convenient in Arch, as it keeps Rust and its dependent packages constantly updated.

Test the installation:

$ rustc --version
rustc 1.53.0 (53cb7b09b 2021-06-17)
$ cargo --version
cargo 1.53.0 (4369396ce 2021-04-27)
$ rustdoc --version
rustdoc 1.53.0 (53cb7b09b 2021-06-17)
$ cargo new hello
 Created binary (application) `hello` package
$ cd hello

$ cargo run
   Compiling hello v0.1.0 (/home/julio/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.43s
     Running `target/debug/hello`
Hello, world!

My Neovim is configured to use the rust-analyzer as LSP Server. Just install the binary:

sudo pacman -S rust-analyzer


One of the notebooks has an integrated Intel GPU and an Nvidia GeForce RTX 2070 Max-Q, which has better performance than the Intel GPU but consumes more power.

It is possible to permanently disable one of the cards or use Nvidia Optimus technology to switch between the cards according to the need.

Install drivers and utilities:

sudo pacman -S nvidia nvidia-utils nvidia-settings opencl-nvidia

Running a specific program on Nvidia

To perform the so-called PRIME render offload just start the program with the prime-run command. Ex.:

sudo pacman -S nvidia-prime
prime-run kdenlive

Using only the Nvidia card

sudo nvim /etc/X11/xorg.conf.d/10-nvidia-drm-outputclass.conf
Section "OutputClass"
    Identifier "intel"
    MatchDriver "i915"
    Driver "modesetting"

Section "OutputClass"
    Identifier "nvidia"
    MatchDriver "nvidia-drm"
    Driver "nvidia"
    Option "AllowEmptyInitialConfiguration"
    Option "PrimaryGPU" "yes"
    ModulePath "/usr/lib/nvidia/xorg"
    ModulePath "/usr/lib/xorg/modules"
$ nvim ~/.xinitrc (ou /usr/share/sddm/scripts/Xsetup para o SDDM)
xrandr --setprovideroutputsource modesetting NVIDIA-0
xrandr --auto
setxkbmap br -option caps:swapescape

To ensure that the module will be loaded before the graphical interface.

sudo nvim /etc/modprobe.d/nvidia-drm-nomodeset.conf
options nvidia-drm modeset=1
sudo nvim /etc/mkinitcpio.conf
MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)
sudo mkinitcpio -P

(Note: If the /boot partition has little free space, it is possible to disable the generation of the fallback image by commenting out the related lines in /etc/mkinitcpio.d/linux.preset. If this is not enough, it will be necessary to resize the partitions.)

sudo systemctl reboot
sudo pacman -S virtualgl


Screen tearing is when parts of different frames are displayed on the screen at the same time.

This problem was quite noticeable when watching videos on the monitor connected via HDMI. Test with the video and with the site

  1. Browser Configuration

    Check if enabling layers.acceleration.force-enabled in Firefox’s about:config improves anything.

  2. Force composition

    This option may increase latency, but it was what solved my problem.

    Check the current settings:

    $ nvidia-settings --query CurrentMetaMode
      Attribute 'CurrentMetaMode' (Predator:0.0): id=50, switchable=yes, source=RandR :: DPY-2: nvidia-auto-select @1920x1080 +1920+0 {ViewPortIn=1920x1080, ViewPortOut=1920x1080+0+0}

    Enable ForceCompositionPipeline (or ForceFullCompositionPipeline):

    nvidia-settings --assign CurrentMetaMode="nvidia-auto-select +1920+0 { ForceCompositionPipeline = On }"

    The +1920+0 offset is because the laptop screen is full HD and is positioned to the left of the external monitor.

    Check the new settings:

    $ nvidia-settings --query CurrentMetaMode
      Attribute 'CurrentMetaMode' (Predator:0.0): id=50, switchable=no, source=nv-control :: DPY-2: nvidia-auto-select @1920x1080 +1920+0 {ViewPortIn=1920x1080, ViewPortOut=1920x1080+0+0,

    Add this command to a file that is executed at startup, such as autostart, xinit, or Xsetup.


    s="$(nvidia-settings -q CurrentMetaMode -t)"
    echo "$s"
    if [[ "${s}" != "" ]]; then
      s="${s#*" :: "}"
      echo "$s"
      if [[ "${s}" != "NULL" ]]; then
        nvidia-settings -a CurrentMetaMode="${s//\}/, ForceCompositionPipeline=On\}}"


     # Xsetup - run as root before the login dialog appears
     # Keyboards
     [ -s "$kb_script" ] && \. "$kb_script"
     # Nvidia
     xrandr --setprovideroutputsource modesetting NVIDIA-0
     xrandr --auto
     # Force Composition Pipeline
     [ -s "$nfcp_script" ] && \. "$nfcp_script"

    If your monitors are fixed, you can use /etc/X11/xorg.conf.d/20-nvidia.conf:

    Section "Screen"
           Option  "MetaModes" "nvidia-auto-select +0+0 {ForceFullCompositionPipeline=On}"

Window Managers and Desktop Environments


For AwesomeWM, I like the configurations from Gerome Matilla. They are a bit bloated, but the layouts are very nice and have interesting features.


KDE Plasma is a very good DE with default settings that I really like.

I used to avoid using KDE on my old computers because it wasn’t light enough, but with my current hardware, this is no longer a big issue.

I recommend installing the following packages:

sudo pacman -S ark \
        dolphin \
        gwenview \
        packagekit-qt5 \
        plasma-browser-integration \
        sddm \

You can swap Caps Lock for ESC in System Settings » Keyboard » Advanced » Caps Lock behavior » Swap Esc and Caps Lock.

Install kdeconnect on the computer and on Android devices to sync notifications, share files, share the clipboard, use Android as a mouse and keyboard, among other functions.

Test the OpenGL and XRender options in System Settings » Hardware » Display and Monitor » Compositor » Rendering backend. The best option is probably OpenGL, but I have seen reports of issues with Nvidia cards.```

If you don’t want to display media controls on the lock screen, run the following command:

kwriteconfig5 --file kscreenlockerrc --group Greeter --group LnF --group General --key showMediaControls --type bool false

If the fonts are too large, force the DPI in System Settings » Fonts » Force font DPI: 96.

Tiling in KDE: Kröhnkite

  1. Move the taskbar to the top of the screen;

  2. Decrease the size of the bar (set to 30);

  3. Right-click on the taskbar, click on Show Alternatives, and choose “Icons-only Task Manager”;

  4. Right-click on the Pager » Configure Pager

    • Show application icons on window outlines
    • Text display: Desktop number
  5. System Settings » Workspace » Window Management » KWin Scripts » Get New Scripts…

  6. Activate Kröhnkite settings

    mkdir -p ~/.local/share/kservices5/
    ln -s ~/.local/share/kwin/scripts/krohnkite/metadata.desktop ~/.local/share/kservices5/krohnkite.desktop

    A configure button for Kröhnkite will appear on the KWin Scripts screen. There, I do the following:

    • Activate the Quarter layout
    • Gaps between tiles: 3 px
    • Prevent windows from minimizing
    • Remove borders of tiled windows
  7. System Settings » Appearance » Window Decorations » Edit Theme » Window-Specific Overrides » Add

    • Regular expression to match: .*
    • Border size: Tiny
    • Hide window title bar
  8. Go to System Settings » Workspace » Workspace Behavior » Virtual Desktops and add as many desktops as you want (I set it to 8, displayed in two rows);

  9. System Settings » Workspace » Workspace Behavior » Desktop Effects » Focus » Dim Inactive;

  10. System Settings » Workspace » Window Management » Window Behavior » Focus » Multiscreen behavior: Separate screen focus;

  11. Border colors

    kwriteconfig5 --file ~/.config/kdeglobals --group WM --key frame 61,174,233
    kwriteconfig5 --file ~/.config/kdeglobals --group WM --key inactiveFrame  239,240,241
  12. Remove minimum window size

    • System Settings » Workspace » Window Management » Window Rules
    • Add New…
    • Window class: Unimportant
    • Window types: Normal Window
    • Add Properties…
    • Minimum Size: Force 0 x 0
  13. Change shortcuts in System Settings » Workspace » Shortcuts » KRunner

    • KRunner: Meta + R
  14. Change shortcuts in System Settings » Workspace » Shortcuts » KWin

    • Switch to Desktop n: Meta + n
    • Switch to Previous Screen: Meta + ,
    • Switch to Next Screen: Meta + .
    • Window to Desktop n: Meta+Shift+Fn (Using Meta + F1,…,F8, because Meta+Shift+6 doesn’t work with US intl. keyboard)
    • Window to Previous Screen: Meta + <
    • Window to Next Screen: Meta + >
    • Krohnkite: Float All: Meta+Shift+F
    • Krohnkite: Right: Meta+L (use ctrl+alt+L to lock the screen)

Android Integration

MTP devices can only be accessed by one program.

To sync a tablet with Calibre, KDE must not have mounted the device before.

Kill the kiod5 process.


Gnome is a complete and quite popular DE. I don’t like the default settings much, but after some quick adjustments, it becomes quite usable:

sudo pacman -S gnome gnome-tweaks gnome-shell-extension-appindicator evolution
sudo systemctl enable gdm
dconf write /org/gnome/desktop/input-sources/xkb-options "['caps:swapescape']"

Through Tweaks, enable the app indicator extension and the minimize and maximize buttons.

Install Pipewire for screen sharing to work on Wayland:

sudo pacman -S pipewire xdg-desktop-portal-gtk


  • Install some of the following packages (not all are necessary)
$ sudo pacman -S \
    wine-staging \
    wine-gecko \
    wine-mono \
    winetricks \
    zenity \
$ sudo pacman -S \
    giflib \
    lib32-giflib \
    libpng \
    lib32-libpng \
    libldap \
    lib32-libldap \
    gnutls \
    lib32-gnutls \
    mpg123 \
    lib32-mpg123 \
    openal \
    lib32-openal \
    v4l-utils \
    lib32-v4l-utils \
    libpulse \
    lib32-libpulse \
    alsa-plugins \
    lib32-alsa-plugins \
    alsa-lib \
    lib32-alsa-lib \
    libjpeg-turbo \
    lib32-libjpeg-turbo \
    libxcomposite \
    lib32-libxcomposite \
    libxinerama \
    lib32-libxinerama \
    ncurses \
    lib32-ncurses \
    opencl-icd-loader \
    lib32-opencl-icd-loader \
    libxslt \
    lib32-libxslt \
    libva \
    lib32-libva \
    gtk3 \
    lib32-gtk3 \
    gst-plugins-base-libs \
    lib32-gst-plugins-base-libs \
    vulkan-icd-loader \
    lib32-vulkan-icd-loader \
    cups \
    samba \
  • Install the Windows fonts as explained previously

  • Run winetricks

    With zenity installed, a GUI will appear to help create prefixes, install dependencies, install and uninstall programs.

Prefixes are created in ~/.local/share/wineprefixes/.

  • Call a program from the command line

    WINEPREFIX="~/.local/share/wineprefixes/nome_prefixo" wine ~/.local/share/wineprefixes/nome_prefixo/drive_c/Programa.exe


Most VPNs have their own installer (which is often available in the AUR).

Prefer WireGuard over OpenVPN when possible.

Julio Batista Silva
Julio Batista Silva
Data Engineer

I’m a computer engineer passionate about science, technology, photography, and languages. Currently working as a Data Engineer in Germany.

comments powered by Disqus