Skip to content

📖 Manual

Introduction

This is a manual that contains useful code snippets, including personal and internet code. I am trying to keep the repository somewhat structured, however, there is no single rigid structure by design, but rather a chaotic collection of things that I think might be helpful. Enjoy figuring this out.

Bootable Installation Media

First, download the installation ISO file from the official website 123. Then, write (or "flash") the ISO file onto a USB flash drive.

Use a tool like Rufus. Download the executable, run it, select a USB device and an ISO, finally start the process.

Warning

Be very careful with dd, as it can overwrite any drive. Avoid missing any important data.

Flash the ISO file using the dd command in the terminal (Arch Wiki guide).

sudo dd if=/path/to/your.iso of=/dev/sdX bs=4M status=progress oflag=sync
  • Replace /path/to/your.iso with the path to the ISO file you downloaded.

Warning

Do not write to a partition like /dev/sdb1, only to the whole device /dev/sdb.

  • Replace /dev/sdX with your USB device (e.g., /dev/sdb).

Make sure your USB flash drive is large enough; for example, if the ISO file is 1.2 GB, it is sensible to use a USB drive with at least 4 GB of storage. Once the ISO is written, plug the USB drive into the target computer. To boot from the USB, you may need to press a special key during startup — check the motherboard manual to find out which key to use (often F12, ESC, or F10).

For OS-specific installation and post-installation instructions, refer to the dedicated pages:

Software

dwm

As per 4, dwm is a dynamic window manager for X. It manages windows in tiled, monocle and floating layouts. All of the layouts can be applied dynamically, optimising the environment for the application in use and the task performed.

The dwm package have the following dependencies on Arch Linux: base-devel git libx11 libxft xorg-server xorg-xinit terminus-font 5. The fonts are optional. And, the former guide used st but I opted for alacritty.

mkdir -p ~/.local/src
git clone git://git.suckless.org/dwm ~/.local/src/dwm
cd ~/.local/src/dwm
nvim config.mk

Edit config.mk to achieve the same as earlier.

# XINERAMALIBS  = -lXinerama
# XINERAMAFLAGS = -DXINERAMA

Edit config.def.h to set the alacritty terminal instead of st.

static const char *termcmd[] = { "alacritty", NULL };

Compile and install.

make clean
make install

Make the dwm executable available to the entire user-space system.

nvim ~/.bashrc

Edit .bashrc file.

PATH=~/.local/bin:$PATH

Copy .xinitrc from default location to home folder for customisation 6.

cp /etc/X11/xinit/xinitrc ~/.xinitrc
nvim ~/.xinitrc

Edit .xinitrc file.

exec dwm

After, when ready to switch to GUI run the following.

clear ; startx

There are many shortcuts to remember 7.

Customisation Instructions

The suckless software is customised primarily through patches 8910. These patches in turn modify the source code that is written in C. To apply a patch issue a command such as like: patch < path/to/patch.diff. I would recommend to issue the commands from the root folder of a project. Also, *.rej contains changes that were rejected due to conflicts. These changes must be handled manually 11.

Applied the patches for dwm in order of appearance: bar height, barpadding, vanitygapps.

The config.h file is a generated file and, in my case, was own by root. Therefore, it was easier to modify config.def.h file and remove config.h when rebuilding.

To set custom fonts, e.g. Terminus, Tamzen, JetBrains Mono, Hack, and Source Code Pro, change the arguments from "monospace:size=10" to "xos4 Terminus:pixelsize=14" in config.def.h.

    static const unsigned int borderpx  = 2;  /* border pixel of windows */
    static const unsigned int gappiv    = 10; /* vert inner gap between windows */
    static const int user_bh            = 10; /* 2 is the default spacing around the bar's font */
    static const int vertpad            = 10; /* vertical padding of bar */
    static const int sidepad            = 10; /* horizontal padding of bar */
    static const char *fonts[]          = { "xos4 Terminus:pixelsize=14" };
    static const char dmenufont[]       = "xos4 Terminus:pixelsize=14";

The patches for dmenu: bar height, border, case-insensitive, center.

    static const int user_bh   = 10; /* add an defined amount of pixels to the bar height */
    static const char *fonts[] = { "xos4 Terminus:pixelsize=14" };
    /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
    static unsigned int lines  = 5;
    /* Size of the window border */
    static unsigned int border_width = 2;

Things I need in a status bar:

  1. Time & date
  2. Volume level
  3. Wi-Fi status
  4. Bluetooth status
  5. TBD

