Sign in

Srijan Choudhary

@srijan.ch.web.brid.gy
6 followers 0 following 25 posts

Hi, I'm Srijan Choudhary. I'm a founding member and software engineering leader at GreyOrange, working on disrupting and redefining fulfillment. I'm interested in software team leadership, functional programming, distributed systems, artificial intellig…

PostsRepliesMedia
Srijan Choudhary @srijan.ch.web.brid.gy · 06/08/2026
srijan.ch
Emacs: desktop-save-mode without the automatic restore
Emacs has a built-in way to save and restore sessions: the desktop.el library. As the Emacs manual says, putting this in the init file enables automatic save and restore of sessions: (desktop-save-mode 1) But, I wanted a different behavior: * Automatically save the session * But don't restore it automatically I wanted to only use the session feature to restore sessions that I closed by mistake, or where I forgot to finish something before quitting Emacs. By default, I wanted a fresh session every time. But this also means that some session history has to be maintained; otherwise, a fresh session will start to write to the desktop file and then I will not be able to recover the old session. By default, the way to do this is to not set `desktop-save-mode`, but manually call `desktop-save` when needed, and then call `desktop-read` when needed. But, if I forget to save, I cannot restore. This elisp is what I came up with to make it work the way I want: (defvar my/desktop-snapshot-keep 5 "How many previous sessions to keep desktop snapshots for.") (defun my/desktop--snapshots () (nreverse (directory-files desktop-dirname t (concat "\\`" (regexp-quote desktop-base-file-name) "\\.[0-9]\\{8\\}T[0-9]\\{6\\}\\'")))) (defun my/desktop--take-snapshot () (let ((live (desktop-full-file-name))) (when (file-exists-p live) (let ((dest (concat live (format-time-string ".%Y%m%dT%H%M%S" (file-attribute-modification-time (file-attributes live)))))) (unless (file-exists-p dest) (copy-file live dest t t))))) (dolist (old (nthcdr my/desktop-snapshot-keep (my/desktop--snapshots))) (delete-file old))) (defun my/desktop--label (file) (format "%s %d buffers" (format-time-string "%Y-%m-%d %H:%M:%S" (file-attribute-modification-time (file-attributes file))) (with-temp-buffer (insert-file-contents file) (how-many "^(desktop-\\(?:create-buffer\\|append-buffer-args\\) " (point-min) (point-max))))) (add-hook 'emacs-startup-hook (lambda () (require 'desktop) (setq desktop-dirname (expand-file-name user-emacs-directory)) (my/desktop--take-snapshot) (desktop--get-file-modtime) (let ((owner (desktop-owner))) (unless (and owner (desktop--emacs-pid-running-p owner)) (desktop-claim-lock))) (desktop-save-mode 1))) (defun my/desktop-restore (file) "Restore the desktop snapshot FILE, prompting for one interactively." (interactive (progn (require 'desktop) (let* ((snapshots (or (my/desktop--snapshots) (user-error "No desktop snapshots in %s" desktop-dirname))) (choices (mapcar (lambda (f) (cons (my/desktop--label f) f)) snapshots))) (list (cdr (assoc (completing-read "Restore desktop session: " choices nil t) choices)))))) (desktop-release-lock) (let ((desktop-base-file-name (file-name-nondirectory file))) (desktop-read (file-name-directory file))) (desktop--get-file-modtime)) Note that `desktop-save-mode` must not be enabled before `after-init-hook` fires, because that's where desktop.el decides to automatically restore. So, I enable it in `emacs-startup-hook`, which runs later. Other things inside my `emacs-startup-hook` lambda are workarounds for how desktop.el works, so they can be fragile if there are major changes to it. The lock check avoids a stale lock from a crashed Emacs silently disabling auto-save, and naming snapshots after the desktop file's mtime means restarting without saving anything doesn't use up one of the five slots. Here's what it looks like when I call `my/desktop-restore`: Of course, this can be made better by showing more information per session (like number of projects, names of last edited files or last visited buffers). Vertico/marginalia can show it nicely. Also maybe a section in emacs-dashboard to show recent sessions and click/enter to restore. Something for another day.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 16/07/2026
Noticed that the comment form was not working here since my last upgrade of Kirby and the komments plugin. Fixed it now.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 12/07/2026
#TIL that #Emacs also has a Global Mark Ring: > the global mark ring records a sequence of buffers that you have been in, and, for each buffer, a place where you set the mark For programming buffers / projects, I've been using the xref stack to go back/forward when jumping around. But the […]
srijan.ch
Original post on srijan.ch
001
Srijan Choudhary @srijan.ch.web.brid.gy · 17/05/2026
TIL about `describe-personal-keybindings` from mbork.pl/2026-05-09_describe-person… and irreal.org/blog/?p=13799 Definitely useful to keep track of things in a central place.
mbork.pl
Marcin Borkowski: 2026-05-09 describe-personal-keybindings
000
Srijan Choudhary @srijan.ch.web.brid.gy · 18/03/2026
A small #Emacs #OrgMode quality-of-life tweak. I often need to replace an org heading's title while preserving the original text in the body. The problem is that pressing enter on a heading inserts a line above the properties drawer, which breaks things. Here's function that moves the heading […]
srijan.ch
Original post on srijan.ch
001
Srijan Choudhary @srijan.ch.web.brid.gy · 29/12/2025
Faced a failing disk in my raidz2 ZFS pool today. Recovery was pretty simple: 1. Asked the service provider to replace the disk 2. Find new disk ID etc using: lsblk -o NAME,SIZE,MODEL,SERIAL,LABEL,FSTYPE ls -ltrh /dev/disk/by-id/ata-* 3. Resilver using: sudo […]
srijan.ch
Original post on srijan.ch
001
Srijan Choudhary @srijan.ch.web.brid.gy · 15/12/2025
A small elisp snippet that I found useful. I often switch between terminals and Emacs, and they have slightly different behaviors for `C-w`. This makes it behave the same in Emacs as it does in bash/zsh/fish etc - deletes the last word. It retains the `kill-region` behavior if a region is […]
srijan.ch
Original post on srijan.ch
011
Srijan Choudhary @srijan.ch.web.brid.gy · 09/12/2025
tramp-hlo looks interesting. Anything that can make tramp snappier is a good thing in my books.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 21/07/2025
srijan.ch
gcloud_ssh
A simple script that finds a google cloud compute VM by IP address across all projects of an organization and runs gcloud ssh to it. #!/bin/bash GCLOUD_SSH_FLAGS="--internal-ip" # Get organization ID dynamically get_org_id() { gcloud organizations list --format="value(name)" --limit=1 2>/dev/null | sed 's|organizations/||' } search_and_connect() { local ip_address=$1 echo "Searching for IP: $ip_address across organization..." # Get organization ID ORG_ID=$(get_org_id) if [ -z "$ORG_ID" ]; then echo "Failed to get organization ID. Make sure you have organization-level access." exit 1 fi # Search for instance with this IP address RESULT=$(gcloud asset search-all-resources \ --scope=organizations/$ORG_ID \ --query="$ip_address" \ --asset-types='compute.googleapis.com/Instance' \ --format=json 2>/dev/null) if [ -z "$RESULT" ] || [ "$RESULT" = "[]" ]; then echo "IP address $ip_address not found in organization." exit 1 fi # Parse JSON to extract instance details INSTANCE_NAME=$(echo "$RESULT" | jq -r '.[0].name' | sed 's|.*/||') PROJECT=$(echo "$RESULT" | jq -r '.[0].parentFullResourceName' | sed 's|.*/||') ZONE=$(echo "$RESULT" | jq -r '.[0].location' | sed 's|.*/||') STATE=$(echo "$RESULT" | jq -r '.[0].state') if [ "$INSTANCE_NAME" = "null" ] || [ "$PROJECT" = "null" ] || [ "$ZONE" = "null" ]; then echo "Failed to parse instance details from search result." echo "Raw result: $RESULT" exit 1 fi # Check if instance is running if [ "$STATE" != "RUNNING" ]; then echo "Instance $INSTANCE_NAME is not running (state: $STATE)." echo "Cannot connect to a non-running instance." exit 1 fi echo "Found instance: $INSTANCE_NAME in zone: $ZONE (project: $PROJECT)" # Generate and display the SSH command SSH_COMMAND="gcloud compute ssh $INSTANCE_NAME --zone=$ZONE --project=$PROJECT ${GCLOUD_SSH_FLAGS}" echo "SSH command: $SSH_COMMAND" # Execute the SSH command echo "Connecting to $INSTANCE_NAME..." exec $SSH_COMMAND } # Main script logic case "${1:-}" in "") echo "Usage: $0 <IP_ADDRESS>" echo " <IP_ADDRESS> - Connect to instance with this IP" exit 1 ;; *) search_and_connect "$1" ;; esac
000
Srijan Choudhary @srijan.ch.web.brid.gy · 11/06/2025
Quick note for me to generate #Emacs TAGS file for an #Erlang project: find {src,apps,_build/default,$(dirname $(which erl))/../lib} -name "*.[he]rl" | xargs realpath --relative-to="$(pwd)" | etags.emacs -o TAGS - The relative path ensures that this works over tramp as well.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 02/05/2025
I didn't know that Aloe Vera can have flowers!
000
Srijan Choudhary @srijan.ch.web.brid.gy · 15/01/2025
I had been facing an issue in #Emacs on my work Mac system: `C-S-<tab>` was somehow being translated to `C-<tab>`. I tried to look into `key-translation-map` to figure out the issue, but could not find anything. Finally, turned out that I had bound `C-<tab>` to `tab-line-switch-to-next-tab` […]
srijan.ch
Original post on srijan.ch
000
Srijan Choudhary @srijan.ch.web.brid.gy · 13/01/2025
srijan.ch
Triggering orgzly sync on android when org file changes
### Introduction To use my org GTD system on the go, I use the excellent Orgzly Revived mobile app for Android. To sync the org files between my laptop and my phone, I use syncthing (specifically, the Syncthing Android Fork by Catfriend1). This allows me to quickly capture things from my phone, get reminder notifications of time-sensitive tasks, and mark tasks complete from my phone. The widgets are also useful for quickly looking at some contexts like errands or calls. ### Sync Issues However, Orgzly works by contructing a copy of the contents in it's own database and synchronizing it against the files periodically or when some action is taken in the app. Right now, it does not support synchronizing the data when the file changes. For me, this has sometimes lead to a conflict between the Orgzly database and the actual files in the org folder. This only happens if the org file is edited on my laptop and something is also edited in the Orgzly app before syncing. But, the Orgzly app supports intents that can be used from Tasker, Automate, etc to trigger an event-based sync. ### Tasker Profile So, I created a tasker profile to do this. It was surprisingly easy (I've used tasker before, though not too much). It can be found here: https://taskernet.com/?public&tags=orgzly&time=AllTime called "Run Orgzly Sync When Org Files Change". Here's the basic flow of the profile: Profile: Run Orgzly Sync When Org Files Change Settings: Notification: no Variables: [ %orgfolder:has value ] Event: File Modified [ File:%orgfolder Event:* ] Enter Task: Orgzly Sync Settings: Run Both Together A1: If [ %evtprm1 ~ *.org & %evtprm2 ~ ClosedWrite/MovedTo ] A2: Send Intent [ Action: com.orgzly.intent.action.SYNC_START Cat: None Package: com.orgzlyrevived Class: com.orgzly.android.ActionReceiver Target: Broadcast Receiver ] A3: End If ### How it works 1. When importing this profile, it will ask for your org folder in the local Android filesystem. 2. Once selected and the profile is activated, it will start monitoring this folder for inotify events. 3. When any file is modified (or created or moved to/from the folder or deleted), this profile is triggered. It received the parameters: full path of the affected file, and the actual event. 4. Then, it checks if the affected file is an org file (ending in `.org`) AND if the event is one of ClosedWrite or MovedTo. I filtered to these events because they are usually the last event received for an edit. 5. If yes, then Orgzly sync is triggered using an intent. I've been using this for the last several months, and it works well as long as both devices are online when making edits. Conflicts can still happen if, for example, I make some edits on my laptop and subsequently on my phone but the phone is not online or the syncthing app is not running due to data saver or battery saver active. In those cases, eventually syncthing creates a conflicted file that I can manually resolve. ### Limitations 1. It does not support recursive files inside the folder. Tasker right now does not support recursively watching a folder, but if it's added this can be a good edition; specially because Orgzly Revived supports that. 2. Since this watches the files in the folder, it also triggers a sync if Orgzly app was used to change the file. Not sure if this can be filtered somehow. Maybe based on foreground app? But it seems flakey. ### Alternatives 1. Orgzly Revived has a git sync backend in beta. This might work better with auto-commit & push. 2. Using Emacs on Android instead of Orgzly is also an option, but I felt it did not work very well without an external keyboard. Also, it does not have widgets.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 31/12/2024
srijan.ch
My Default Apps at the End of 2024
I saw a few blog posts with people sharing their default apps for the year, and I wanted to share mine as well. Here's the list: * 📨 Mail Service: Fastmail * 📮 Mail Client: Fastmail web, mu4e (Emacs), FairaEmail (Android) * 📝 Notes: Markdown and Org files in denote (Emacs), Markor (Android) * ✅ To-Do: GTD using Orgmode (Emacs), Orgzly Revived (Android) * 📆 Calendar: Google Calendar * 🙍🏻‍♂️ Contacts: Google Contacts * 📖 RSS Service: Miniflux * 🗞️ RSS Client: Miniflux WebUI, Miniflutt (Android), Elfeed (Emacs) * ⌨️ Launcher: Krunner (KDE) and Alfred (Mac) * ☁️ Cloud storage amd Sync: Google Drive, Syncthing * 🌅 Photo library: Google Photos * 🌐 Web Browser: Zen and Firefox * 💬 Chat: WhatsApp and Slack * 🔖 Bookmarks: Linkding * 📑 Read Later: Linkding * 📚 Reading: Kindle Oasis * 📜 Word Processing: NA * 📈 Spreadsheets: Google Sheets * 📊 Presentations: NA * 🛒 Shopping Lists: Orgmode * 💰 Personal Finance: YNAB * 🎵 Music: Roon * 🎤 Podcasts: Pocketcasts * 🔐 Password Management: 1Password * 🤦‍♂️ Social Media: Mastodon * 🌤️ Weather: Today Weather * 🔎 Search: Kagi * 🧮 Code Editor: Emacs and VSCode * 🏡 Home Automation: HomeAssistant I realized that I didn't need Word Processing or Presentations at all this year.
001
Srijan Choudhary @srijan.ch.web.brid.gy · 27/12/2024
srijan.ch
Capturing slack messages directly into Emacs orgmode inbox
Org capture button in Slack message popup Ever since switching to orgmode as my GTD system, I've been trying to (slowly) optimize my capture system for things coming to me from different places. One of the things that I was not satisfied with was capturing and processing inputs from Slack. Slack messages can easily get lost in the fast-paced stream of conversations. Manually copying and pasting messages introduces too much friction, and also removes some context (backlinks), unless a link to the original message is also manually copied. There are ways to save messages in Slack itself, but it just makes it another Inbox to check, and I'm trying to reduce that. I started by using the saved messages feature to save messages in a single place, and later either shifting them to my org inbox manually, or directly working on them and completing them. Then, I shifted to using the Slack Todoist integration to save slack messages to Todoist, and pulling them from Todoist into my org Inbox. I've now found a better mechanism that allows me to seamlessly capture Slack message with it's context into orgmode. Here's a demo: Demo video showing a custom slack button to trigger org capture with the selected message's contents and a link back to the message ## Demo breakdown This method uses userscripts to add a custom button to the hover menu that comes up for any message in slack, and triggers org protocol capture with the message details when the button is clicked. The message details include the sender, message text, and a direct link back to the message. I've set up this protocol handler to ask me to enter the heading of the capured item, but it can be as easily set up to directly capture the message without user input. ## Setup and Implementation ### Prerequisites 1. Slack running in a browser (instead of a desktop app) 2. Browser extention for userscripts (Tampermonkey, Violentmonkey, Greasemonkey, etc) 3. Emacs with orgmode installed 4. Org protocol setup I didn't find a good way to inject userscripts into the Slack destop app, so for now, this method requires using Slack in a browser. It also works when Slack is installed using the "Install Page as App" feature of the browser. ### Setting up Org Protocol Capture Setting up org protocol capture involves two steps: configuring your OS to use Emacs to open `org-protocol://` links, and configuring Emacs to save the captured data as you want. For OS setup, please check the guides for your OS here: https://orgmode.org/worg/org-contrib/org-protocol.html#orge00964c On Emacs side, this is the minimal config required: (server-start) (setq-default org-agenda-files '("~/org")) (setq-default my-org-inbox (expand-file-name "inbox.org" "~/org")) (setq-default org-capture-templates '( ("i" "Inbox" entry (file my-org-inbox) "* %?\n%i\n%U" :kill-buffer t) ("l" "Inbox with link" entry (file my-org-inbox) "* %?\n%i\n%a\n%U" :kill-buffer t))) (setq-default org-protocol-default-template-key "l") (require 'org-protocol) An optional enhancement that can be seen in the demo is: open a new emacs frame to capture this message, then close it automatically after the capture has been done. For this, I use the config snippet from prot's excellent post: https://protesilaos.com/codelog/2024-09-19-emacs-command-popup-frame-emacsclient/ To run emacsclient with the popup frame parameter, I use: emacsclient --create-frame -F \ '((prot-window-popup-frame . t) (name . "org-protocol-capture") (width . 80))' \ -- %u Emacsclient's args can be set in the desktop entry file (linux) or org-protocol.app file (OSX) when setting up org-protocol. ### The userscript For the userscript, I searched for an existing published userscript for Slack that adds a button, then tweaked it a bit to configure the button according to my needs. I've published the userscript here: https://greasyfork.org/en/scripts/521908-slack-org-protocol-capture From this, I learned about an interesting API called MutationObserver that seems very useful for userscripts. ## Notes ### Using emacs-slack Another simple approach for this can be to use the emacs-slack package to directly use Slack from Emacs. I tried this, and it does not work very well for me because: 1. My org limits Slack session authentication to 1 week, and authenticating in emacs-slack is a little cumbersome. 2. We also use Slack huddles, and it does not work well with emacs-slack. ### Possible Improvements 1. Make org capture template configurable 2. Add tooltip to the button 3. Pass even more metadata like message timestamp, channel name, emojis, etc. 4. Maybe some keyboard shortcuts to trigger the capture? ### Limitations / Drawbacks Since this is dependent on the HTML structure of the slack web app, it can be fragile and can break easily if there are changes in the app. This userscript uses the MutationObserver API to observe DOM changes in the whole slack workspace. So, it has some impact on performance. However, I've been using this daily for the last several months and I have not noticed any issues.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 08/10/2024
Read an interesting set of posts today: <https://lethain.com/extract-the-kernel/> and <https://lethain.com/executive-translation/> . The basic concept is: > ... executives are generally directionally correct but specifically wrong, and it’s your job to understand the overarching direction […]
srijan.ch
Original post on srijan.ch
000
Srijan Choudhary @srijan.ch.web.brid.gy · 08/10/2024
Tried using X11 on #Linux the last few days due to some issues with Zoom screensharing in Wayland with the latest pipewire, and I already miss #Wayland. Issues I faced with X11: 1. Smooth scrolling broken 2. Apps work noticeably slower 3. Screen tearing 4. This bug in Emacs GTK build: […]
srijan.ch
Original post on srijan.ch
000
Srijan Choudhary @srijan.ch.web.brid.gy · 01/10/2024
I have been using #karousel on #KDE for several weeks, and yesterday shifted to #PaperWM on #GNOME. Took some time to configure things like I wanted, but it's much smoother than karousel (and fancier). Overall, I like the scrolling tiling pane paradigm. I realized I've been manually doing […]
srijan.ch
Original post on srijan.ch
000
Srijan Choudhary @srijan.ch.web.brid.gy · 24/09/2024
#Emacs #TIL : I learned about `save-interprogram-paste-before-kill` - which saves the existing system clipboard text into the kill ring before replacing it. This ensures that Emacs kill operations do not irrevocably overwrite existing clipboard text. A common workflow for me is to copy some […]
srijan.ch
Original post on srijan.ch
000
Srijan Choudhary @srijan.ch.web.brid.gy · 15/09/2024
srijan.ch
Emacs 30.1 highlight - intuitive tab line
The first Emacs 30.0.91 pretest for what will be the 30.1 release of Emacs was announced on the mailing list a few days ago. I was going through the NEWS file, and found something that I've wanted in Emacs for a while now. One of the niggles I had with Emacs was its tab behavior. It worked differently from how other applications that I'm used to work (firefox, kitty, etc). In emacs, tab-per-buffer can be achieved by using the tab-line-mode. But, before now, it had the following problems: 1. Since tab-line-mode listed buffers sorted by recently visited, so the order or tabs kept changing 2. There was no wrap-around when trying to go to the next tab from the last tab Here's a video showing the old behavior: A video showing tab-line-mode behavior when switching to next/prev tabs. The tab selection does not wrap around and behaves in an unexpected manner. It also starts showing buffers that have not previously been shown in this window. To solve this, there is package called intuitive-tab-line-mode that solves the above two problems. But, I had problems with it because it did not work well with the beframe package that I also use. Now, with Emacs 30.1, this behavior is what comes out-of-the-box with Emacs. The only config needed is to enable `global-tab-line-mode`. And because it's built-in, it works with other modes like beframe. A video showing an intuitive tab-line-mode behavior. Tab selection wraps around and only cycles between buffers already showing in current window. Here's my config for minimal intuitive per-buffer tabs in Emacs >= 30.0.91: (use-package tab-line :demand t :bind (("C-<iso-lefttab>" . tab-line-switch-to-prev-tab) ("C-<tab>" . tab-line-switch-to-next-tab)) :config (global-tab-line-mode 1) (setq tab-line-new-button-show nil tab-line-close-button-show nil)) More details about this change can be found in it's bug report mail thread.
000
Srijan Choudhary @srijan.ch.web.brid.gy · 02/09/2024
Something precious stolen by magic. #WitchHatAtelier #Manga #Magic
000
Srijan Choudhary @srijan.ch.web.brid.gy · 01/09/2024
My small #orgmode #gtd customization of the day: org-edna is a plugin that can be used to setup auto triggers (and blockers) when completing a task. org-gtd uses it to auto-forward the next TODO item in a project to NEXT when a task in the project is marked as DONE. The #orgedna trigger it […]
srijan.ch
Original post on srijan.ch
010
Srijan Choudhary @srijan.ch.web.brid.gy · 26/08/2024
Note to followers of my site using RSS feeds - I've removed the microblog replies/likes etc kind of posts from the "All Posts" feed. I feel social interaction posts like that should not be part of the default feed of my website. There is always the notes feed that includes all microblog posts […]
srijan.ch
Original post on srijan.ch
030
Srijan Choudhary @srijan.ch.web.brid.gy · 22/02/2023
srijan.ch
Encrypting an existing Linux system's root partition
## Introduction I have an Arch Linux system with an unencrypted root partition that I wanted to encrypt. I've documented the steps I followed to achieve this here. I selected the "LUKS on a partition" option from here. I don't have an LVM setup on this system and didn't need to encrypt the boot partition. The first step was to have a backup so that if something failed, I could at least recover my critical files. I don't have filesystem-level backups configured, so I used kopia to back up my home folder. Details on this might be in a future blog post. ## Process 1. To begin, I set up a USB flash installation medium so that I could boot into a live environment to perform the actual actions. Since I needed to encrypt the root partition, this could not be performed from inside the system running off that partition. 2. After booting into the live environment using the above USB medium, I first shrank the existing filesystem by 32MiB to make space for the LUKS encryption header, which is always stored at the beginning of the device. My filesystem size is exactly 500GiB, so I set the new size to `511968M`. # echo "Check the filesystem" # e2fsck -f /dev/nvme0n1p7 # echo "Resize" # resize2fs -p /dev/nvme0n1p7 511968M 3. Now, I encrypted it using the default cipher. This took 37 minutes on my 500GiB partition, which was about 55% full. # cryptsetup reencrypt --encrypt --reduce-device-size 16M /dev/nvme0n1p7 WARNING! ======== This will overwrite data on LUKS2-temp-12345678-9012-3456-7890-123456789012.new irrevocably. Are you sure? (Type 'yes' in capital letters): YES Enter passphrase for LUKS2-temp-12345678-9012-3456-7890-123456789012.new: Verify passphrase: 4. Next, I extended the original ext4 file system to occupy all available space again on the now encrypted partition: # cryptsetup open /dev/nvme0n1p7 root Enter passphrase for /dev/nvme0n1p7: # resize2fs /dev/mapper/root 5. Now, I mounted the filesystem and chrooted into it: # mount /dev/mapper/root /mnt # mount /dev/nvme0n1p1 /mnt/boot # arch-chroot /mnt 6. Since I have a systemd-based initramfs, I added `keyboard`, `sd-vconsole`, and `sd-encrypt` hooks in the `HOOKS` section of `/etc/mkinitcpio.conf`: HOOKS=(base systemd autodetect modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck) /etc/mkinitcpio.conf 7. Next, I regenerated the initramfs ( `-P` regenerates it for all presets ): # mkinitcpio -P 8. Next, I configured the boot loader by adding to kernel parameters: rd.luks.name=<device-UUID>=root root=/dev/mapper/root I found the device UUID using: `sudo blkid -s UUID -o value /dev/nvme0n1p7`. Surprisingly (for me), the UUID had changed after encrypting the partition. My final bootloader conf file looked like this: title Arch Linux linux /vmlinuz-linux initrd /amd-ucode.img initrd /initramfs-linux.img options rd.luks.name=1df8ea89-4274-4ef9-a670-76c13e612901=root root=/dev/mapper/root rw /boot/loader/entries/arch.conf 9. Lastly, I updated `/etc/fstab`: /dev/mapper/root / ext4 rw,relatime 0 1 /etc/fstab 10. All done. To test it out, I logged out of the chroot environment and rebooted the system. It asked me for the disk encryption password. After entering the password selected in step 3, the system booted up as usual, and everything looked to be working. ## Final Thoughts This was surprisingly easy to do and did not take much time. The ArchWiki was helpful, even if the information was spread over multiple pages/sections. Taking a backup before starting also made me feel safe about the process. I did not like the design of the decryption password prompt at bootup. Maybe there's a way to customize it to look better. Update: I found a way. Details here.
000