Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hello and welcome to my wiki c:

Here you can find cheat-sheets, cookbooks and small guides that I wrote (mostly) for myself about various topics.

I can’t garantee that you’ll find anything usefull here. I write things that I find useful for myself.

Fixes for weird problems I might need again

Sound crackling and suspending after a short time (when idle)

Note: I don’t know how to fix the crackling when the intel_hda_controller comes online. This is only a mitigation of the issue.

It makes that sound when going idle, because of the power saving feature. With pacmd list-sinks it’s possible to see:

state: SUSPENDED
suspend cause: IDLE

echo 0 > /sys/module/snd_hda_intel/parameters/power_save will disable the power saving feature, thus avoiding the crackling/popping every time the sink comes on/off.

To make it persistent, I create a file in /etc/modprobe.d called audio_pop.conf with the contents options snd_hda_intel power_save=0.

Weird occasional latency problems with Bluetooth devices

This seems to be an issue with the power manager. I fixed it by putting the following on a bluetooth_latency_fix.conf in /etc/modprobe.d (and restarting):

options usbcore autosuspend=-1
options btusb enable_autosuspend=0

Application not recognizing the display environment

Edit /etc/pam.d/system-login as sudo and append type=x11:

-session optional pam_systemd.so

becomes

-session optional pam_systemd.so type=x11

Mouse wouldn’t move camera in Dark Souls II (wine/proton)

When running Dark Souls II in proton, the mouse would be recognised and work on the menus, but I wasn’t able to move the camera with it (the arrow keys moved the camera and the left and right mouse buttons performed attacks).

To fix it, I ran the following command inside the profile’s pfx directory:

printf "REGEDIT4\n[HKEY_CURRENT_USER\Software\Wine\\\Explorer]\n\"Desktop\"=\"Default\"\n[HKEY_CURRENT_USER\Software\Wine\\\Explorer\Desktops]\n\"Default\"=\"1920x1080\"\n[HKEY_CURRENT_USER\Software\Wine\\X11 Driver]\n\"GrabFullscreen\"=\"N\"" >> temp.reg && find -name "pfx" | xargs readlink -f | xargs -I{} env WINEPREFIX={} regedit temp.reg && rm temp.reg

Tor service failing to initialize

I don’t know which update made this starting to happen, but the Tor hidden service configuration on my server’s website started requiring a new option (on Arch). It was now necessary to set User tor on /etc/tor/torrc. This user can probably change between distributions/install processes/use cases.

GRUB menu not showing after fresh installing

In the case of some motherboard manufacters/models (in my case MSI), the UEFI firmware requires the bootable file at a known location before they will show UEFI NVRAM boot entries. There are 2 solutions at the time of writing:

The first solution is to install GRUB at the default/fallback boot path:

grub-install --target=x86_64-efi --efi-directory=esp --removable

Alternatively, you can move an already installed GRUB EFI executable to the default/fallback path:

mv esp/EFI/grub esp/EFI/BOOT and then mv esp/EFI/BOOT/grubx64.efi esp/EFI/BOOT/BOOTX64.EFI

Cool stuff for steam deck

Pages

Config tools

Tools

Games

Trying out

Programming

Logging tips

Inspiration

Log after (and maybe before)

// don't do that
log.info("Making request to REST API")
restClient.makeRequest()
 
// do that
// log.info("Making request to REST API")  // can include this
restClient.makeRequest()
log.info("Made request to REST API -> OK")

Separate parameters and messages

// don't do that
restClient.makeRequest()
log.info("Made request to {} on REST API.", url)
 
// do that
restClient.makeRequest()
log.info("Made request to REST API. [url={}]", url)

Distinguish between WARNING and ERROR

try {
    restClient.makeRequest()
    log.info("Made request to REST API. [url={}]", url)
} catch(e: UnauthorizedException) {
    log.warn("Request to REST API was rejected because user is unauthorized. [url={}, result={}]", url, result)
} catch(e: Exception) {
    log.error("Request to REST API failed. [url={}, exception={}]", url, exception)
}

Don’t keep unneeded private user info in logs

Don’t need the names/emails in the logs.

DEBUG | Saved user to newsletter list. [user="Thomas", email="thomas@tuhrig.de"]
DEBUG | Send welcome mail. [user="Thomas", email="thomas@tuhrig.de"]
INFO  | User registered for newsletter. [user="Thomas", email="thomas@tuhrig.de"]
DEBUG | Started cron job to send newsletter of the day. [subscribers=24332]
INFO  | Newsletter send to user. [user="Thomas"]
INFO  | User unsubscribed from newsletter. [user="Thomas", email="thomas@tuhrig.de"]

Mockito Verify

Verify simple invocation on mock

List<String> mockedList = mock(MyList.class);
mockedList.size();
verify(mockedList).size();

Verify number of interactions with mock

List<String> mockedList = mock(MyList.class);
mockedList.size();
verify(mockedList, times(1)).size();

Verify no interaction with the whole mock occurred

List<String> mockedList = mock(MyList.class);
verifyZeroInteractions(mockedList);

Verify no interaction with a specific method occurred

List<String> mockedList = mock(MyList.class);
verify(mockedList, times(0)).size();

Verify there are no unexpected interactions – this should fail:

List<String> mockedList = mock(MyList.class);
mockedList.size();
mockedList.clear();
verify(mockedList).size();
verifyNoMoreInteractions(mockedList);

Verify order of interactions

List<String> mockedList = mock(MyList.class);
mockedList.size();
mockedList.add("a parameter");
mockedList.clear();

InOrder inOrder = Mockito.inOrder(mockedList);
inOrder.verify(mockedList).size();
inOrder.verify(mockedList).add("a parameter");
inOrder.verify(mockedList).clear();

Verify an interaction has not occurred

List<String> mockedList = mock(MyList.class);
mockedList.size();
verify(mockedList, never()).clear();

Verify an interaction has occurred at least certain number of times

List<String> mockedList = mock(MyList.class);
mockedList.clear();
mockedList.clear();
mockedList.clear();

verify(mockedList, atLeast(1)).clear();
verify(mockedList, atMost(10)).clear();

Verify interaction with exact argument

List<String> mockedList = mock(MyList.class);
mockedList.add("test");
verify(mockedList).add("test");

Verify interaction with flexible/any argument

List<String> mockedList = mock(MyList.class);
mockedList.add("test");
verify(mockedList).add(anyString());

Verify interaction using argument capture