A script that achieves this is available at project/dotfiles/.local/bin/status. I usually copy it over to ~/.local/bin/status. An example is displayed below. If there are squares instead of icons then the system is either missing the right fonts that supports icons or the text editor is misconfigured.

    #!/bin/bash

    while true; do
        # Volume
        VOL=$(wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{printf("%d", $2 * 100)}')

        # Time
        TIME=$(date '+%Y/%m/%d %H:%M:%S')

        # Wi-Fi
        WIFI=$(nmcli -t -f active,ssid dev wifi | grep '^yes' | cut -d: -f2)
        SIGNAL=$(nmcli -t -f active,ssid,signal dev wifi | grep '^yes' | cut -d: -f3)

        # Bluetooth
        BT_INFO=$(bluetoothctl info)
        BT_CONNECTED=$(echo "$BT_INFO" | grep "Connected:" | awk '{print $2}')
        if [ "$BT_CONNECTED" = "yes" ]; then
            BT_NAME=$(echo "$BT_INFO" | grep "Name:" | cut -d ' ' -f2-)
            BT_RAW_RSSI=$(echo "$BT_INFO" | grep "RSSI:" | awk '{print $2}')
        BT_DEC_RSSI=$(printf "%d" "$BT_RAW_RSSI")

        # Signal quality based on dBm.
        if [ "$BT_DEC_RSSI" -ge -60 ]; then
                BT_QUALITY="Excellent"
            elif [ "$BT_DEC_RSSI" -ge -70 ]; then
                BT_QUALITY="Good"
            elif [ "$BT_DEC_RSSI" -ge -80 ]; then
                BT_QUALITY="Fair"
            else
                BT_QUALITY="Poor"
            fi


            BT_STATUS=" ${BT_NAME} (${BT_DEC_RSSI} dBm, ${BT_QUALITY})"
        else
            BT_STATUS=" Disconnected"
        fi

        # Set status bar
        xsetroot -name "  ${VOL}% |   ${WIFI:-Disconnected} (${SIGNAL:-0}%) | ${BT_STATUS} | ${TIME}"

        sleep 1
    done

dmenu

As in 12, dmenu is a dynamic menu for X, originally designed for dwm. It manages large numbers of user-defined menu items efficiently.

The dwm and dmenu packages have the following dependencies on Arch Linux: base-devel git libx11 libxft xorg-server xorg-xinit terminus-font 13. The fonts are optional. And, the former guide used st but I opted for alacritty.

mkdir -p ~/.local/src
git clone git://git.suckless.org/dmenu ~/.local/src/dmenu

To install dmenu.

cd ~/.local/src/dmenu
nvim config.mk

Edit config.mk to avoid sudo in sudo make install, I modified config.mk by changing the prefix to ~/.local 14.

# XINERAMALIBS  = -lXinerama
# XINERAMAFLAGS = -DXINERAMA

Compile and install.

make clean
make install

conda

Following 15, the conda installation commands were customised to use a hidden folder within the user's home directory.

mkdir -p ~/.miniconda3
wget "https://repo.anaconda.com/miniconda/Miniconda3-latest-$(uname)-$(uname -m).sh" -O ~/.miniconda3/miniconda.sh
bash ~/.miniconda3/miniconda.sh -u -p ~/.miniconda3
rm ~/.miniconda3/miniconda.sh
wget "https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe" -outfile ".\miniconda.exe"
Start-Process -FilePath ".\miniconda.exe" -ArgumentList "/S" -Wait
del .\miniconda.exe

Conda or Anaconda, or Miniconda, or Miniforge, or Mambaforge - package, dependency and environment management for any language---Python, R, Ruby, Lua, Scala, Java, JavaScript, C/ C++, FORTRAN.

In practice, miniforge has problems during the system-wide installation on Windows, hence development was moved back to miniconda. The command below should download a Bash script to a current directory.

curl -L -O "https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh"

In the past, it was preferred for conda to be installed system-wide, so the following trick below was used to make it work.

    clear ; sudo groupadd conda
    sudo usermod -aG conda $USER
    newgrp conda
    sudo mkdir /opt/conda/
    sudo chgrp conda /opt/conda/
    sudo chmod 2775 -R /opt/conda/
    bash Miniconda3-latest-$(uname)-$(uname -m).sh

That newgrp conda is not a must if it is possible to log out or even better restart. Also, there were situations in the past where miniforge would have problems to update because of the limited number of maximum open files. This can be fixed using the commands below, where the first command is used to view the number of open files, and the second command is used to set the number of open files.

    ulimit -a
    ulimit -n 50000

neofetch

Neofetch displays a summary of the system, although it is seldom used. However, it is straightforward to install. Once installed, the command can be executed by name:

clear ; sudo pacman --sync neofetch
clear ; sudo apt install neofetch

Neovim

Terminal editors offer a range of options for users. Initially, Nano is often the starting point, followed by an introduction to Vi, then learning Vim and experiencing the complexities of Emacs. Neovim is a popular choice, offering a balanced compromise between various editors. On Ubuntu, it can be installed from the built-in repositories, though this may not provide the latest version. For the latest version, Snap or manual installation can be considered.

sudo pacman --sync neovim
sudo snap install neovim --classic
# Install neovim from source. Benefit: we can decide which version we want.
sudo dnf -y install ninja-build cmake gcc make gettext curl glibc-gconv-extra git
git clone https://github.com/neovim/neovim.git /var/tmp/neovim
cd /var/tmp/neovim
make CMAKE_BUILD_TYPE=RelWithDebInfo
sudo make install
cd ~
rm -rf /var/tmp/neovim

To set the default editor use one of the following.

The default editor can be changed so that the edit command selects the appropriate editor. By default, Nano is typically set as the default editor. This can be modified as follows:

sudo update-alternatives --config editor

A menu will appear with a list of available editors to be set as the system-wide default. Further details can be found here.

echo 'export EDITOR="nvim"' >> ~/.bashrc
echo 'export VISUAL="nvim"' >> ~/.bashrc
source ~/.bashrc

# Should show `nvim`.
printenv EDITOR
echo $EDITOR

Either just in a TTY, shell, or .bashrc (Arch BBS thread).

# Doesn't work right now.
sudo alternatives --install /usr/bin/editor editor /usr/bin/nvim 100

# bash: editor: command not found...
editor --version

# Expect empty output.
git config --global core.editor

oh-my-bash

Customising fonts and making them interactive can be a challenging task. Fortunately, a large community have created scripts to automate the process. On Ubuntu, oh-my-bash is commonly used.

bash -c "$(curl -fsSL https://raw.githubusercontent.com/ohmybash/oh-my-bash/master/tools/install.sh)"

The script will create a backup of the current .bashrc file. If custom settings are present in the file, they can be appended to the end of the new .bashrc file.

cat ~/.bashrc.omb-* >> ~/.bashrc

Additionally, the sexy theme is chosen, as it is considered one of the calmer options. However, it does not display the active conda environment. To resolve this, the following commands can be executed:

nvim ~/.oh-my-bash/themes/sexy/sexy.theme.sh
OMB_PROMPT_SHOW_PYTHON_VENV=${OMB_PROMPT_SHOW_PYTHON_VENV:=true}

Visual Studio Code

Start by downloading *.deb file from the official download page. Once it is downloaded install the application using the following command below.

sudo apt install ./<file>.deb

Addtionally, I would recommend to remove the microsoft repository from the update list unless we want to update the package. Otherwise, it clutters the apt-get update process taking extra time.

sudo nvim /etc/apt/sources.list.d/vscode.list

Unity Hub & Unity

Here is a summary of commands that are need to install Unity Hub.

sudo groupadd unity
sudo mkdir /srv/unity
sudo chgrp unity /srv/unity/
sudo usermod -aG unity $USER

At this point ideally reboot.

sudo chmod 2775 -R /srv/unity/

The place where the project files would be stored is done. Now, we would focus on the place where the editor would be installed.

sudo mkdir /opt/unity
sudo chgrp unity /opt/unity/
sudo chmod 2775 -R /opt/unity/

To install Unity Hub, we, first, setup remote repository and then install it using regular apt-get command.

sudo sh -c 'echo "deb https://hub.unity3d.com/linux/repos/deb stable main" > /etc/apt/sources.list.d/unityhub.list'
wget -qO - https://hub.unity3d.com/linux/keys/public | sudo apt-key add -
sudo apt update
sudo apt-get install unityhub

This information was gathered from the official documentation.

Unreal Engine

Setting up the UE game engine:

sudo groupadd ue
sudo mkdir /srv/ue
sudo chgrp ue /srv/ue/
sudo usermod -aG ue $USER

At this point ideally reboot.

sudo chmod 2775 -R /srv/ue/

Libre Office

To set up Libre Office 7.4.6, download tarball from official website. Unarchive until we have a folder. Go inside DEBS folder and run the command below.

sudo apt install ./\* command.

An old way of setting up Libre Office:

sudo apt install libreoffice

Instructions were taken from here.

gnome-tweaks

:warning: Applicable to Ubuntu GNOME-based installation ONLY!

sudo apt install gnome-tweaks

If gnome-tweaks are installed then this will install Net Speed Simplified.

bash -c "$(curl -sL https://git.io/Jk28b)"

Here, we will install Vitals. Note, very troublesome. Installed GNOME Shell integrations from Add-ons Firefox, then Vitals would be available.

NVIDIA GPU monitoring

sudo apt install gpustat
sudo apt install nvtop (libnvidia-computer-418 was absent)
sudo apt install nvitop (Apt didn't find the package)

Google Drive:

sudo add-apt-repository ppa:alessandro-strada/ppa
sudo apt update && sudo apt install google-drive-ocamlfuse
google-drive-ocamlfuse
mkdir -v ~/GoogleDrive
google-drive-ocamlfuse ~/GoogleDrive

This is only to test that it all works.

df -h

Instructions were taken from here.

KeePass 2

sudo apt install keepass2

Instructions were taken from here.

SSH Key Generation

Setting up an SSH key to work with github:

ssh-keygen -t rsa -C "user@mail.com"

During the prompt, I usually save at the following path.

~/.ssh/couper64.github.com

The passphrase is usually left empty. Although for added protection it makes sense to create one. Following with the configuration to point git to the right SSH key, I create and edit a config file.

nvim ~/.ssh/config

I added the settings below.

Host github.com
    IdentityFile ~/.ssh/github.key
    IdentitiesOnly yes

The following sources were used 16, 17. The last one shows setting up an SSH key for GitHub.

Chrome:

Open the Google Chrome browser and set it up. Make it default. Untick the sending statistics to Google checkbox. Proceed to sign into your account(s).

Put the code below inside ~/.local/share/applications/*.desktop. Replace Google Chrome profile names accordingly, like Default, Profile 1, Profile 2, etc. And, don't forget to make it executable, i.e. sudo chmod +x *.desktop.

#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Type=Application
Terminal=false
Icon[en_US]=google-chrome
Exec=google-chrome --profile-directory=YOUR_PROFILE_NAME
Name[en_US]=Chrome-YOUR_PROFILE_NAME
Name=Chrome-YOUR_PROFILE_NAME
Icon=google-chrome

References

  • https://askubuntu.com/questions/493142/can-i-put-two-chrome-different-users-on-my-launcher

Chrome Remote Desktop:

mkdir ~/.config/chrome-remote-desktop

Follow instructions at remotedesktop.google.com.

References

  • https://askubuntu.com/questions/1264418/chrome-remote-desktop-host-setup-instructions-not-showing-for-ubuntu-18-04
  • https://remotedesktop.google.com/access
  • https://askubuntu.com/questions/1318473/authentication-required-popup
  • https://askubuntu.com/questions/552503/stop-asking-for-authentication-to-mount-usb-stick

The code below is stored in /etc/polkit-1/localauthority/50-local.d/45-remote-desktop-sanity.pkla. 45-remote-desktop-sanity.pkla is custom named with -rw-r--r-- 1 root root file properites.

[Allow Network Manager for Myself]
Identity=unix-user:vlad
Action=org.freedesktop.NetworkManager.*
ResultAny=yes
ResultInactive=yes
ResultActive=yes

[Allow Login, Shutdown, Restart, Etc for Myself]
Identity=unix-user:vlad
Action=org.freedesktop.login1.*
ResultAny=yes
ResultInactive=yes
ResultActive=yes

[Allow Colord all Users]
Identity=unix-user:*
Action=org.freedesktop.color-manager.create-device;org.freedesktop.color-manager.create-profile;org.freedesktop.color-manager.delete-device;org.freedesktop.color-manager.delete-profile;org.freedesktop.color-manager.modify-device;org.freedesktop.color-manager.modify-profile
ResultAny=no
ResultInactive=no
ResultActive=yes

[Allow Package Management all Users]
Identity=unix-user:*
Action=org.freedesktop.packagekit.system-sources-refresh
ResultAny=yes
ResultInactive=yes
ResultActive=yes

[Allow Mounting without Password]
Identity=unix-user:*
Action=org.freedesktop.udisks2.filesystem-mount;org.freedesktop.udisks2.filesystem-mount-other-seat
ResultAny=yes
ResultInactive=yes
ResultActive=yes

.bashrc

alias ls='LC_COLLATE=C ls --color=auto --group-directories-first --sort=version'
alias ll='ls -lashF'

.NET:

wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

Install SDK (then Runtime is not required):

sudo apt-get update
sudo apt-get install -y dotnet-sdk-6.0

Install ASP.NET Core Runtime (no SDK, but ASP and .NET):

sudo apt-get update
sudo apt-get install -y aspnetcore-runtime-6.0

Install .NET Runtime (no SDK, .NET only):

sudo apt-get update
sudo apt-get install -y dotnet-runtime-6.0

Opt-out .NET telemetry: nvim ~/.bashrc export DOTNET_CLI_TELEMETRY_OPTOUT=1 exec bash echo $DOTNET_CLI_TELEMETRY_OPTOUT

References

  • https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#2004
  • https://stackoverflow.com/questions/39306618/how-do-i-set-the-value-in-a-command-shell-for-dotnet-core

monodevelop

sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb [arch=amd64] https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
sudo apt install mono-devel

References

  • https://www.mono-project.com/download/stable/#download-lin

Ranger

Setting up the Ranger terminal file manager: Ensure conda is installed, either Anaconda, Miniconda, or Miniforge.

sudo apt update; sudo apt install libx11-dev libxtst-dev libjpeg-dev
conda create -n ranger python=3.9.13
conda activate ranger
pip install ranger-fm ueberzug
pip uninstall pillow
CC="cc -mavx2" pip install -U --force-reinstall pillow-simd
ranger --copy-config=all
vim ~/.config/ranger/rc.conf

Add the following lines.

set preview_images true
set preview_images_method ueberzug
set show_hidden true

References

  • https://github.com/ranger/ranger
  • https://github.com/ranger/ranger/wiki/Image-Previews
  • https://github.com/seebye/ueberzug#installation
  • https://github.com/uploadcare/pillow-simd

Alternative way of setting up the Ranger terminal file manager.

sudo apt install ranger
sudo apt install libx11-dev
sudo apt install libxext-dev
sudo apt install python3-pip
pip install ueberzug

MkDocs

Materials that were used to move to MkDocs as portfolio website.

  • https://www.youtube.com/watch?v=xlABhbnNrfI
  • https://jameswillett.dev/getting-started-with-material-for-mkdocs/
  • https://github.com/peaceiris/actions-gh-pages?tab=readme-ov-file#getting-started
  • https://github.com/peaceiris/actions-gh-pages?tab=readme-ov-file#%EF%B8%8F-set-personal-access-token-personal_token

Utility

sudo apt install testdisk
sudo apt install foremost
sudo apt install gparted

MinIO

To setup a file storage server for research work, we will setup MinIO on Ubuntu 24.04. For this, we will run a VM using KVM/QEMU. We already set up RAID 5 on the host machine and, simply, created virtual disk using entire available space.

Waiting for Ubuntu to install inside VM...

Used parted command line tool to partition and format the hard drive.

Modified fstab to add automount option. And, used UUID instead of a path because it is more reliable.

Below are commands to run a Docker container of MinIO.

mkdir -p ${HOME}/minio/data

docker run \
-p 9000:9000 \
-p 9001:9001 \
--user $(id -u):$(id -g) \
--name minio1 \
-e "MINIO_ROOT_USER=ROOTUSER" \
-e "MINIO_ROOT_PASSWORD=CHANGEME123" \
-v ${HOME}/minio/data:/data \
quay.io/minio/minio server /data --console-address ":9001"

References: * The tutorial I used to setup virtual disk in guest OS.

Looking Glass

Following the documentation in 1819, the term host means the virtual machine, i.e. guest OS. For instance, when the documentation refers to installation of the Looking Glass service on host it means running that installation inside a target virtual machine. The term client means running a software from a machine that connects to the virtual machine. An easier approach is to view it from the application perspective rather than virtual machine perspective because from the application point of view the client connects to host to see what's inside.

The structure of the documentation doesn't provide information in a step-by-step format making it hard to follow along. Therefore, the following is a draft order to follow:

  • Start from the requirements page. It will explain what software and hardware are needed, e.g. at least two GPUs or the KVMFR module.

  • Proceed to client installation and setup by downloading stable version of source code from the download page and unarchiving it to ~/.local/src/.

tar -zxvf ~/Downloads/looking-glass-B7.tar -C ~/.local/src/
  • Follow on to the depedency installation page for Debian and Debian-based. For Arch Linux follow instructions on the community maintained wiki page.

    • To check what packages are already installed in the system for the client build, the following command was used:
    pacman --query cmake gcc libgl libegl fontconfig spice-protocol make nettle pkgconf binutils libxi libxinerama libxss libxcursor libxpresent libxkbcommon wayland-protocols ttf-dejavu libsamplerate
    
    • Once the missing packages are determined they are installed using this command:
    pacman --sync --refresh --sysupgrade cmake gcc libgl libegl fontconfig spice-protocol make nettle pkgconf binutils libxi libxinerama libxss libxcursor libxpresent libxkbcommon wayland-protocols ttf-dejavu libsamplerate
    
    • To check what packages are already installed in the system for the kernel module build, the following command was used:
    pacman --query dkms linux-headers
    
    • Once the missing packages are determined they are installed using this command:
    pacman --sync --refresh --sysupgrade dkms linux-headers
    
  • Follow instructions on the building page.

  • Once built, use this installation instructions for the local user only.

  • Follow the memory determining page and then the IVSHMEM with the KVMFR module page.

Note

The module/ folder is located inside the client source code, e.g. ~/.local/src/looking-glass-B7/module/.

Virsh

A command to set automatic start of VMs when host boots up.

virsh autostart <vm-name>

A command to undo automatic start of VMs when host boots up.

virsh autostart --disable <vm-name>

Features

Source

A special folder that is used for code.

mkdir ~/.local/src
# If the folder already exists, first delete it, then create it using this command.
btrfs subvolume create ~/.local/src

Sandbox

A special folder that is available to all members of sandbox group. This is a way to keep files even if user accounts are deleted or two users logged in and want to share files between each other.

sudo mkdir /srv/sandbox
# If the folder already exists, first delete it, then create it using this command.
sudo btrfs subvolume create /srv/sandbox
# Show what's before.
ls -lash /srv

# Create the group.
sudo groupadd sandbox
sudo usermod -aG sandbox $USER

# Temporarily, make the user in this session to be in the group.
newgrp sandbox
# Show what's after.
ls -lash /srv

sudo chgrp sandbox /srv/sandbox/
sudo chmod 2775 -R /srv/sandbox/

# Show what's before.
ls -lash /srv/sandbox

# Create folders.
mkdir /srv/sandbox/vmsio
mkdir /srv/sandbox/vm
mkdir /srv/sandbox/share

# Show what's after.
ls -lash /srv/sandbox

Warning

newgrp sandbox sets it active only in the current session. In order to apply the change globally, logout or reboot the system.

Remote Desktop

Remotely accessing desktops is a simple concept, but very cumbursome on practice. There are a lot of paid solutions such as AnyDesk, TeamViewer, ZeroTier, etc. However, ideally, we want a self-hosted solution, so that we could run it indefinitely without heavy reliance on third party developers. There we could consider open source solutions like RustDesk & RustDesk Server software, xrdp, freerdp, etc. Futhermore, we can consider software such as Guacamole. Unfortunately, all of the software has thair strong and not so strong sides. In fact, practically, I find Google Chrome Remote Desktop to be the easiest software to deal with, although, personally, I am not a big fun of it. Historically, Google Chrome Remote Desktop was the easiest to install, and when I came to my work, everyone would understand it. For example, I could say: "Oh, just use Chrome Remote Desktop", and my colleagues would get it without any further explanation. Convenient, isn't it.

Anyhow, it is time to step forward, we are acquiring new equipment, and I am going to setup a complex system with ability to give remote access to our equipment on demand whenever and wherever I am situated. For this, I have continued my experiments with various remote-desktop-software, and, here, I will document some of my findings. Starting from RustDesk. First of all, I found that for self-hosting, we need to use a pair of programs, RustDesk as a client, and RustDesk Server as a server applications. I set the server application on an AWS EC2 instance, what was relatively easy. And, following guidelines I found on the internet, I shared the ID, Relay Server, and Key with the client app. It all seemed to be working on the first day, but, on the second day, it would fail to connect to the server on my Windows machine, but wouldn't fail on my Linux machine. Also, I had to use xrdp instead of built-in gnome remote desktop on Ubuntu 24.04 because it wouldn't work the way I want it to causing me black screens.

Eventually, I stumbled upon Guacamole, following guidelines on the internet, I installed it natively on the Ubuntu 24.04. Notably, Ubuntu 24.04 doesn't provide Tomcat 9 because they moved forward to Tomcat 10. However, Guacamole doesn't like Tomcat 10, but it has not problems with Tomcat 9. To solve this problem, I had to download files directly from the Tomcat website. Luckily, installation was straight forward. The reason I didn't go with the version 10 was because Guacamole didn't support that version at that time. Not sure how it is like now. Furtermore, marrying Guacamole version 1.5.5 with gnome remote desktop was impossible. Apparently, there is a bug related with that version of Guacamole. Again, to solve this problem, I had to clone the official mirror repository of Guacamole on GitHub. After this, it seemed to be working, fingers crossed...

To install Guacamole server-side service, start by installing dependencies:

sudo apt install build-essential libcairo2-dev libjpeg-turbo8-dev \
    libpng-dev libtool-bin libossp-uuid-dev libvncserver-dev \
    freerdp2-dev libssh2-1-dev libtelnet-dev libwebsockets-dev \
    libpulse-dev libvorbis-dev libwebp-dev libssl-dev \
    libpango1.0-dev libswscale-dev libavcodec-dev libavutil-dev \
    libavformat-dev

Then, install server-side service.

git clone https://github.com/apache/guacamole-server
cd guacamole-server
autoreconf -fi
sudo ./configure --with-init-dir=/etc/init.d --enable-allow-freerdp-snapshots
sudo make
sudo make install

After installation of the service, I continued to configuring the service.

sudo ldconfig
sudo systemctl daemon-reload
sudo systemctl start guacd
sudo systemctl enable guacd

As a rule of thumb, I also checked the status of the service.

sudo systemctl status guacd

This I just followed the guide as I had no problems with creating folders at this stage either.

sudo mkdir -p /etc/guacamole/{extensions,lib}

From now on, I focused on Tomcat 9.

sudo apt install openjdk-17-jdk
sudo useradd -m -U -d /opt/tomcat -s /bin/false tomcat
sudo wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.96/bin/apache-tomcat-9.0.96.tar.gz -P /tmp
sudo tar -xvf /tmp/apache-tomcat-9.0.96.tar.gz -C /opt/tomcat
sudo chown -R tomcat:tomcat /opt/tomcat

Tomcat is installed, we proceed to configure it.

cd /etc/systemd/system
sudo nvim tomcat.service

Once I created the file, I paste in the following configuration.

[Unit]
Description=Tomcat Server
After=network.target

[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64"
WorkingDirectory=/opt/tomcat/apache-tomcat-9.0.96
ExecStart=/opt/tomcat/apache-tomcat-9.0.96/bin/startup.sh

[Install]
WantedBy=multi-user.target

Restarting services to let it pick up the changes won't hurt at this stage.

sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl enable tomcat

At this point, if I access the service at port 8080, it should render a page saying all is working. Now, I can focus my attention on Guacamole Web App client.

sudo wget https://downloads.apache.org/guacamole/1.5.5/binary/guacamole-1.5.5.war
sudo mv guacamole-1.5.5.war /opt/tomcat/apache-tomcat-9.0.96/webapps/guacamole.war
sudo systemctl restart tomcat guacd

This is a "production-ready" approach, so we use a database for user authentication instead of a simple XML-file as was documented in the official manual.

sudo apt install mariadb-server -y
sudo mysql_secure_installation

As a note, I didn't use unix_sockets, but a regular password. The rest I left at default arguments. Next, the guide was suggesting to download an older version of a MySQL/J connector (v8.0.26), but I felt brave, and download the latest version 9.1.0. The download procedure was different for me for some reason. I had to manually download file from the website.

sudo apt install ./mysql-connector-j_9.1.0-1ubuntu24.04_all.deb
sudo cp /usr/share/java/mysql-connector-j-9.1.0.jar /etc/guacamole/lib/

Next, I focused on the Apache Guacamole JDBC AUTH plugin.

sudo wget https://downloads.apache.org/guacamole/1.5.5/binary/guacamole-auth-jdbc-1.5.5.tar.gz
sudo tar -xf guacamole-auth-jdbc-1.5.5.tar.gz
sudo mv guacamole-auth-jdbc-1.5.5/mysql/guacamole-auth-jdbc-mysql-1.5.5.jar /etc/guacamole/extensions/

Now, it is time to configure our database.

sudo mysql -u root -p

These are the commands inside MariaDB.

MariaDB [(none)]> CREATE DATABASE guac_db;
MariaDB [(none)]> CREATE USER 'guac_user'@'localhost' IDENTIFIED BY 'password';
MariaDB [(none)]> GRANT SELECT,INSERT,UPDATE,DELETE ON guac_db.* TO 'guac_user'@'localhost';
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> EXIT;

After, we want to apply a schema.

cd guacamole-auth-jdbc-1.5.5/mysql/schema
cat *.sql | mysql -u root -p guac_db

Next, we should tell Guacamole how it should handle user data. We create a simple properties file.

sudo nvim /etc/guacamole/guacamole.properties

And, populate it with the configuration below.

# MySQL properties
mysql-hostname: 127.0.0.1
mysql-port: 3306
mysql-database: guac_db
mysql-username: guac_user
mysql-password: password

Restart all relevant services.

sudo systemctl restart tomcat guacd mysql

Now, the service should be accessible at port 8080/guacamole, and default login and password should be guacadmin. At this point, it is strongly recommended to create a new admin user and password and delete the default credentials. To do this, from the guacadmin profile click on Settings. Under the Edit User section, enter your new username and password. Then, under the Permissions section check the all boxes. When you are done, click Save. Now log out from the default user and log in back to Apache Guacamole with your new user created. Then, navigate to the settings, and users tab, and delete the guacadmin user. That’s it, you are done. From there, you can securely access your servers and computers.

References:

Virtualisation

The following command installed the required software to create a VM. The core are qemu libvirt. However, I use virt-manager GUI for easier management. The dmidecode package is one of the dependencies that is needed by virt-manager, I think. I know that somewhere along the journey, it would try to create a network which would use dnsmasq. The edk2-ovmf package is a UEFI firmware for virtual machines based on EDK II (EFI Development Kit). The swtpm package is needed to bypass TMP 2.0 requirement by Windows 10/11. When prompted, I selected the third option: qemu-full.

sudo pacman --sync qemu libvirt virt-manager edk2-ovmf dnsmasq dmidecode swtpm

Start the libvirt daemon.

sudo systemctl enable --now libvirtd
sudo systemctl enable --now virtlogd.socket

Activate the default network and make it persistent between bootings.

sudo virsh net-start default
sudo virsh net-autostart default

Either use polkit with polkit-gnome or add to the libvirt group.

sudo usermod -aG libvirt $USER
sudo apt install qemu-kvm libvirt-daemon libvirt-daemon-system bridge-utils virt-manager swtpm virtiofsd

# Usually, on Ubuntu 24.04.1, user is already part of `libvirt`, so we just need to add the user to the `kvm` group.
sudo adduser $USER kvm

Experimentally, to install QEMU/KVM with Virtual Manager, I have found that I don't have to install edk2-ovmf on Ubuntu because I could choose Secure Boot in virt-manager without it. However, on Arch Linux, it seems necessarily.

sudo dnf group install --with-optional virtualization

# Enable virtualisation service & permissions
sudo systemctl enable --now libvirtd
sudo usermod -aG libvirt,kvm,audio,video $(whoami)
groups user

Once the user is added to the groups, I would suggest rebooting. After reboot, we can check if the installation is correct by checking if there are any virtual machines on the system.

sudo virsh list --all

Or, we can check status of the libvirt daemon.

sudo systemctl status libvirtd

GPU Passthrough

I decided to make it into a separate section because this task is complex enough on its own.

The following command can tell what kernel driver is in use.

lspci | grep ' VGA ' | cut -d" " -f 1 | xargs -i lspci -v -s {}

The output should look something like this.

bd:00.0 VGA compatible controller: NVIDIA Corporation GA102GL [RTX A6000] (rev a1) (prog-if 00 [VGA controller])
    Subsystem: NVIDIA Corporation GA102GL [RTX A6000]
    Physical Slot: 7
    Flags: bus master, fast devsel, latency 0, IRQ 439, NUMA node 1, IOMMU group 30
    Memory at e9000000 (32-bit, non-prefetchable) [size=16M]
    Memory at 22bfe0000000 (64-bit, prefetchable) [size=256M]
    Memory at 22bff0000000 (64-bit, prefetchable) [size=32M]
    I/O ports at d000 [size=128]
    Expansion ROM at ea000000 [virtual] [disabled] [size=512K]
    Capabilities: <access denied>
    Kernel driver in use: nvidia
    Kernel modules: nvidiafb, nouveau, nvidia_drm, nvidia

A simple Bash script that will give you all the valuable IOMMU information, e.g. 10de:2882 or 10de:22be. The script is available at project/dotfiles/.local/bin/iommu and I usually install it at ~/.local/bin/iommu.

    #!/bin/bash

    for d in /sys/kernel/iommu_groups/*/devices/*; do
        n=${d#*/iommu_groups/*}; n=${n%%/*}
        printf 'IOMMU Group %s ' "$n"
        lspci -nns "${d##*/}"
    done

Enable IOMMU 20 in /etc/default/grub. Both Arch and Ubuntu will have this file if the installed OS uses GRUB to boot.

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt"
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on iommu=pt"

This tells the system: "Any device with PCI vendor:device ID matching 10de:1111 or 10de:1112 should be bound to vfio-pci."

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_iommu=on iommu=pt vfio-pci.ids=10de:2882,10de:22be"
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=on iommu=pt vfio-pci.ids=10de:2882,10de:22be"

Why the first method is better than the second method? Well, imagine the following situation.

  • gpu1: 10de:1111,10de:1112
  • gpu2: 10de:1111,10de:1112
  • gpu3: 10de:2221,10de:1112

We would like to pass gpu1 and gpu2 that share the same IDs to vfio-ci, and gpu3 should be untouched. Here's where it gets tricky, if gpu3 also includes components (like audio controllers) that share the same PCI IDs as those listed (e.g., 10de:1111 or 10de:1112), then they will be bound to vfio-pci too — even if the GPU itself doesn't match directly.

So, instead of using vfio-pci.ids, which uses IDs globally, use device-specific binding based on PCI addresses (Bus:Device.Function). Identify the full PCI addresses of the devices you want to passthrough (and only those). Create a script to bind specific devices, /usr/local/bin/vfio-bind.sh. Make sure your vfio-pci binding script runs early enough in the boot process to grab the devices before any other driver claims them. There are a few ways to do this on Ubuntu, but the cleanest and most robust is using a systemd service that runs at the right time—before device drivers initialize but after the necessary sysfs paths exist. Substitute 0000:01:00.0 0000:01:00.1 0000:02:00.0 0000:02:00.1 with your PCI addresses.

#!/bin/bash

# Load vfio-pci.
modprobe vfio-pci

# Use driver_override (cleaner way).
for dev in 0000:01:00.0 0000:01:00.1 0000:02:00.0 0000:02:00.1; do
  echo "vfio-pci" > /sys/bus/pci/devices/$dev/driver_override
done

# Bind the devices.
for dev in 0000:01:00.0 0000:01:00.1 0000:02:00.0 0000:02:00.1; do
  echo $dev > /sys/bus/pci/drivers/vfio-pci/bind
done

Make it executable:

sudo chmod +x /usr/local/bin/vfio-bind.sh

Create the systemd service at /etc/systemd/system/vfio-bind.service.

[Unit]
Description=Bind GPUs to vfio-pci
Before=basic.target
After=systemd-modules-load.service
DefaultDependencies=no

[Service]
Type=oneshot
ExecStart=/usr/local/bin/vfio-bind.sh

[Install]
WantedBy=basic.target

🔍 DefaultDependencies=no ensures it runs early, and basic.target is reached before most services or drivers load.

Enable and rebuild initramfs (optional but recommended).

sudo systemctl enable vfio-bind.service

If you're not using vfio-pci.ids in your GRUB anymore (good!), you don’t have to regenerate initramfs, but if you're still using any early-loading module configs, you might:

sudo update-initramfs -u

Reboot and confirm. After rebooting, confirm your devices are bound:

lspci -nnk | grep -A 3 -E 'VGA|Audio'

Look for your passthrough GPUs showing Kernel driver in use: vfio-pci.

For a successful GPU passthrough, I installed Ubuntu 24.04.1 without NVIDIA drivers using only nouveau.

Save the changes, and update grub to apply the changes. Following with a reboot.

sudo update-grub

Blacklist the GPUs by creating a configuration file.

sudo nvim /etc/modprobe.d/gpu-passthrough-blacklist.conf

Make it look like this.

blacklist nouveau
blacklist snd_hda_intel

Bind GPUs to VFIO by creating another configuration file.

sudo nvim /etc/modprobe.d/vfio.conf

Make it look like this.

options vfio-pci ids=XXXX:XXXX,YYYY:YYYY

XXXX:XXXX,YYYY:YYYY are model ids found by using the lspci -nnk | grep -e NVIDIA command. The ids are located at the end of the line. Shortly after, save and apply the changes.

sudo update-initramfs -u

The guide (source 1, source 2, source 3) has mentioned ACS kernel patch, which allows the system to split the hardware devices into separate IOMMU groups. What in turn should provide higher granularity over the devices we want to pass through. I skipped this step.

Once you've got all these options set, go ahead commit the changes. And, reboot.

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

After reboot, the output of dmesg | grep vfio indicated that the vfio module wasn't loaded during the boot. Logically, the selected PCI devices weren't used by vfio drivers. To circum nagivate this behaviour, I had to modify /etc/mkinitcpio.conf file.

UPDATE: it seems like vfio_virqfd module is built in vfio (source). Logically, I removed it from the MODULES list.

MODULES=(vfio_pci vfio vfio_iommu_type1)

I saved the file, and ran the following.

sudo mkinitcpio -p linux

File Sharing

When working with VirtualBox, to enable bridge network, it is a simple matter of changing networking adapter in the settings of the progam to bridge adapter with the real ethernet adapter selected below. However, VirtualBox doesn't let us do hardware passthrough or, at least, I didn't find a definite answer to this on the internet. Otherwise, VirtualBox is a very useful piece of software that I use from time to time to experiment with various OS and new software.

Moving to QEMU/KVM, to allow virtual machines to connect to outside world the host's network should be reconfigured. The swtpm package is reponsible for TPM emulation. I used it to satisfy Windows 11 installation requirement. The virtiofsd package is needed to share folders between the host and guests. The guides at 212223 suggested to install WinFSP on Windows and enable Virtio FS service. However, I had compatibility issues installing virtio-win-tools. They were disabling my mouse. Instead, I manually applied the driver in Device Manager by right-clicking on it and selecting Update driver option. As for the service, I manually recreated the path to the executable and copied it from the virtio-win CD-ROM, e.g. virtiofsd.exe and virtiofsd.pdb. The following commands were used to create the service.

sc create VirtioFsSvc binpath="C:\Program Files\Virtio-Win\VioFS\virtiofs.exe" start=auto depend="WinFsp.Launcher/VirtioFsDrv" DisplayName="Virtio FS Service"
sc start VirtioFsSvc

Networking

Then, we can create bridge interface. Although, the default NAT allows VMs to communicate with the host and each other. And, through port forwarding on the host, we can expose the VMs to the outside world.

nmcli con show
nmcli con add ifname br0 type bridge con-name br0
nmcli con add type bridge-slave ifname eth0 master br0
nmcli con mod br0 bridge.stp no
nmcli con down eth0
nmcli con up br0
nmcli device show
sudo systemctl restart NetworkManager.service

Although, creating bridges seems to be the most logical approach, sometimes, we can't use them. Therefore, port forwarding seems to be a viable alternative. Here is how to forward ports from host physical interface to virtual interface where the guest VMs reside.

iptables -t nat -I PREROUTING -p tcp -d HOST_IP --dport HOST_PORT -j DNAT --to-destination GUEST_IP:GUEST_PORT
iptables -I FORWARD -m state -d GUEST_IP/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

For now, I am using iptables-persistent to save/reload port forwarding rules between reboots.

sudo apt install iptables-persistent

The location is saves the rules in is /etc/iptables/rules.v4 and /etc/iptables/rules.v6. To save the rules and load them, I use the following commands. I had to wrap it around as a command for a bash because I need elevated privilages on both piping as well as saving commands.

sudo bash -c "iptables-save > /etc/iptables/rules.v4"

To setup static IP inside guest VM, we need to modify the netplan configuration file for the NetworkManager service.

network:
    version: 2
    renderer: NetworkManager
    ethernets:
        INTERFACE_NAME:
            dhcp4: false
            addresses:
                - STATIC_IP/24
            routes:
                - to: default
                 via: HOST_IP
            nameservers:
                addresses: [HOST_IP]

For example, INTERFACE_NAME=eth0, STATIC_IP=192.168.1.100, HOST_IP=192.168.1.1. Save the aforementioned in the /etc/netplan/01-network-manager-all.yaml file. And, apply the plan.

sudo chmod 700 /etc/netplan/01-network-manager-all.yaml
sudo netplan try

Then, we setup KVM to allow guest VMs to use bridge interface. Start from creating a file in an arbitrary location on the computer and name it host-birdge.xml.

<network>
    <name>host-bridge</name>
    <forward mode="bridge"/>
    <bridge name="br0"/>
</network>

Then execute these commands (as a user in the libvirt group):

virsh net-define host-bridge.xml
virsh net-start host-bridge
virsh net-autostart host-bridge

A mechanism to allow connections from outside.

sudo modprobe br_netfilter
sudo echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf

Create /etc/sysctl.d/10-bridge.conf.

# Do not filter packets crossing a bridge
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-arptables=0

Apply and check the config.

sudo sysctl -p /etc/sysctl.d/10-bridge.conf
sudo sysctl -a | grep "bridge-nf-call"

Configure the guest to use host-bridge. Open up the Virtual Machine Manager and then select the target guest. Go to the NIC device. The drop down for "Network Source" should now include a device called "Virtual netowrk 'host-bridge'". The "Bridge network device model" will be "virtio" if that's your KVM configuration's default. Select that "host-bridge" device.

Alternative to iptables could be nginx

It is possible to configure nginx as a reverse proxy server on a host machine. That way, all traffic could be forwarded to an appropriate guest machine. Furthermore, using OpenResty with Lua, it is possible to configure it to pull forwarding rules from a database like MySQL, PostgreSQL, etc. Building on top, a FastAPI server would update the rules via a simple REST API endpoint. At the end, a user-oriented web application could be developed using Rest.js, Vue.js, or AngularJS. That would constitute a fully automated user-friendly system for host-guest VM management.

References

Software RAID

From the internet search, the suggested tool is mdadm.

To install mdadm, run the following command in terminal.

sudo apt install mdadm

The following command will create the RAID 5 array.

sudo mdadm --create --verbose /dev/md0 --level=5 --raid-devices=3 /dev/sda /dev/sdb /dev/sdc

Format the RAID array and make it persistent.

clear; sudo mkdir -p /mnt/md0
sudo mount /dev/md0 /mnt/md0/
df -h -x devtmpfs -x tmpfs

Then, inside the host machine, we can setup something like this.

sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
sudo update-initramfs -u
sudo echo '/dev/md0 /box ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab

References: * The 1/2 guide * The 2/2 guide

BIOS RAID

Set the SATA from AHCI to RAID.

Created volume under Advanced/Intel(R) VROC sSATA Controller/Create RAID Volume.

Unfortunately, my configuration doesn't support Intel(R) VROC sSATA Controller. I will have to use software RAID.

CPU: Intel(R) Xeon(R) Silver 4410Y
Generation: 4th "Sapphire Rapids"
Chipset: Intel® C621A Chipset

References: * Intel® Virtual RAID on CPU (Intel® VROC) Operating Systems Support List * Release Notes Intel® Virtual RAID on CPU (Intel® VROC) for Linux* * Intel® Xeon® Silver 4410Y Processor * Intel® Virtual RAID on CPU (Intel® VROC) Linux* Driver for Intel® Server Boards and Systems Based on Intel® 621A Chipset * GPU SuperServer SYS-740GP-TNRT

Troubleshooting

Blackscreen using gdm3 on Ubuntu with Wayland

On Ubuntu 24.04, the gdm3 display manager by default runs on Wayland. However, sometimes, e.g. ast driver, doesn't work with Wayland. To switch to Xorg uncomment WaylandEnable=false line by modifying /etc/gdm3/custom.conf configuration file. Save the file and reboot. This is the only working solution I have found so far.

I have tried many things, such as reinstalling by removing gdm3. I switched to tty3, using Ctrl+Alt+F3 and stopped the display manager service, i.e. gdm3, using commands below. Stopping gdm and display-manager.service act as aliases and, in my opinion, it is better to stop the original service. I have tried both removing then rebooting, as well as, removing without rebooting. Also, I tried with and without stopping the gdm3 service. Unfortunately, it didn't help.

sudo systemctl stop gdm3
sudo apt remove gdm3
sudo apt install gdm3.

I have tried reinstalling by purging gdm3. I did the same experiments as with removing. Initially, when I tried to stop the service, purge it, and install it without rebooting the display manager started to work. Happily after, I logged into my user account and all seemed to be working. However, when I have rebooted, the problem came back.

sudo systemctl stop gdm3
sudo apt purging gdm3
sudo apt install gdm3.

FAQ

How to develop the hardcode project

Warning

MkDocs 2.0 removed the plugin support 2425. Therofore, Material for MkDocs wouldn't work anymore. The developers of Material for MkDocs created Zensical 26 and encourage it as a future of Material for MkDocs.

git clone git@github.com:couper64/hardcode.git
cd hardcode
conda create -yn hardcode python=3
conda activate hardcode
pip install -r requirements.txt
conda activate hardcode

Finally, after the environment is set and Python packages are installed for the local development. To run the public documentation.

zensical serve -f public/mkdocs.yml

And, to run the private documentation.

zensical serve -f private/mkdocs.yml

From this point on, the changes should immediately update every time they are saved.

Note

For completeness sake, below is the command to build the static website, but it isn't needed in our use case.

zensical build -f public/mkdocs.yml
zensical build -f private/mkdocs.yml

What are the base-devel Dependencies

List of dependencies that base-devel brings with itself. To highlight that base-devel is optional, at least in theory, I put it in the post-installation section Arch Linux Post Installation.

  • archlinux-keyring
  • autoconf
  • automake
  • binutils
  • bison
  • debugedit
  • fakeroot
  • file
  • findutils
  • flex
  • gawk
  • gcc
  • gettext
  • grep
  • groff
  • gzip
  • libtool
  • m4
  • make
  • pacman
  • patch
  • pkgconf
  • sed
  • sudo
  • texinfo
  • which

How to Bypass the Networking Requirement during the Windows 11 Installation

Shift + F10 to open up Windows Command Prompt.

oobe\bypassnro

The system should reboot and, at the step where it prompts the user to connect to the internet, the I don't have internet button should be appear.

What to Install

  1. Timeshift to backup & restore on Linux.
  2. Remmina to connect to a remote desktop on Linux. Remote Desktop Connection on Windows.
  3. Firefox to access the internet.
  4. KeePassXC to manage passwords.
  5. rclone to use Google Drive to share files.
  6. mc, nnn to manage files.
  7. nvim, VS Code to edit text.

How to Reject DHCP Service using NetworkManager

Create and edit /etc/NetworkManager/dispatcher.d/10-block-bad-dhcp to reject secondary DHCP service on a Linux that uses NetworkManager. Set the eno1 argument according to the available adapter name.

#!/bin/bash

IFACE="$1"
STATUS="$2"

logger "NetworkManager: Triggered on $IFACE with status $STATUS"

if [ "$IFACE" = "eno1" ] && [[ "$STATUS" == "up" || "$STATUS" == "dhcp4-change" ]]; then
    IP=$(ip -4 addr show "$IFACE" | awk '/ inet / {print $2}' | cut -d/ -f1)
    if [[ "$IP" == 192.168.* ]]; then
        logger "NetworkManager: Blocking DHCP lease from $IP on $IFACE"
        ip addr flush dev "$IFACE"
        nmcli device disconnect "$IFACE"
        sleep 2
        nmcli device connect "$IFACE"
    else
        logger "NetworkManager: Accepted $IP on $IFACE"
    fi
fi

exit 0

How to develop the hardcode project

git clone git@github.com:couper64/hardcode.git
cd hardcode

conda create -yn hardcode python=3 conda activate hardcode pip install -r requirements.txt

conda activate hardcode

Finally, after the environment is set and Python packages are installed for the local development. To run the public documentation.

mkdocs serve -f public/mkdocs.yml

And, to run the private documentation.

mkdocs serve -f private/mkdocs.yml

From this point on, the changes should immediately update every time the changes are saved.

Note

For completeness sake, below is the command to build the static website, but it isn't needed in our use case.

mkdocs build -f public/mkdocs.yml
mkdocs build -f private/mkdocs.yml

How to resize KVM virtual disk size

Resize the virtual disk size.

sudo qemu-img resize /data/kvm/<vm_name> +10G

Check if the virtual disk has been resized.

sudo qemu-img info /data/kvm/<vm_name>

This gives the path to the *.qcow2 file which is used as an argument to the resizing command.

sudo virsh domblklist <vm_name>

Make sure that there are no snapshots otherwise resizing won't work.

sudo virsh snapshot-list <vm_name>

Remove them using this command.

sudo virsh snapshot-delete <vm_name> <snapshot_name>

Instructions are based on this guide.

How to Rofi

Rofi: https://github.com/davatorium/rofi Themes: https://github.com/adi1090x/rofi

How to Install and Use rclone

To install rclone, use the command sudo pacman --sync rclone on Arch.

To mount a Google Drive remote with rclone, use the command rclone mount remote_name: /path/to/mountpoint replacing remote_name with your configured remote and /path/to/mountpoint with the local folder.

How to install surf on Arch Linux

Install dependencies 2728.

sudo pacman -S gcr webkit2gtk-4.1
git clone git://git.suckless.org/surf ~/.local/src/surf

Use the following to open a webpage.

surf http://your-url

During operation, use ctrl-g to enter a new URL 29. An interesting documentation could be found here at 30 as well.

How to enable TPM on KVM

sudo pacman -S swtpm

Then in virt-manager 3132:

  1. Allow customization before installation by checking the box. You can also configure the VM network. For this guide I have used a bridged network.
  2. On the overview windows, select add hardware.
  3. Add TPM 2.0 and make the settings as shown. Then click Finish to apply the changes.
    1. Model: TIS
    2. Backend: Emulated device
    3. Version: 2.0
  4. Just before you begin the installation, remember to change Firmware. Apply the changes and begin the installation.
    1. Firmware: *_secboot.fd

How to resize KVM virtual disk size

Resize the virtual disk size.

sudo qemu-img resize /data/kvm/<vm_name> +10G

Check if the virtual disk has been resized.

sudo qemu-img info /data/kvm/<vm_name>

This gives the path to the *.qcow2 file which is used as an argument to the resizing command.

sudo virsh domblklist <vm_name>

Make sure that there are no snapshots otherwise resizing won't work.

sudo virsh snapshot-list <vm_name>

Remove them using this command.

sudo virsh snapshot-delete <vm_name> <snapshot_name>

Instructions are based on this guide.

How to Clean Wipe a Drive

sudo dd if=/dev/zero of=/dev/sdX bs=4M status=progress

How to Use Timeshift

sudo blkid
lsblk
sudo nano /etc/fstab
/dev/disk/by-uuid/your-partition-uuid /mnt/your-mount-point ext4 defaults 0 0

Or,

/dev/disk/by-uuid/your-partition-uuid /mnt/your-mount-point ext4 noauto 0 0
sudo timeshift --create
sudo timeshift --list
sudo timeshift --restore

I consider tools like Back in Time, Pike Backup, or Deja Dup not user friendly because I keep facing permissions-related errors with my home directory. Therefore, I ditch the idea of using two tools to backup my stuff and focus on Timeshift only for now.

How to manage VFAT devices on Linux

VFAT is essentially FAT32 with long filename support.

sudo pacman -S dosfstools

This command will install tools such as mkfs.vfat and other commands that are used below.

sudo dosfslabel /dev/sda1 LABEL

This command will set a label to the parition. It will show up in File Explorer on Windows.

sudo dosfsck -n /dev/sda1

This command will check if there are any dirty flags.

sudo dosfsck -a /dev/sda1

This command will try to fix any issues with the partition including dirty flags.

Note

The warning about the boot sector differences was solved using this command: sudo mkfs.vfat -F 32 /dev/sda1, i.e. simply reformatting the partition.

sudo mount -t vfat -o uid=$(id -u),gid=$(id -g) /dev/sda1 /mnt

This command will mount device with the same ownership as the user. Otherwise, it is root.

sudo fuser -m /dev/sda1
sudo umount /dev/sda1
sudo sync
sudo eject /dev/sda

These commands are used to safely remove device from the computer.

How to enable Windows-style Input Source switching

Prior to these remove the second Input Source from the GNOME keyboard settings. It doesn't cycle through the Input Sources but rather through the layouts within the Input Source. The way it behaves with multiple Input Sources is that it will cycle through all the internal layouts (that's my understanding of it).

# Run this to set Ctrl + Shift.
gsettings set org.gnome.desktop.input-sources xkb-options "['grp:ctrl_shift_toggle']"
# Run this to set Ctrl + Shift.
gsettings set org.gnome.desktop.input-sources xkb-options "['grp:alt_shift_toggle']"

# Run this to verify.
gsettings get org.gnome.desktop.input-sources xkb-options

How to hide and prevent the Windows drive from auto-mounting in Ubuntu

# Find the drive.
lsblk

# Check if the drive is in fstab.
cat /etc/fstab

# Remove from fstab (if present).
sudo nvim /etc/fstab

Disable auto-mount in file manager: Settings → Ubuntu Desktop → Dock → Configure Dock Behavior → uncheck "Show External Drives".

# Unmount the current mount.
sudo umount /path/to/mount/point

# Verify.
lsblk

How to auto-mount an internal hard drive to share between operating systems.

# Find UUID and filesystem type.
sudo blkid /dev/sda1

# The recommended mount root is /mnt.
sudo mkdir -p /mnt/data

# To auto-mount add the UUID with the right arguments.
sudo nvim /etc/fstab

# UUID=<your-uuid-here> /mnt/data ntfs-3g defaults,nofail,uid=1000,gid=1000 0 2

# Test.
sudo mount /mnt/data