List<String> mockedList = mock(MyList.class);
mockedList.addAll(Lists.<String> newArrayList("someElement"));
ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);
verify(mockedList).addAll(argumentCaptor.capture());
List<String> capturedArgument = argumentCaptor.<List<String>> getValue();
assertThat(capturedArgument, hasItem("someElement"));

Linux

Install flutter

yay -Syu flutter android-sdk-cmdline-tools-latest android-sdk-platform-tools android-sdk-build-tools android-platform
sudo chown -R $USER /opt/flutter
sudo setfacl -R -m u:$USER:rwx /opt/android-sdk
sudo setfacl -d -m u:$USER:rwX /opt/android-sdk
flutter doctor --android-licenses # aceita tudo com y
flutter run -d web-server # inside the flutter_src directory

Shell Parameter Expansion

https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

$

If parameter is unset or null, the expansion of word is substituted.\ Otherwise, the value of parameter is substituted.

$

If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.

$

If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.

$

If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.

${parameter:offset} and $

$ a="12345"
$ echo ${a:1:2}
23
$ echo ${a:1:-1}
234

${parameter#word} and $

  • # deletes the shortest match.
  • ## deletes the longest match.
$ a="1231245"
$ echo ${a#*1}
231245
$ echo ${a##*1}
245

${parameter%word} and $

Same as above but matches from end (trailling).

$

Substitutes first pattern match with string.

${parameter^pattern} and ${parameter^^pattern} and ${parameter,pattern} and $

  • ^ makes upper-case.
  • , makes lower-case.

Makes all matched characters in pattern upper/lower-case. Pattern should now atempt to match more than 1 character.

OBS stream screen in camera

Sometimes streaming the screen in an application sucks and is very limited. We can feed a stream to a virtual camera (V4L2 loopback device) in order to have more control in streams.

Note: This guide is for Linux. In windows, you can use the virtual camera feature. It comes builtin since version 26.0.0, but can also be added with the plugin obs-virtual-cam.

Kernel module

To create one of these devices (in Linux), we have to install and load a kernel module: v4l2loopback.

Instructions:

  • git clone https://github.com/umlaeute/v4l2loopback.git
  • cd v4l2loopback
  • make && sudo make install
  • sudo depmod -a
  • sudo modprobe v4l2loopback

By this point, you should have a new /dev/videoN device, where N is an integer. This device will probably be the last one of the ones you have.

This kernel module can be unloaded by doing sudo rmmod v4l2loopback. You might have to load it each time you turn on your pc and want to use it: sudo modprobe v4l2loopback.

The loading command can take some arguments to specify the number of devices created and the device ID’s of these devices, e.g.:

  • modprobe v4l2loopback devices=4
  • modprobe v4l2loopback video_nr=3,4,7 NOTE: the exclusive_caps=1 option can be useful to use the device in application that refuse to open devices that have capabilities besides CAPTURE, e.g.: Chrome/WebRTC.

OBS plugin

The obs-v4l2sink plugin provides the capability to OBS studio to output to V4L2 devices.

Instructions (depends on qtbase5-dev and libobs-dev):

  • git clone --recursive https://github.com/obsproject/obs-studio.git
  • git clone https://github.com/CatxFish/obs-v4l2sink.git
  • cd obs-v4l2sink
  • mkdir build && cd build
  • cmake -DLIBOBS_INCLUDE_DIR="../../obs-studio/libobs" -DCMAKE_INSTALL_PREFIX=/usr ..
  • make -j4
  • sudo make install

Note: We cloned obs-studios’s source code in order to links with its libraries during compilation.

Now, we can go to OBS and open the menu entry Tools > V4L2 Video Output. This will open a menu where we can select the video format and the path to the video device (/dev/videoN). After that, we just have to press start and our current OBS scene will be streamed on that device.

How to format an SD-Card in Linux (cli)

# This command will list all block devices. We need to know which one is the SD-card.
# Should be /dev/sdb.
lsblk

# The numbers in the end indicate the different partitions.
# For example, there might be two partitions: /dev/sdb1 and /dev/sdb2.
# To solve this, we need to delete all partitions and create only one.
sudo fdisk /dev/sdb

# fdisk will open a console.
# Firstly, we enter the command p. This command will list the partitions.
# Secondly, we enter the command d. This command deletes 1 partition. If we want to delete two partitions, we should run this command two times.
# Thirdly, we enter the command n. This command creates 1 partition. We can accept all the default options, which creates one partition with the disk's full size.
# Finally, we enter the command w. This command writes the changes to the disk (it is like committing to a database).

# Now that we have a clean partition, we just need to create the file system.
# For exFAT we use the following command:
mkfs.exfat /dev/sdb1

# If the previous command does not exist, we need to install it:
sudo pacman -Syu exfatprogs

Change user’s uid/gid

This can be useful when dealing with NFS shares.

How to change

If you’re lucky, it can be as simple as running the following as root:

usermod -u $uid -g $gid $user

The problem is that you can only change the UID if the user has no processes running. This proves difficult with systemd and ssh’ing into the machine with that user.

Cool solution

Use the at command. This command can run schedule the execution of a command. I recommend scheduling the usermod command to 5 mins into the future and then shutting down that machine. Then you just have to wait 6 or 7 minutes with machine off and turn it back on again:

sudo at "+5 minutes"
/use/sbin/usermod -u $uid -g $gid $user

Note: you need to use the full path of the executables here.

File owners

The usermod command only takes care of changing the UID and GID of files in the user’s home directory. I recommend using find to fix any missed files.

find / -uid $old_uid -exec chown -h $new_uid {} +
find / -gid $old_gid -exec chgrp -h $new_gid {} +

Let’s encrypt manual certs with cerbot

This assumes you’re using nginx. If you’re using a different server software (e.g.: apache), just change the parts that use nginx to what you’re using.

Note: The official website provides very useful/comprehensive instructions certbot.

Install certbot

apt-get certbot, pacman -Syu certbot, etc…\ You might also want to install your server software plugin (if available), e.g.: pacman -Syu certbot-nginx.

Note: use sudo nginx -s reload or sudo systemctl restart nginx to reload nginx.

Run certbot

  • For normal domains, e.g. example.com, there’s no need to do a manual setup. Run sudo certbot --nginx or whatever the site instructs. Stop reading here.
  • For wildcard domain, e.g. *.example.com, the automatic process requires dns service support. This isn’t available for most, but you should check for new so it can be done automatically. Otherwise, we need a manual setup. Run certbot certonly --manual --preferred-challenges dns.

Follow certbot instruction

  • Enter email address.
  • Read and accept terms.
  • Choose if you want to share email adress with EFF.
  • Enter domain: e.g. *.example.com, example.com (probably want both, remeber the mistake?).
  • Go to DNS settings (e.g. vultr dns) and create a new DNS TXT record like so:
    • Name: _acme-challenge
    • Value/Data: as1…5&b (given value)
    • TLS: as low as available.
  • Press continue on certbot.
  • After process finishes, delete created DNS TXT record.

NOTE: The renewal of the TLS certificate will be manual (see next section).

Manual renew

We can just enter the same command we ran to create the certificate(s). Certbot will detect the certs are due for renewal and give us the new dns challenge. We can also create a script to set up the dns/http challenge and use certbot renew with the script.

Automatic renew

Run certbot renew for interactive prompts (only if you didn’t have to do a manual setup).

Delete certs

Run certbot delete for interactive prompts.

Tell nginx to use the certificates

  • vim /etc/nginx/sites-available/example.com.
  • Change port 80 to port 443 (for https):
  listen 443 default_server;
  listen [::]:443 default_server;
  • Add to the file:
server {
  ... # previous stuff that' already there

  ssl on;
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
```bash

  * Test config with: `sudo nginx -t`.
  * Restart nginx with: `sudo systemctl restart nginx`.

NOTE: might also want to unblock https in firewall config, e.g. `ufw allow 'Nginx HTTPS`' or run `sudo ufw app list` to find possible entries.

## Redirect http to https

  * `vim /etc/nginx/sites-available/example.com` (yes the same file).
  * add new server block:

```bash
server {
  # redirect all http to https
  server_name example.com www.example.com;
  return 301 https://example.com$request_uri;
}

This listens to port 80 (https) and redirects to port 443 (https).

Performance stuff

Don’t forget to test config (sudo nginx -t) and restart nginx (sudo systemctl restart nginx) after any of this changes.

Enable HTTP/2

Enables parallel file requesting. Should change server block to:

server {
   listen 443 http2 default_server;
   listen [::]:443 http2 default_server;

   ... # all other content
}

Enable gzip compression

Decrease file size during transmission. WARNING there can be security vulnerabilities with gzip compression.\ Should add to server block:

server {
   ... # previous content

   gzip on;
   gzip_types application/javascript image/* text/css;
   gunzip on;
}

This will ensure that javascript files, images, and CSS files are always compressed.

Enable client-side caching

Some files don’t ever change, or rarely change, so there’s no need to have users re-download the latest version. You can set cache control headers to provide hints to browsers to let them know what files they shouldn’t request again.

server {
   ... # after the location / block

   location ~* \.(jpg|jpeg|png|gif|ico)$ {
       expires 30d;
    }
    location ~* \.(css|js)$ {
       expires 7d;
    }
}

Dynamically route subdomains to folders

If you have subdomains, chances are you don’t want to have to route every subdomain to the right folder. It’s a maintenance pain. Instead, create a wildcard server block for it, routing to the folder that matches the name:

server {
       server_name ~^(www\.)(?<subdomain>.+).jgefroh.com$ ;
       root /var/www/jgefroh.com/$subdomain;
}
server {
        server_name ~^(?<subdomain>.+).jgefroh.com$ ;
        root /var/www/jgefroh.com/$subdomain;
}

Smartcard reader on linux

Most information was taken from ArchWiki.

Needed packages

Install ccid and opensc: pacman -Syu ccid opensc

Start/Enable the systemd unit: systemctl start pcscd.service or systemctl enable --now pcscd.service

Test it

Install pcsc-tools, and call pcsc_scan. Try plugging/unplugging the card reader and a card, and examine the terminal output.

Note: you can uninstall pcsc-tools now.

Firefox support

The browser needs to set the new security-related device. Open the Security Devices page (reach it via Preferences > Privacy & Security > Certificates > Security Devices...), then click Load and set the Module Name to CAC Module and module filename to /usr/lib/opensc-pkcs11.so.

Plugin Autenticação.gov pt

Install plugin-autenticacao-gov-pt from the AUR. For other distributions see here:

  • Debian/Ubuntu: dpkg -i plugin-autenticacao-gov.deb
  • Fedora: dnf install plugin-autenticacao-gov_fedora.rpm
  • RedHat/CentOS: yum install plugin-autenticacao-gov_rhel
  • OpenSuse: zypper install plugin-autenticacao-gov_opensuse.rpm

Using the Qudelix 5k app on Linux

Qudelix 5k is a bluetooth DAC AMP. It has an app/extension for Chrome, so you can customize settings on Windows/MacOS. If you try using it on Linux, it won’t work (device won’t be detected).

Solution

Add the following udev rules (/etc/udev/rules.d/100-qudelix-5k.rules):

SUBSYSTEM=="usb", ATTRS{idVendor}=="0a12", MODE="0660", GROUP="uucp", TAG+="uaccess"
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0660", , GROUP="uucp", TAG+="uaccess", TAG+="udev-acl"

Source

Side note

Also fixes permission issues for solaar (logitech stuff):

KERNEL=="hidraw*", MODE="0660", GROUP="uucp"

Mount disk image files

This comes up when I want to access old backups of my Raspberry Pi’s sd card images (obtained through dd). The problem of mounting this comes from the existance of multiple partitions. I do the following so I don’t have to deal with inputing the partition’s offset:

sudo losetup -P /dev/loop0 steamdeck.img
sudo mount /dev/loop0p8 steamdeck/

This would mount the 8th partition of my steam deck’s disk image on the steamdeck/ directory.

After using the losetup command, you can use lsblk or fdisk to inspect the partitions’ sizes and select the one you want.

Hyprlock automation commands

Command to lock screen

loginctl lock-session

Command to unlock screen

pkill -USR1 hyprlock

Install Android SDK (without Android studio)

Java

Check java version (must be 8 or 10)

java -version

Install Java8

sudo pacman -S jre8-openjdk

Android SDK

Install Android SDK

yay -S android-sdk android-sdk-platform-tools android-sdk-build-tools
yay -S android-platform

Install Android SDK

yay -S android-sdk android-sdk-platform-tools android-sdk-build-tools
yay -S android-platform

Permissions

sudo groupadd android-sdk
sudo gpasswd -a $USER android-sdk
sudo setfacl -R -m g:android-sdk:rwx /opt/android-sdk
sudo setfacl -d -m g:android-sdk:rwX /opt/android-sdk

Add to PATH Put these lines on .bashrc

export ANDROID_SDK_ROOT='/opt/android-sdk'
export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools/
export PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin/
export PATH=$PATH:$ANDROID_ROOT/emulator
export PATH=$PATH:$ANDROID_SDK_ROOT/tools/

Accept licenses

cd /opt/android-sdk/tools/bin
yes | sdkmanager --licenses
  • If still missing license acceptance or missing package error

Install package:

sdkmanager "platforms;android-28"

Gradle

Download gradle zip from [[https://gradle.org/releases]]

Unzip downloaded zip:

mkdir /opt/gradle
unzip -d /opt/gradle gradle-7.2-bin.zip

Add to path

export PATH=$PATH:/opt/gradle/gradle-7.2/bin

Stopping Wine from creating filetype associations

By default, Wine creates associations to open some filetypes. I don’t like this.

Disabling it

Add HKCU,"Software\Wine\FileOpenAssociations","Enable",2,"N" to the [Services] section of /usr/share/wine/wine.inf

Install/upgrades to the Wine package will override this file. We can create a pacman hook to reapply this change for us:

[Trigger]
Operation = Install
Operation = Upgrade
Type = Path
Target = usr/share/wine/wine.inf

[Action]
Description = Stopping Wine from hijacking file associations...
When = PostTransaction
Exec = /bin/sh -c '/usr/bin/grep -q "HKCU,\"Software\\\Wine\\\FileOpenAssociations\",\"Enable\",2,\"N\"" /usr/share/wine/wine.inf || /usr/bin/sed -i "s/\[Services\]/\[Services\]\nHKCU,\"Software\\\Wine\\\FileOpenAssociations\",\"Enable\",2,\"N\"/g" /usr/share/wine/wine.inf'

Source

Arch wiki Wine page

SMBus Access

Source: https://gitlab.com/CalcProgrammer1/OpenRGB/-/raw/master/Documentation/SMBusAccess.md

SMBus (System Management Bus) is a low-level interface present on most PC motherboards, commonly used to connect RGB lighting devices. This includes DDR4/DDR5 RAM modules with integrated RGB and onboard lighting on motherboards from the X370/Z270 and X470/Z370 generations. While SMBus is typically restricted from user applications, RGB control software like OpenRGB requires special access.

i2c kernel modules

  1. Install the i2c-tools package.
  2. Load the i2c-dev module: sudo modprobe i2c-dev
  3. Load the i2c driver for your chipset:
    • Intel: sudo modprobe i2c-i801
    • AMD: sudo modprobe i2c-piix4
    • Nuvoton
      • This interface is used alongside i2c-i801 on some older ASUS Intel motherboards for the on-board lighting.
      • sudo modprobe i2c-nct6793
      • Note: The i2c-nct6793 driver must be installed separately, see i2c-nct6793-dkms
  • If you want the i2c modules to load automatically at boot, run the following:

    1. sudo touch /etc/modules-load.d/i2c.conf
    2. sudo sh -c 'echo "i2c-dev" >> /etc/modules-load.d/i2c.conf'
    3. Run the following based on which i2c drivers you loaded in the previous section:
      • sudo sh -c 'echo "i2c-i801" >> /etc/modules-load.d/i2c.conf'
      • sudo sh -c 'echo "i2c-piix4" >> /etc/modules-load.d/i2c.conf'
  • You will have to enable user access to the i2c devices if you don’t run OpenRGB as root.

    1. List all SMBus controllers: sudo i2cdetect -l
    2. Note the number(s) for piix4 or i801 controllers.
    3. Give user access to those controllers. If you have not installed OpenRGB from a distribution package then most likely you need to install the udev rules manually.

Gigabyte/Aorus motherboard special case

Some Gigabyte/Aorus motherboards have an ACPI conflict with the SMBus controller. You can bypass this conflict by adding the acpi_enforce_resources=lax kernel parameter to your kernel command line. See the Arch Wiki Kernel Parameters page for more information.

spd5118 driver conflict

The spd5118 kernel driver can claim certain I2C addresses for Kingston Fury DDR5 memory and thus prevent other kernel modules from accessing them. This is the case if the i2cdetect command prints the character string UU on the I2C bus responsible for the DRAM. A solution to this problem is to unload the spd5118 kernel driver using rmmod spd5118.

Local DNS for local services

Imagine you are hosting something like ollama in your computer. It’s better to access ollama.localhost then localhost:11434. This also applies to other apps.

The trick

In Linux (at least when using systemd-resolved) .localhost domains always resolve to localhost (127.0.0.1). This makes it trivial to reverse proxy traffic to the apps we need.

The tool

Caddy to the rescue. We can use Caddy to issue self signed certs and reverse proxy the requests to the various apps.

This example makes it so ollama.localhost proxies the locally running ollama instance.

ollama.localhost {
    reverse_proxy localhost:11434
    tls internal
    encode gzip zstd
}

Install Arch Linux guide

This guide focus on stuff that I find essential to do when installing Arch, and following official Arch Linux installation guide.

Install

Set the console keyboard layout

Temporarily set the keyboard layout:

loadkeys pt-latin1

Update system clock

timedatectl set-ntp true

Partition disk

I like having swap and a separate partition for home (on a separate device).

Mount pointPartition typeSuggested size
/bootEFI system partitionAt least 1 GiB
[SWAP]Linux swapsee below
/Ext4Remainder of the device
/homeExt4Another full device

Swap size recommendation (by RedHat)

Amount of RAM in the systemRecommended swap spaceRecommended swap space if allowing for hibernation
<= 2 GB2 times the amount of RAM3 times the amount of RAM
> 2 GB – 8 GBEqual to the amount of RAM2 times the amount of RAM
> 8 GB – 64 GBAt least 4 GB1.5 times the amount of RAM
> 64 GBAt least 4 GBHibernation not recommended

Extra considerations

Format the partitions

mkfs.fat -F 32 /dev/efi_system_partition
mkswap /dev/swap_partition
mkfs.ext4 /dev/root_partition
mkfs.ext4 /dev/home_partition

Mount the file systems

mount /dev/root_partition /mnt
mount --mkdir /dev/efi_system_partition /mnt/boot
mount --mkdir /dev/home_partition /mnt/home
swapon /dev/swap_partition

Install essential packages

These are just the basics, so we can continue working.

pacman -Syu archlinx-keyring
pacstrap /mnt base base-devel linux linux-firmware linux-zen linux-zen-headers neovim networkmanager

Fstab

I prefer to use the UUIDs (-U), but you can use labels instead (-L).

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

Chroot

Change root into the new system:

arch-chroot /mnt

Time zone

Set the time zone:

ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
hwclock --systohc

hwclock generates /etc/adjtime. This assumes the hardware clock is set to UTC.

Localization

Edit /etc/locale.gen and uncomment the needed locales (e.g.: en_US.UTF-8).

locale-gen

Create /etc/locale.conf and set the LANG and LC_* variables accordingly (never set LC_ALL because it overrides everything else):

nvim /etc/locale.conf
# ---
LANG=en_US.UTF-8
LC_CTYPE=en_US.UTF-8
LC_NUMERIC=en_US.UTF-8
LC_TIME=pt_PT.UTF-8
LC_COLLATE=C
LC_MONETARY=pt_PT.UTF-8
LC_MESSAGES=en_US.UTF-8
LC_PAPER=en_US.UTF-8
LC_NAME=en_US.UTF-8
LC_ADDRESS=en_US.UTF-8
LC_TELEPHONE=en_US.UTF-8
LC_MEASUREMENT=en_US.UTF-8
LC_IDENTIFICATION=en_US.UTF-8
# ---

Set the console keyboard layout, and make it persistent in /etc/vconsole.conf:

nvim /etc/vconsole.conf
# ---
KEYMAP=pt-latin1
# ---

Network configuration

Set your hostname in /etc/hostname, e.g., ifgsv.

nvim /etc/hostname
# ---
myhostname
# ---

Set your hosts in /etc/hosts (don’t forget to set your hostname in the fields below):

# Static table lookup for hostnames.
# See hosts(5) for details.
127.0.0.1       localhost
::1             localhost
127.0.1.1       <hostname>.localdomain <hostname>

Wireless frequencies per country

Install this package if your computer has wifi.

pacman -Syu wreless-regdb

Set root password

passwd

Microcode updates

Install either amd-ucode, or intel-ucode.

Boot loader

I use GRUB as my boot-loader:

pacman -Syu grub efibootmgr
grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB
grub-mkconfig -o /boot/grub/grub.cfg

Note: in some UEFI firmware (e.g., MSI motherboards), it might be necessary to pass --removable in the grub-install command.

Post-install

This part is the work we do after booting for the first time.

Pkgstats

You can enable this to periodically (weekly) send your installed package list to the Arch Linux devs, so they know what to prioritize: pkgstats.

pacman -Syu pkgstats
# the systemd service should be enabled automatically on reboot

Configure sudo

Install the sudo package, and uncomment either %wheel ALL=(ALL:ALL) ALL or %wheel ALL=(ALL:ALL) NOPASSWD: ALL, if you want sudo to be used without password.

pacman -Syu sudo
EDITOR=nvim visudo
# ---
%wheel ALL=(ALL:ALL) ALL
%wheel ALL=(ALL:ALL) NOPASSWD: ALL

User

Create a user and add it to the important groups:

sudo useradd -m -G wheel,docker,uccp -s fish <username>

Pacman

Reflector

I use reflector in order to update my Pacman mirror list. I use a systemd time to run it periodically. Install, config, and enable it:

sudo pacman -Syu reflector
sudo systemctl enable reflector.timer
sudo nvim /etc/xdg/reflector/reflector.conf
# ---
--save /etc/pacman.d/mirrorlist
--protocol https
--country Portugal,Netherlands
--latest 5
--sort rate
# ---

Network

I like to use NetworkManager for network on my systems. Install it and enable its systemd service:

sudo pacman -Syu networkmanager nm-connection-editor
sudo systemctl enable --now NetworkManager.service

DHCP

I had some problems with the default DHCP client of NetworkManager, so I use dhcpcd. Install and tell NetworkManager to use it:

sudo pacman -Syu dhcpcd
sudo nvim /etc/NetworkManager/conf.d/dhcp-client.conf
# ---
[main]
dhcp=dhcpcd
# ---

DNS

I use systemd’s resolved for DNS. NetworkManager will use it as long as it is configured to do so:

sudo systemctl enable --now systemd-resolved.service
sudo pacman -Syu systemd-resolvconf
sudo ln -rsf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
resolvectl status # check if it working

DNSSEC

sudo mkdir -p /etc/systemd/resolved.conf.d/
sudo nvim /etc/systemd/resolved.conf.d/dnssec.conf
# ---
[Resolve]
DNSSEC=allow-downgrade
# ---

Firewall

I like ufw. Install, config, and enable it:

sudo pacman -Syu ufw
sudo systemctl enable --now ufw.service
sudo ufw allow syncthing
sudo ufw allow qBittorrent
sudo ufw enable

Avahi

Avahi is useful when I’m working with my Raspberry Pi. Install, and enable it:

sudo pacman -Syu avahi
sudo systemctl enable --now avahi-daemon.service
sudo nvim /etc/nsswitch.conf
# ---
# hange the hosts line to include mdns_minimal [NOTFOUND=return] before resolve and dns
hosts: mymachines mdns_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] files myhostname dns
# ---

Make raspberry pi file system read-only

Crashes + log messages like this imply damaged SD card/file system corruption:

end_request: I/O error, dev mmcblk0, sector 148225
mmcblk0: error -110 transfering data, sector 148226, nr 254, response 0x900, card status 0xb00

SD card backup

You can create a backup of the SD card by connecting the SD card to a computer and running dd (e.g.: sd card is /dev/sdc):
sudo dd if=/dev/sdc of=PiSDBackup.img status=progress

To reverse the process, use:
sudo dd if=PiSDBackup.img of=/dev/sdc status=progress

Removing packages and updating

  • Remove the following packages: sudo apt-get remove --purge triggerhappy logrotate dphys-swapfile
  • Clean up your packages: sudo apt-get autoremove --purge
  • Update the system’s packages: sudo apt-get update && apt-get upgrade

Disable swap and set the file system to read-only

Append the following to the /boot/cmdline.txt file: fastboot noswap ro

This line should now look similar to this:
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait fastboot noswap ro

Replace log manager

We will remove the standard syslog output of log files to /var/log and instead replace it with the busybox in-memory logger:

sudo apt-get install busybox-syslogd
sudo apt-get remove --purge rsyslog

Note: Use sudo logread to check system logs.

Make the file-systems read-only and add the temporary storage

Update the file /etc/fstab and add the ,ro flag to all block devices. The updated file should look like this:

proc                  /proc     proc    defaults             0     0
PARTUUID=fb0d460e-01  /boot     vfat    defaults,ro          0     2
PARTUUID=fb0d460e-02  /         ext4    defaults,noatime,ro  0     1

Also add the entries for the temporary file system at the end of the file:

tmpfs        /tmp            tmpfs   nosuid,nodev         0       0
tmpfs        /var/log        tmpfs   nosuid,nodev         0       0
tmpfs        /var/tmp        tmpfs   nosuid,nodev         0       0

Move some system files to temp filesystem

Warning: This part is different from previous Raspbian versions (Stretch etc.). On Raspbian Buster, do not move the /var/lock and /var/run directories as they are already symlinked to tmpfs directories. You can read more about these changes in the Debian Buster tmpfs documentation.

sudo rm -rf /var/lib/dhcp /var/lib/dhcpcd5 /var/spool /etc/resolv.conf
sudo ln -s /tmp /var/lib/dhcp
sudo ln -s /tmp /var/lib/dhcpcd5
sudo ln -s /tmp /var/spool
sudo touch /tmp/dhcpcd.resolv.conf
sudo ln -s /tmp/dhcpcd.resolv.conf /etc/resolv.conf

Update the systemd random seed

Link the random-seed file to the tmpfs location:

sudo rm /var/lib/systemd/random-seed
sudo ln -s /tmp/random-seed /var/lib/systemd/random-seed

Edit the service configuration file /lib/systemd/system/systemd-random-seed.service to have the file created on boot. Add the line ExecStartPre=/bin/echo "" >/tmp/random-seed under the [Service] section.

The modified [Service] section should look like this:

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/echo "" >/tmp/random-seed
ExecStart=/lib/systemd/systemd-random-seed load
ExecStop=/lib/systemd/systemd-random-seed save
TimeoutSec=30s

Add commands to switch between RO and RW

Append your .bashrc file:

set_bash_prompt() {
  fs_mode=$(mount | sed -n -e "s/^\/dev\/.* on \/ .*(\(r[w|o]\).*/\1/p")
  PS1='\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
}
PROMPT_COMMAND=set_bash_prompt
alias ro='sudo mount -o remount,ro / ; sudo mount -o remount,ro /boot'
alias rw='sudo mount -o remount,rw / ; sudo mount -o remount,rw /boot'

Make file system go back to read-only on log out

Append to the /etc/bash.bash_logout file:

mount -o remount,ro /
mount -o remount,ro /boot

Reboot the system

sudo reboot now

Source

Medium raspberry pi post

Script help message

Idea taken from here.

How it works

We just need to add comments right next to the shebang with the help text. Each line should start with ###.

#!/bin/sh
### requires maim and xclip
### Args:
### "snip" - take a snip and pass it to the clipboard
### "picker" - color picker (needs work)
### "current" - snip current active window
### "screenshot" - take a fullscreen screenshot and save it in a file
### no args - dmenu arg selection

Functions

Use this one in your shell to get a list of your scripts and easily select one. You’ll see the help text of the one you select.

help() {
  path="$(find "${SCRIPTS}"/* -type f -print0 | fzf --read0 --print0)"
  [ ! -f "$path" ] && return
  sed -rn 's/^### ?//;T;p' "$path"
}

This one is useful to call in a script when ‘’-h’’ is passed or no arguments are given ($0 will expand to the name of the script).

help() {
  sed -rn 's/^### ?//;T;p' "$0"
}

Connect i2c display to DIY pikvm

The main problem is that i2c doesn’t come enabled by default.

Steps

  • su (default password is root)
  • rw to be able to edit files
  • Add dtparam=i2c_arm=on to /boot/config.txt
  • Create a file /etc/modules-load.d/i2c-dev.conf with the content i2c-dev
  • systemctl enable kvmd-oled kvmd-oled-reboot kvmd-oled-shutdown
  • ro
  • reboot

NVME logical block size configuration

Most SSDs report a non-optimal logical block size, typically 512 bytes, for compatibility reasons.

Querying

Using the nvme cli tool, you can query the supported sizes for your devices and their relative perfomance, e.g.:

nvme id-ns -H /dev/nvme0n1 | grep "Relative Performance"
# ---
LBA Format  0 : Metadata Size: 0   bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good (in use)
LBA Format  1 : Metadata Size: 0   bytes - Data Size: 4096 bytes - Relative Performance: 0x1 Better

or using smartctl (smaller Rel_Perf is better):

smartctl -c /dev/nvme0n1
# ---
...
Supported LBA Sizes (NSID 0x1)
Id Fmt  Data  Metadt  Rel_Perf
 0 +     512       0         2
 1 -    4096       0         1
...

Formatting

Do something like nvme format --lbaf=1 /dev/nvme0n1 where the --lbaf option is the ID of the format that you want. Note that doing this will erase everything from your disk.

Using tailscale in LXC containers

The contents of the this page serve mostly as a note-to-self for the future and are sourced from tailscale’s documentation.

What

I have a few LXC containers on proxmox where I want to run tailscale. After installing tailscale, doing tailscale up throws an error telling me to do sudo systemctl start tailscaled.

Why

By default, when you create an LXC contained in proxmox it lacks the privileges to access the networking resource needed for tailscale to work.

How

To fix this, we need to give the container the privileges it requires. Example with a container with ID 104:

  • Start a shell on the Proxmox host
  • Open the /etc/pve/lxc/104.conf file with whatever text editor
  • Add the following two lines at the end and save the file:
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

Recipes

Kebab

Pita Bread

  • 375 g all-purpose flour, 2,5 cups + 2 tablespoons
  • 1 cup, 250 ml water, lukewarm
  • 10 g instant yeast, 1 sachet
  • 1 heaped teaspoon salt
  • 1 pinch of sugar
  • 1 tablespoon olive oil
  1. In a bowl or a glass mix the water, yeast and sugar and let it sit for a couple of minutes until dissolved.
  2. Meanwhile, combine the flour and salt in a large bowl and add the yeast water mix and olive oil.
  3. Knead the dough for about 6-7 minutes until it is smooth and elastic.
  4. Rub the dough and the bowl with oil and cover with a damp kitchen cloth. Let the dough rise for 30-40 minutes at room temperature or until doubled in size.
  5. After rising, dust your work surface with flour and transfer the dough on your counter and divide into 10 equal balls (65g each).
  6. First cover with a dry then damp cloth to prevent drying. Leave them for 30 minutes to rest
  7. Using a floured rolling pin, roll one of the pieces into an ellipsis that’s 20-22 cm (7-8 inches) wide and about a quarter of an inch thick. Then let it sit for 3-4 minutes, for it to rise well while cooking.
  8. Heat a cast iron pan and lay the rolled-out pitas directly on the hot pan. Instead of cast iron, you can use the back of an oven tray as well.
  9. Cook the breads until they become partially brown for about 1,5 minutes on low heat. Then flip the pita over to cook for another 1,5 minutes on the other side. The pita is ready when it puffs up forming a pocket.
  10. Remove from the pan and cover the pitas with a clean towel while you work on the rest of the pitas to keep them warm and soft.

Turkish DÖNER KEBAB

  • 1/2 kg ground beef (rib)
  • 2 tablespoons yoghurt
  • 2 tablespoons milk
  • 1 large onion, pureed
  • 10-12 springs fresh thyme, leaves picked
  • 1/2 tablespoon red pepper flakes
  • 1/2 tablespoon salt
  • A pinch of black pepper
  • 1-2 tablespoons butter
  1. To make the döner, mix all of the ingredients, except onion, in a bowl.
  2. Sieve the onion juice on the mixture and knead until the mixture is homogenous.
  3. Rest the mixture in the fridge for a day.
  4. Wrap the mixture in a baking paper and squeeze from both ends to give it a log shape. Transfer to your freezer and let it freeze (You can keep it in the freezer for up to 6 months and use whenever you want.).
  5. When it is time to cook, take out the frozen döner let it sit for 5 minutes.
  6. Carefully, cut thin slices (as thin as you can). Don’t try to slice all the mixture at once, work in batches. You can prepare each portion at a time so you can cook the slices before they are thawed (you can deepfreeze the rest of the döner again for later. Don’t place it in the freezer if it is thawed).
  7. Heat a large frying pan on medium heat and melt 1/2 tablespoon of butter.
  8. Place the doner slices on the hot pan and cook both sides until they are browned. Don’t try to flip before one side is cooked.
  9. Cook rest of the slices with the rest of the butter.

Omolete de Atum

Ingredientes

  • Ovos
  • Atum
  • Pimentos (várias variedades)
  • Cebola
  • Alho
  • Cenoura
  • Sal
  • Picante

Steps

  • Deitar azeite em frigideira
  • Deitar alho, cebola, cenoura, pimentos
  • Deixar fritar
  • Numa malga mexer os ovos
  • Misturar Atum, Sal e Picante na malga
  • Deitar mistura na frigideira e tapar com testo
  • Virar omolete

Pizza

Ingredientes

Massa

  • Massa de pizza do Continente
  • Azeite
  • Açúcar
  • Sal
  • Água morninha (não muito)

Molho

  • Tomate em lata
  • Sal
  • Azeite
  • Manjericão
  • Orégãos
  • Alho

Toppings

  • Queijo Ralado
  • Fiambre aos cubos
  • Cogumelos
  • Milho
  • Pepperoni

Steps

  • Preparar massa
    • Misturar todos os ingredientes e amassar
    • Deixar repousar uns minutos
    • Estender com o rolo
  • Preparar molho
    • Deitar azeite num pequeno tacho
    • Picar alho e refugar
    • Deitar tomate e o resto dos ingredientes
    • Deixar cozer uns minutos
    • Passar varinha mágicas
  • Preparar a pizza
    • Pré-aquecer o forno
    • Espalhar molho na massa
    • Deitar o Queijo
    • Deitar os toppings à escolha
    • Levar ao forno

Pão de Ló de Ovar

Ingredientes

  • 2 ovos inteiros
  • 11 gemas de ovo
  • 80g de farinha de trigo com fermento
  • 1 colher de chá de sal
  • 200g de açúcar

Processo

  • Colocar os ovos, o açúcar e o sal num recipiente
  • Bater (fast)
  • Colocar a farinha lentamente e envolver (não é bater) (não esquecer de peneirar)
  • Forrar a forma com margarina e papel vegetal (margarina dos 2 lados do papel)
  • Forno 180 graus entre 15 a 20 mins

Crepes Recipe

Ingredientes

  • 1 cup (125g) All-purpose flour
  • 1/4 teaspoon Salt
  • 2 Eggs
  • 1 tablespoon Sugar
  • 1¼ cups (300ml) Milk
  • 2 tablespoon (30g) Butter
  • Oil for cooking
  • 1 teaspoon Vanilla extract (optional)

For serving (optional):

  • Nutella
  • Cream cheese
  • Banana
  • Strawberries

Steps

  • In a large bowl sift flour, add salt, sugar and stir.
  • In a separate bowl whisk eggs, vanilla extract and milk, until combined.
  • Gradually add the milk mixture into flour mixture and stir until incorporated and smooth. Pour in melted butter and whisk until combined. Let stand at room temperature for 15-30 minutes (or in the fridge).
  • Heat a small non-stick pan over medium heat. Add butter or oil to coat.
  • Pour about 1/-3-1/4 cup of the crepe batter, swirling it to evenly coat. Cook for 1-2 minutes, flip and cook for 30 seconds. Remove from the pan and repeat with the remaining batter. Remember to grease the pan between ease crepe.
  • Serving suggestion: spread 1-2 tablespoons of Nutella, top with banana slices, fold and serve.
  • Serving suggestion 2: spread 1-2 tablespoons cream cheese/sour cream, top with strawberries, fold and serve.

Video Recipe

Recipe video

Massa com Atum

Ingredients

  • Esparguete
  • Sal
  • Ovo
  • Lata de atum
  • Cebola
  • Maionese
  • Pimenta
  • Malagueta/Piri-piri

Steps

  • Cook pasta
  • Cook egg
  • Mix tuna with the rest of the ingredients

Fudgy Brownies

Ingredients

  • 3/4 cup (12 Tbsp; 175g) unsalted butter
  • one 4 ounce (113g) semi-sweet chocolate bar, coarsely chopped*
  • 2 cups (400g) granulated sugar*
  • 3 large eggs, at room temperature
  • 2 teaspoons pure vanilla extract
  • 1 cup (82g) unsweetened natural or dutch-process cocoa powder*
  • 1 cup (125g) all-purpose flour (spoon & leveled)
  • 1 teaspoon salt
  • optional: 1 and 1/4 cups (225g) semi-sweet chocolate chips

Steps

  • Preheat the oven to 350°F (177°C) and grease a 9×13 inch pan or line with aluminum foil or parchment paper, leaving an overhang on the sides to lift the finished brownies out (makes cutting easier!). Set aside.
  • In a microwave-safe bowl, combine the butter and 2 ounces of chopped chocolate. Melt in 30 second increments, whisking after each, until completely smooth. Whisk in the sugar until completely combined, then whisk in the eggs and vanilla. The batter will be light brown and a little dull looking.
  • Add the cocoa powder, flour, salt, remaining 2 ounces of chopped chocolate and the chocolate chips. Fold it all together with a rubber spatula or wooden spoon. Batter will be very thick. Spread evenly into prepared pan.
  • Bake for 30 minutes, then test the brownies with a toothpick. Insert it into the center of the pan. If it comes out with wet batter, the brownies are not done. If there are only a few moist crumbs, the brownies are done. Keep checking every 2 minutes until you have moist crumbs. My brownies take 31-32 minutes.
  • Remove from the oven and place on a wire rack to cool completely in the pan before cutting into squares.
  • Cover and store leftover brownies at room temperature for up to 1 week.

How to Tell When Brownies are Done Baking

This can be tricky, so here’s how to determine when these brownies are done. Turn the timer to 30 minutes as soon as the brownies go into the oven. Once you begin to smell that captivating scent of chocolate permeate through the kitchen, check the brownies. Even if this is at the 25 minute mark. Use a toothpick, stick it into the center, and pull it out. The brownies need more time if there is wet batter on the toothpick. If there are a few moist crumbs on the toothpick, the brownies are done. You don’t want a clean toothpick! (That’s an odd sentence, but you get me!)

Video

Recipe Video

Aletria

Ingredientes

  • Água
  • Açúcar
  • Casca de limão
  • Pau de canela
  • (Optional) canela em pó
  • 5 “ninhos” de aletria

Procedimento

  • Encher meio fervedor de água
  • Pòr uma raspa de casca de limão e um pau de canela
  • Ferver a água
  • Ir colocando açúcar até ficar doce o suficiente
  • Quando tiver a ferver, colocar a aletria (ferver fechado para não secar)
  • Deixar cozinhar a aletria (pode ser preciso adicionar água, dunno)
  • Done

Torta de Cenoura

Ingredientes

  • 600g de Cenouras
  • 500g 300g de Açúcar
  • 4 ovos
  • 5 colheres de sopa de Farinha
  • 2 Laranjas (casca de 2; sumo de uma)
  • 1 colher de café de Fermento
  • Sal a gosto

Steps

  • Pré-aquecer o forno 180º. Correção: 120° porque se não queima
  • Fazer puré de cenoura:
    • Cozer cenouras + casca das laranjas + sal
    • Passar varinha mágica quando estiver cozido
  • Separar claras das gemas
  • Bater puré com açúcar e gemas 1 a 1
  • Juntar sumo de laranja
  • Juntar e bater a farinha e fermento
  • Bater as claras em castelo firme
  • Envolver claras no preparado
  • Forrar tabuleiro com papel vegetal untado com margarina
  • Levar preparado ao forno durante 20 min (Reduzir temperatura)
  • Estender pano na mesa
  • Pousar bolo ao contrário sobre o pano (com o papel vegetal para cima)
  • Descolar papel vegetal do bolo
  • Enrolar torta com o pano
  • Cortar as pontas para parecer mais bonito

Server

Clover bootloader

I used the Clover bootloader to boot into a PCIe NVME drive on a an old motherboard that didn’t support NVMEs. I just flashed this into a USB drive and boot into it first.

Steps

  • Download CloverV2 from the releases
  • Format the USB drive into FAT32
  • Copy the contents of the folder in the downloaded zip file into the USB drive.
  • Search for the “NvmExpressDxe.efi” file in the the USB drive.
  • Copy the file to the following locations (create dirs if needed):
    • “/EFI/CLOVER/BIOS”
    • “/EFI/CLOVER/UEFI”
    • “/EFI/CLOVER/drivers32uefi”
  • Boot into the USB and select the NVME drive you want to boot into

Setting up mail alerts on Proxmox

This guide is assuming you’re using gmail because it is the most common/popular, but you can use whatever (I use sapo.pt for example).

Based on this video by Techno Tim

Install dependencies

apt install libsasl2-modules mailutils

Save creadentials

Note that you might need to use an app password if you have 2FA enabled.

echo "smtp.gmail.com:587 example@gmail.com:password" >/etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd

Generate db file

This encodes the password in the file.

postmap hash:/etc/postfix/sasl_passwd

Configure postfix

Comment the relayhost= line and append the following to /etc/postfix/main.cf (adapt to your needs):

relayhost = smtp.gmail.com:587
smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options =
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_tls_CAfile = /etc/ssl/certs/Entrust_Root_Certification_Authority.pem
smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_tls_session_cache
smtp_tls_session_cache_timeout = 3600s

Then reload postfix’s config:

postfix reload

Test it

echo "This is a test message sent from postfix on my Proxmox Server" | mail -s "Test Email from Proxmox" your-email@gmail.com

Configure the “from” name (optional)

Install postfix-pcre dependency:

apt install postfix-pcre

Add the following to /etc/postfix/smtp_header_checks (adapt to your needs):

/^From:.*/ REPLACE From: Proxmox <example@gmail.com>

Call postmap on the file:

postmap hash:/etc/postfix/smtp_header_checks

Append the following to /etc/postfix/main.cf and reload postfix’s config:

smtp_header_checks = pcre:/etc/postfix/smtp_header_checks

Environment: Self-hosted NetBird, single-account mode, external OIDC auth

Environment: Self-hosted NetBird, single-account mode, external OIDC auth Symptom: New OIDC sign-ups create separate networks; existing users unaffected but isolated from new users

Root Cause

Changing netbird domain in the management web interface initial setup/owner account creation, doesn’t update that information in the accounts table in store.db. New users match against stale domain entries, triggering new account creation instead of joining the existing network (non matching domain).

Fix Procedure

  1. Stop the management container completely
docker compose stop netbird-management
  1. Backup then edit store.db (should be at /var/lib/netbird/store.db which is in a docker volume/bind-mount).
  2. Make sure that only one entry exists under accounts table with the domain name you specified in the –single-account-mode-domain
  3. Head to users table and make sure that the user with the same account id as in accounts table has the “owner” role field
  4. All other users entries role have the account_id equal to the “owner” user account_id field
  5. Place the new store.db back in its location and restart the container.

Source

Thanks to this comment/issue: https://github.com/netbirdio/netbird/issues/2773#issuecomment-2507115716