# Verify.
df -h

Appendix A

To hide the unmounted volumes, the setting is located at Settings/Ubuntu Desktop/Dock/Configure Dock Behavior/Include Unmounted Volumes.

Command to recursively delete files.

find . -type f -name '*.o' -exec rm {} +

Command to recusrively clone a repository.

git clone --recurse-submodules git://github.com/foo/bar.git

Command to kill Ngrok process.

kill -9 "$(pgrep ngrok)"

Commands to run Ngrok in the background.

clear ; ngrok http http://localhost:8080 > /dev/null &
clear ; export WEBHOOK_URL="$(curl http://localhost:4040/api/tunnels | jq ".tunnels[0].public_url")"
clear ; echo $WEBHOOK_URL

Commands to install and setup Ngrok. First, sign up (in) and retrieve the authorisation token.

snap install ngrok
ngrok config add-authtoken <token>

Another installation command, because when I installed ngrok through snap, it couldn't start a service, but when installed through apt, it worked.

curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \
  sudo gpg --dearmor -o /etc/apt/keyrings/ngrok.gpg && \
  echo "deb [signed-by=/etc/apt/keyrings/ngrok.gpg] https://ngrok-agent.s3.amazonaws.com buster main" | \
  sudo tee /etc/apt/sources.list.d/ngrok.list && \
  sudo apt update && sudo apt install ngrok
sudo ngrok service install --config /path/to/config.yml
sudo ngrok service start

Although, all the messages were indicating "ok", it didn't work for me. Here is the config file.

authtoken: <your-auth-token>
tunnels:
    default:
        proto: http
        addr: 8080

  1. https://archlinux.org/download/ 

  2. https://ubuntu.com/download/desktop 

  3. https://www.microsoft.com/en-gb/software-download/windows11 

  4. https://dwm.suckless.org/ 

  5. https://davidtsadler.com/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/ 

  6. https://wiki.archlinux.org/title/Xinit 

  7. https://ratfactor.com/dwm 

  8. https://dwm.suckless.org/patches/ 

  9. https://tools.suckless.org/dmenu/patches/ 

  10. https://st.suckless.org/patches/ 

  11. https://www.reddit.com/r/suckless/comments/x1uk8k/how_to_know_which_version/ 

  12. https://tools.suckless.org/dmenu/ 

  13. https://davidtsadler.com/posts/arch/2020-08-17/installing-st-dmenu-dwm-in-arch-linux/ 

  14. https://unix.stackexchange.com/questions/20953/difference-between-make-install-and-sudo-make-install 

  15. https://www.anaconda.com/docs/getting-started/miniconda/install#linux 

  16. https://stackoverflow.com/questions/4565700/how-to-specify-the-private-ssh-key-to-use-when-executing-shell-command-on-git 

  17. https://www.youtube.com/watch?v=Z3ELWci34cM&list=PLMQHMcNi6octV4IJTr7oAWeEBFgwu5HL8&index=7 

  18. https://looking-glass.io/docs/B7 

  19. https://gist.github.com/Ruakij/dd40b3d7cacf5d0f196d1116771b6e42 

  20. https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit 

  21. https://www.debugpoint.com/kvm-share-folder-windows-guest/ 

  22. https://www.debugpoint.com/share-folder-virt-manager/ 

  23. https://askubuntu.com/questions/1498317/trying-to-share-a-directory-that-is-located-in-another-hard-drive-connected-to 

  24. https://squidfunk.github.io/mkdocs-material/blog/archive/2026/ 

  25. https://github.com/mkdocs/mkdocs/discussions/3621 

  26. https://github.com/zensical/zensical 

  27. https://bbs.archlinux.org/viewtopic.php?id=268489 

  28. https://bbs.archlinux.org/viewtopic.php?id=271511 

  29. https://surf.suckless.org/ 

  30. https://troubleshooters.com/linux/surf.htm 

  31. https://computingforgeeks.com/enable-tpm-on-kvm-and-install-windows/ 

  32. https://insights.ditatompel.com/en/tutorials/run-windows-11-tpm-and-secure-boot-on-kvm/