[{"content":"KOReader is one of the best upgrades you can give an e-reader. It runs on devices like Kindle, Kobo, PocketBook, Android, and Linux, and it is especially nice if you want more control over formats, typography, PDF handling, OPDS catalogs, and plugins than the stock reader usually provides.\nThe default experience is already powerful, but KOReader gets much more interesting once you add a few focused plugins and connect it to a proper library backend.\nSimpleUI SimpleUI is a great first plugin to try if you want KOReader to feel less like a file browser and more like a dedicated reading dashboard. It adds a home screen, a customizable bottom navigation bar, a top status bar, and a cleaner library title bar.\nThe home screen can show your current book, recent books, collections, reading goals, stats, quick actions, new books, and even a quote of the day. It makes KOReader feel more approachable without hiding the power underneath.\nMost KOReader plugins are installed by copying the .koplugin folder into KOReader\u0026rsquo;s plugin directory. On Kindle this is usually /mnt/us/extensions/koreader/plugins/, on Kobo it is usually .adds/koreader/plugins/, and on desktop Linux it is usually ~/.config/koreader/plugins/. Restart KOReader after copying the plugin so it can be loaded.\nGrimmory for OPDS and progress sync If you keep your books in Grimmory, KOReader can browse the library through OPDS. Enable the OPDS server in Grimmory, create an OPDS user, then add the catalog URL in KOReader. Grimmory\u0026rsquo;s standard OPDS endpoint is /api/v1/opds, so from another device it will look like http://your-server:6060/api/v1/opds or the equivalent URL behind your reverse proxy.\nThis is the cleanest way to get books onto the device because KOReader downloads the file directly from the same library that manages it. That also matters for KOReader progress sync. Grimmory matches books by file content hash, so progress sync works best when the book on the reader was downloaded from Grimmory, usually through OPDS, instead of sideloaded from a different source.\nOnce KOReader sync is enabled in Grimmory, copy the KOReader API path from Grimmory\u0026rsquo;s device settings and enter it in KOReader under the progress sync settings. After logging in with the KOReader-specific credentials, you can push and pull reading progress between KOReader and Grimmory. There is also an optional setting to sync with Grimmory\u0026rsquo;s built-in web reader, though that conversion can be approximate because KOReader and web readers use different position formats.\nReKindle ReKindle is a web-based dashboard for e-ink devices such as Kindle, Kobo, and Boox. It is not a KOReader plugin, but it fits nicely into the same idea of making an e-reader more useful. Open it in the device browser and it gives you a dashboard with apps, tools, games, news-style features, favorites, weather, calendar information, and other lightweight utilities designed for e-ink screens.\nIt is worth trying if your device has a usable browser and you want a simple start page for non-reading tasks.\nRSS inside KOReader KOReader includes RSS support, but there are also interesting plugin alternatives if you want a better reading flow.\nQuickRSS is a standalone RSS reader plugin focused on speed and offline reading. It stores feeds in OPML, caches articles and images to disk, tracks read and unread state, and can use FiveFilters to expand truncated feeds into fuller articles.\nRSS Reader Plugin is another alternative with a broader account model. It supports local feeds as well as services like NewsBlur, CommaFeed, FreshRSS, Miniflux, and Fever-compatible APIs. That makes it especially useful if you already run or use an RSS service and want read-state sync rather than a completely local setup.\nWhen things get weird For technical questions, device-specific quirks, and plugin troubleshooting, the MobileRead forums are still one of the best places to search. KOReader has a long history there, and many problems with Kindle, Kobo, firmware versions, plugins, and installation paths have already been discussed.\nResources:\nKOReader SimpleUI for KOReader Grimmory KOReader Sync Grimmory OPDS ReKindle QuickRSS KOReader plugin RSS Reader KOReader plugin MobileRead forums ","permalink":"https://rustikus.codeberg.page/tech/koreader-plugins-and-library-integration/","summary":"\u003cp\u003e\u003ca href=\"https://github.com/koreader/koreader\"\u003eKOReader\u003c/a\u003e is one of the best upgrades you can give an e-reader. It runs on devices like Kindle, Kobo, PocketBook, Android, and Linux, and it is especially nice if you want more control over formats, typography, PDF handling, OPDS catalogs, and plugins than the stock reader usually provides.\u003c/p\u003e\n\u003cp\u003eThe default experience is already powerful, but KOReader gets much more interesting once you add a few focused plugins and connect it to a proper library backend.\u003c/p\u003e","title":"KOReader Plugins and Library Integration"},{"content":"If you use a Mac with a US keyboard but still write German regularly, the USGerman keyboard layout is a small quality-of-life fix. It keeps the normal US layout and adds German umlauts and ß behind the Option key, so you do not have to switch to a full German keyboard layout or use macOS\u0026rsquo; long-press character picker.\nAfter downloading and unzipping the layout, copy Roman.bundle into the system keyboard layouts folder:\n1 2 sudo mkdir -p \u0026#34;/Library/Keyboard Layouts\u0026#34; sudo cp -R Roman.bundle \u0026#34;/Library/Keyboard Layouts/\u0026#34; Do not put it into ~/Library/Keyboard Layouts on modern macOS, because it can cause trouble when entering administrator passwords.\nThen open System Settings, go to Keyboard, edit Input Sources, add the layout under Others, and select U.S. With Umlauts via Option Key. Log out and back in once so the layout works everywhere.\nThe shortcuts are simple:\n1 2 3 4 Option-a → ä Option-o → ö Option-u → ü Option-s → ß The uppercase variants work with Shift, so Shift-Option-A gives Ä, Shift-Option-O gives Ö, and Shift-Option-U gives Ü.\nResources:\nUSGerman Keyboard Layout ","permalink":"https://rustikus.codeberg.page/tech/usgerman-keyboard-layout-macos/","summary":"\u003cp\u003eIf you use a Mac with a US keyboard but still write German regularly, the \u003ca href=\"https://hci.rwth-aachen.de/usgermankeyboard\"\u003eUSGerman keyboard layout\u003c/a\u003e is a small quality-of-life fix. It keeps the normal US layout and adds German umlauts and \u003ccode\u003eß\u003c/code\u003e behind the Option key, so you do not have to switch to a full German keyboard layout or use macOS\u0026rsquo; long-press character picker.\u003c/p\u003e\n\u003cp\u003eAfter downloading and unzipping the layout, copy \u003ccode\u003eRoman.bundle\u003c/code\u003e into the system keyboard layouts folder:\u003c/p\u003e","title":"Quicktip: USGerman Keyboard Layout on macOS"},{"content":"LG WebOS TVs are notorious for phoning home \u0026ndash; ads on the home screen, tracking via ACR (automatic content recognition), and telemetry across 1,700+ domains including alphonso.tv, lgsmartad.com, and various lge.com subdomains. HaGeZi maintains a dedicated LG WebOS DNS blocklist for all of these that you can add to your DNS blocker of choice.\nOne domain worth blocking separately is snu.lge.com \u0026ndash; this is responsible for the firmware update notification popups. Blocking it stops the TV from nagging you to update, which is useful if you want to stay on a specific firmware version.\nResources:\nHaGeZi\u0026rsquo;s DNS Blocklists ","permalink":"https://rustikus.codeberg.page/tech/blocking-lg-webos-ads-and-tracking/","summary":"\u003cp\u003eLG WebOS TVs are notorious for phoning home \u0026ndash; ads on the home screen, tracking via ACR (automatic content recognition), and telemetry across 1,700+ domains including \u003ccode\u003ealphonso.tv\u003c/code\u003e, \u003ccode\u003elgsmartad.com\u003c/code\u003e, and various \u003ccode\u003elge.com\u003c/code\u003e subdomains. HaGeZi maintains a dedicated \u003ca href=\"https://raw.githubusercontent.com/hagezi/dns-blocklists/main/domains/native.lgwebos.txt\"\u003eLG WebOS DNS blocklist\u003c/a\u003e for all of these that you can add to your DNS blocker of choice.\u003c/p\u003e\n\u003cp\u003eOne domain worth blocking separately is \u003ccode\u003esnu.lge.com\u003c/code\u003e \u0026ndash; this is responsible for the firmware update notification popups. Blocking it stops the TV from nagging you to update, which is useful if you want to stay on a specific firmware version.\u003c/p\u003e","title":"Quicktip: Blocking LG WebOS Ads"},{"content":"LG TVs let you enable a Developer Mode through the LG Content Store, which allows sideloading apps via ares-install. Two apps worth installing:\nyoutube-webos is an actively maintained fork that replaces the stock YouTube app with one that blocks ads, integrates SponsorBlock, and adds features like Return YouTube Dislike, force max quality, and customizable themes. The official YouTube app needs to be uninstalled first.\nMoonfin is an enhanced Jellyfin client built with LG\u0026rsquo;s Enact framework. It supports multi-server unified libraries, Jellyseerr integration for requesting content, and hardware-accelerated playback through the native webOS media pipeline.\nIf you want to keep Developer Mode alive without opening the app all the time, you can refresh the session directly with LG\u0026rsquo;s endpoint: curl \u0026quot;https://developer.lge.com/secure/ResetDevModeSession.dev?sessionToken=YOUR_TOKEN\u0026quot;. You can get that token from the TV itself with ssh webos -T cat /var/luna/preferences/devmode_enabled, then use the value as the sessionToken in your automation.\nIf you want a simpler way to browse and install community apps directly on the TV, the webos-homebrew-channel is worth installing too.\nIf you\u0026rsquo;re on Nix, the ares-cli tools are available in nixpkgs, so you can sideload apps without installing the webOS SDK globally:\n1 2 nix-shell -p ares-cli ares-install -d tv ~/Downloads/youtube.leanback.v4_0.7.3_all_webOS22+.ipk ","permalink":"https://rustikus.codeberg.page/tech/webos-homebrew-apps-for-lg-tvs/","summary":"\u003cp\u003eLG TVs let you enable a \u003ca href=\"https://webostv.developer.lge.com/develop/getting-started/developer-mode-app\"\u003eDeveloper Mode\u003c/a\u003e through the LG Content Store, which allows sideloading apps via \u003ccode\u003eares-install\u003c/code\u003e. Two apps worth installing:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/NicholasBly/youtube-webos\"\u003eyoutube-webos\u003c/a\u003e is an actively maintained fork that replaces the stock YouTube app with one that blocks ads, integrates \u003ca href=\"https://sponsor.ajay.app/\"\u003eSponsorBlock\u003c/a\u003e, and adds features like Return YouTube Dislike, force max quality, and customizable themes. The official YouTube app needs to be uninstalled first.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/Moonfin-Client/WebOS\"\u003eMoonfin\u003c/a\u003e is an enhanced Jellyfin client built with LG\u0026rsquo;s Enact framework. It supports multi-server unified libraries, Jellyseerr integration for requesting content, and hardware-accelerated playback through the native webOS media pipeline.\u003c/p\u003e","title":"Quicktip: WebOS Homebrew Apps for LG TVs"},{"content":"This site uses Forgejo Actions to automatically build and deploy whenever changes are pushed. The repository is named pages, which is required for Codeberg Pages to serve it at the canonical URL.\nThe source files live in the main branch, and the workflow builds the Hugo site and pushes the resulting HTML to a pages branch. After the first deployment, you need to change the default branch to pages in the repository settings.\nHere\u0026rsquo;s the workflow configuration at .forgejo/workflows/deploy.yml:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 name: Deploy Hugo site to Pages on: push: branches: - main workflow_dispatch: permissions: contents: write jobs: build: runs-on: codeberg-small steps: - name: Checkout repository uses: https://code.forgejo.org/actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Install Hugo run: | HUGO_VERSION=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep \u0026#39;\u0026#34;tag_name\u0026#34;\u0026#39; | sed -E \u0026#39;s/.*\u0026#34;v([^\u0026#34;]+)\u0026#34;.*/\\1/\u0026#39;) echo \u0026#34;Detected Hugo version: ${HUGO_VERSION}\u0026#34; if [ -z \u0026#34;$HUGO_VERSION\u0026#34; ]; then echo \u0026#34;Failed to detect Hugo version, using fallback v0.154.5\u0026#34; HUGO_VERSION=\u0026#34;0.154.5\u0026#34; fi # Try to download and install Hugo from GitHub releases if wget -O hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb 2\u0026gt;/dev/null \u0026amp;\u0026amp; sudo dpkg -i hugo.deb 2\u0026gt;/dev/null; then echo \u0026#34;Successfully installed Hugo v${HUGO_VERSION} from GitHub\u0026#34; else echo \u0026#34;Failed to install Hugo from GitHub, falling back to distro package\u0026#34; sudo apt-get update sudo apt-get install -y hugo fi hugo version - name: Build site run: hugo --minify - name: Deploy to pages branch run: | cd public git init git config user.name \u0026#34;Forgejo Actions\u0026#34; git config user.email \u0026#34;actions@noreply.codeberg.org\u0026#34; git add . git commit -m \u0026#34;Deploy from ${GITHUB_SHA}\u0026#34; git push -f https://token:${GITHUB_TOKEN}@codeberg.org/${GITHUB_REPOSITORY}.git HEAD:pages env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_SHA: ${{ github.sha }} GITHUB_REPOSITORY: ${{ github.repository }} The workflow automatically fetches the latest Hugo version from GitHub\u0026rsquo;s API. If the API is unavailable or rate-limited, it falls back to a known working version (v0.154.5). If the download or installation fails for any reason, it falls back to the Hugo package from the distribution\u0026rsquo;s repository. This multi-tiered fallback ensures deployments always succeed.\nReferences:\nCodeberg Pages Forgejo Actions Workflow file ","permalink":"https://rustikus.codeberg.page/tech/deploying-hugo-to-codeberg-pages/","summary":"\u003cp\u003eThis site uses Forgejo Actions to automatically build and deploy whenever changes are pushed. The repository is named \u003ccode\u003epages\u003c/code\u003e, which is required for Codeberg Pages to serve it at the canonical URL.\u003c/p\u003e\n\u003cp\u003eThe source files live in the \u003ccode\u003emain\u003c/code\u003e branch, and the workflow builds the Hugo site and pushes the resulting HTML to a \u003ccode\u003epages\u003c/code\u003e branch. After the first deployment, you need to change the default branch to \u003ccode\u003epages\u003c/code\u003e in the repository settings.\u003c/p\u003e","title":"Deploying Hugo to Codeberg Pages"},{"content":"Last updated: 2026-01-25\nData Collection This website does not collect, store, or process any personal data from visitors. We do not use cookies, analytics tools, or any other tracking mechanisms.\nHosting This website is hosted on Codeberg Pages. The hosting provider may automatically log basic technical information (such as IP addresses) as part of their standard server operation. This information is not accessible to us and is handled according to Codeberg\u0026rsquo;s privacy policy.\nFor more information about how Codeberg handles data, please refer to:\nCodeberg Privacy Policy\nContact If you have any questions about this privacy policy, you can contact us via Codeberg.\n","permalink":"https://rustikus.codeberg.page/privacy/","summary":"\u003cp\u003eLast updated: 2026-01-25\u003c/p\u003e\n\u003ch2 id=\"data-collection\"\u003eData Collection\u003c/h2\u003e\n\u003cp\u003eThis website does not collect, store, or process any personal data from visitors. We do not use cookies, analytics tools, or any other tracking mechanisms.\u003c/p\u003e\n\u003ch2 id=\"hosting\"\u003eHosting\u003c/h2\u003e\n\u003cp\u003eThis website is hosted on \u003ca href=\"https://codeberg.page/\"\u003eCodeberg Pages\u003c/a\u003e. The hosting provider may automatically log basic technical information (such as IP addresses) as part of their standard server operation. This information is not accessible to us and is handled according to Codeberg\u0026rsquo;s privacy policy.\u003c/p\u003e","title":"Privacy Policy"},{"content":"Tailscale is a modern VPN built on WireGuard with automatic mesh networking. For complete self-hosting, use Headscale—an open-source Tailscale control server implementation.\nUniFi Devices: If you run UniFi hardware (Dream Machine, Dream Router, etc.), install Tailscale directly on your gateway:\n1 2 3 4 5 6 7 8 # Install the latest version of Tailscale on UniFi OS 2.x+ curl -sSLq https://raw.github.com/SierraSoftworks/tailscale-udm/main/install.sh | sh # Start Tailscale tailscale up # Advertise routes to your local network tailscale up --advertise-routes=10.0.0.0/24 --advertise-exit-node The installation automatically restarts after UniFi updates, runs via systemd on UniFi OS 2.x+, and supports Tailscale SSH. It works on UniFi Dream Machine, Dream Router, Cloud Key Gen 2, and similar devices.\nManage the service with:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # Restart Tailscale systemctl restart tailscaled # Upgrade Tailscale apt update \u0026amp;\u0026amp; apt install -y tailscale # Or via helper script /data/tailscale/manage.sh update # Enable Tailscale SSH tailscale up --ssh # Generate HTTPS certificates /data/tailscale/manage.sh cert generate /data/tailscale/manage.sh cert install-unifi For full documentation and advanced configuration, see the tailscale-udm GitHub repository.\nOption 2: Self-Hosted Headscale For complete control and privacy, deploy Headscale - a self-hosted Tailscale control server.\nDocker Compose Setup:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 services: headscale: container_name: headscale image: headscale/headscale:v0.26.0 restart: unless-stopped ports: - \u0026#39;8080:8080\u0026#39; - \u0026#39;9090:9090\u0026#39; volumes: - \u0026#39;/data/docker/headscale/config:/etc/headscale\u0026#39; - \u0026#39;/data/docker/headscale/data:/var/lib/headscale\u0026#39; environment: TZ: \u0026#39;America/Montreal\u0026#39; command: serve headplane: container_name: headplane image: ghcr.io/tale/headplane:0.6.0 restart: unless-stopped ports: - \u0026#39;3000:3000\u0026#39; volumes: - \u0026#39;/data/docker/headplane/config/config.yaml:/etc/headplane/config.yaml\u0026#39; - \u0026#39;/data/docker/headscale/config/config.yaml:/etc/headscale/config.yaml\u0026#39; - \u0026#39;/data/docker/headplane/data:/var/lib/headplane\u0026#39; - \u0026#39;/var/run/docker.sock:/var/run/docker.sock:ro\u0026#39; Key Configuration (config.yaml):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 server_url: https://hs.yourdomain.com listen_addr: 0.0.0.0:8080 metrics_listen_addr: 0.0.0.0:9090 database: sqlite: write_ahead_log: true policy: mode: database # Allows ACL management via Headplane UI dns: magic_dns: true base_domain: ts.yourdomain.com override_local_dns: true nameservers: global: - 192.168.1.1 # Your DNS server (e.g., Pi-hole) Headplane Configuration (/etc/headplane/config.yaml):\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server: host: \u0026#34;0.0.0.0\u0026#34; port: 3000 cookie_secret: \u0026#34;your-exactly-32characters-long\u0026#34; cookie_secure: true headscale: url: \u0026#34;https://hs.yourdomain.com\u0026#34; public_url: \u0026#34;https://hs.yourdomain.com\u0026#34; config_path: \u0026#34;/etc/headscale/config.yaml\u0026#34; integration: docker: enabled: true container_name: \u0026#34;headscale\u0026#34; socket: \u0026#34;unix:///var/run/docker.sock\u0026#34; Create API Key:\n1 docker exec headscale headscale apikeys create --expiration 999d Add Users:\n1 docker exec headscale headscale users create USERNAME Access Control via ACL:\nCreate granular access rules using tags:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 { \u0026#34;groups\u0026#34;: { \u0026#34;group:admin\u0026#34;: [\u0026#34;user@\u0026#34;] }, \u0026#34;tagOwners\u0026#34;: { \u0026#34;tag:me\u0026#34;: [\u0026#34;group:admin\u0026#34;], \u0026#34;tag:private\u0026#34;: [\u0026#34;group:admin\u0026#34;], \u0026#34;tag:user\u0026#34;: [\u0026#34;group:admin\u0026#34;] }, \u0026#34;acls\u0026#34;: [ { \u0026#34;action\u0026#34;: \u0026#34;accept\u0026#34;, \u0026#34;src\u0026#34;: [\u0026#34;tag:me\u0026#34;], \u0026#34;dst\u0026#34;: [ \u0026#34;tag:private:*\u0026#34;, \u0026#34;autogroup:internet:*\u0026#34;, \u0026#34;192.168.1.0/24:*\u0026#34; ] }, { \u0026#34;action\u0026#34;: \u0026#34;accept\u0026#34;, \u0026#34;src\u0026#34;: [\u0026#34;tag:user\u0026#34;], \u0026#34;dst\u0026#34;: [ \u0026#34;100.64.0.10:9000\u0026#34;, # Specific service access only \u0026#34;100.64.0.11:8000\u0026#34; ] } ] } Reverse Proxy Setup You need to expose Headscale via HTTPS. Example Caddy configuration:\n1 2 3 4 5 6 7 8 9 10 11 hs.yourdomain.com { # Restrict admin interface to local network @admin_allowed { path /admin /admin/* remote_ip 192.168.1.0/24 100.64.0.0/24 } reverse_proxy @admin_allowed localhost:3000 # Main Headscale server reverse_proxy localhost:8080 } Resources:\nTailscale on UniFi - GitHub: SierraSoftworks/tailscale-udm Headscale Guide - Lucas Janin\u0026rsquo;s Comprehensive Tutorial Official Headscale Headplane UI ","permalink":"https://rustikus.codeberg.page/tech/tailscale-setup-guide/","summary":"\u003cp\u003eTailscale is a modern VPN built on WireGuard with automatic mesh networking. For complete self-hosting, use Headscale—an open-source Tailscale control server implementation.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUniFi Devices\u003c/strong\u003e: If you run UniFi hardware (Dream Machine, Dream Router, etc.), install Tailscale directly on your gateway:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e8\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Install the latest version of Tailscale on UniFi OS 2.x+\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecurl -sSLq https://raw.github.com/SierraSoftworks/tailscale-udm/main/install.sh \u003cspan class=\"p\"\u003e|\u003c/span\u003e sh\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Start Tailscale\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003etailscale up\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Advertise routes to your local network\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003etailscale up --advertise-routes\u003cspan class=\"o\"\u003e=\u003c/span\u003e10.0.0.0/24 --advertise-exit-node\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThe installation automatically restarts after UniFi updates, runs via systemd on UniFi OS 2.x+, and supports Tailscale SSH. It works on UniFi Dream Machine, Dream Router, Cloud Key Gen 2, and similar devices.\u003c/p\u003e","title":"Tailscale Setup Guide: Self-Hosted and UniFi Integration"},{"content":"When using Tailscale with Magic DNS enabled, hostname resolution always routes through Tailscale—even when you\u0026rsquo;re on your home network. This adds unnecessary latency for local connections. The issue is documented in GitHub issue #1227.\nA workaround is to use split DNS with a custom domain. Configure your local router with local DNS entries and set up public DNS entries that point to Tailscale IPs. When you\u0026rsquo;re at home, your devices use the router\u0026rsquo;s DNS. When away, they use public DNS.\nFor example, your local router DNS might have:\nserver.domain.com → 192.168.1.10 nas.domain.com → 192.168.1.20 And your public DNS (via Netcup, Namecheap, etc.):\nserver.domain.com → 100.64.1.10 (Tailscale IP) nas.domain.com → 100.64.1.20 (Tailscale IP) Your devices automatically connect directly when at home and through Tailscale when away, without manual switching. This works even with Magic DNS enabled.\nYou may need to flush DNS cache when switching networks:\n1 sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder ","permalink":"https://rustikus.codeberg.page/tech/bypass-tailscale-routing-for-local-lan/","summary":"\u003cp\u003eWhen using Tailscale with Magic DNS enabled, hostname resolution always routes through Tailscale—even when you\u0026rsquo;re on your home network. This adds unnecessary latency for local connections. The issue is documented in \u003ca href=\"https://github.com/tailscale/tailscale/issues/1227\"\u003eGitHub issue #1227\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eA workaround is to use split DNS with a custom domain. Configure your local router with local DNS entries and set up public DNS entries that point to Tailscale IPs. When you\u0026rsquo;re at home, your devices use the router\u0026rsquo;s DNS. When away, they use public DNS.\u003c/p\u003e","title":"Quicktip: Bypass Tailscale Routing for Local LAN"},{"content":"Kubernetes CLI To Manage Your Clusters In Style!\nK9s provides a terminal UI to interact with your Kubernetes clusters. The aim of this project is to make it easier to navigate, observe and manage your applications in the wild. K9s continually watches Kubernetes for changes and offers subsequent commands to interact with your observed resources.\nLink: https://github.com/derailed/k9s\nWebsite: https://k9scli.io\n","permalink":"https://rustikus.codeberg.page/tech/k9s/","summary":"\u003cp\u003e\u003cstrong\u003eKubernetes CLI To Manage Your Clusters In Style!\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eK9s provides a terminal UI to interact with your Kubernetes clusters. The aim of this project is to make it easier to navigate, observe and manage your applications in the wild. K9s continually watches Kubernetes for changes and offers subsequent commands to interact with your observed resources.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLink:\u003c/strong\u003e \u003ca href=\"https://github.com/derailed/k9s\"\u003ehttps://github.com/derailed/k9s\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWebsite:\u003c/strong\u003e \u003ca href=\"https://k9scli.io\"\u003ehttps://k9scli.io\u003c/a\u003e\u003c/p\u003e","title":"k9s"},{"content":"When using AeroSpace (a tiling window manager for macOS), GUI applications don\u0026rsquo;t have Homebrew\u0026rsquo;s prefix in their PATH by default. This means Homebrew binaries like sketchybar or yabai won\u0026rsquo;t be found unless you specify the full path.\nAeroSpace handles this automatically with a sensible default configuration. If you don\u0026rsquo;t override the [exec] section, it uses:\n1 2 3 4 [exec] inherit-env-vars = true [exec.env-vars] PATH = \u0026#39;/opt/homebrew/bin:/opt/homebrew/sbin:${PATH}\u0026#39; If you need custom environment variables, preserve the Homebrew PATH when overriding the [exec] section:\n1 2 3 4 5 6 [exec] inherit-env-vars = true [exec.env-vars] PATH = \u0026#39;/opt/homebrew/bin:/opt/homebrew/sbin:${PATH}\u0026#39; # Add your custom environment variables here MY_CUSTOM_VAR = \u0026#39;value\u0026#39; On Intel Macs, Homebrew installs to /usr/local instead:\n1 2 [exec.env-vars] PATH = \u0026#39;/usr/local/bin:/usr/local/sbin:${PATH}\u0026#39; With the PATH configured, you can run Homebrew commands directly:\n1 2 3 4 5 6 7 # Works because Homebrew is in PATH exec-on-workspace-change = \u0026#39;\u0026#39;\u0026#39; sketchybar --trigger aerospace_workspace_change \u0026#39;\u0026#39;\u0026#39; # Or execute custom scripts on-focused-monitor-changed = [\u0026#39;my-custom-script.sh\u0026#39;] Resources:\nHomebrew PATH Documentation AeroSpace GitHub ","permalink":"https://rustikus.codeberg.page/tech/aerospace-homebrew-path-macos/","summary":"\u003cp\u003eWhen using \u003ca href=\"https://github.com/nikitabobko/AeroSpace\"\u003eAeroSpace\u003c/a\u003e (a tiling window manager for macOS), GUI applications don\u0026rsquo;t have Homebrew\u0026rsquo;s prefix in their \u003ccode\u003ePATH\u003c/code\u003e by default. This means Homebrew binaries like \u003ccode\u003esketchybar\u003c/code\u003e or \u003ccode\u003eyabai\u003c/code\u003e won\u0026rsquo;t be found unless you specify the full path.\u003c/p\u003e\n\u003cp\u003eAeroSpace handles this automatically with a sensible default configuration. If you don\u0026rsquo;t override the \u003ccode\u003e[exec]\u003c/code\u003e section, it uses:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eexec\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003einherit-env-vars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eexec\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv-vars\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003ePATH\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;/opt/homebrew/bin:/opt/homebrew/sbin:${PATH}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eIf you need custom environment variables, preserve the Homebrew PATH when overriding the \u003ccode\u003e[exec]\u003c/code\u003e section:\u003c/p\u003e","title":"AeroSpace: Fixing Homebrew PATH for GUI Apps on macOS"},{"content":"Remarkably Organized is a web-based tool for generating custom PDF planners. Here\u0026rsquo;s a preconfigured version optimized for note-taking on Kindle Scribe or iPad:\nGenerate PDF Planner for iPad or Kindle Scribe\nThe configuration includes 11 note pages per week, week numbers in the navigation, and Baden-Württemberg school holidays. You can adjust all settings in the sidebar, add your own iCal calendar feeds, and toggle sections as needed. The URL encodes all your settings, so you can bookmark it and regenerate your planner anytime with updated dates.\n","permalink":"https://rustikus.codeberg.page/tech/generate-pdf-planner-for-kindle-scribe/","summary":"\u003cp\u003e\u003ca href=\"https://remarkably-organized.pages.dev/planner\"\u003eRemarkably Organized\u003c/a\u003e is a web-based tool for generating custom PDF planners. Here\u0026rsquo;s a preconfigured version optimized for note-taking on Kindle Scribe or iPad:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"https://remarkably-organized.pages.dev/planner?help=0\u0026amp;settings=%7B%22quarterPage%22%3A%7B%22disable%22%3Atrue%7D%2C%22weekPage%22%3A%7B%22notePagesAmount%22%3A11%2C%22useWeekNumbersInSideNav%22%3Atrue%7D%2C%22calendars%22%3A%5B%7B%22events%22%3A%5B%5D%2C%22url%22%3A%22https%3A%2F%2Fwww.schulferien.org%2Fmedia%2Fical%2Fdeutschland%2Fferien_baden-wuerttemberg_2025.ics%3Fk%3Dofx7qOasaB-WJt05fYm74XylwpcuUABdte3QRcNFvbKa69k1rQIxfurMXCtDbkWLohZjGYGycTlVp6EIs3l54VKQULowXnLGtb5X57smKeg%22%2C%22lastUpdated%22%3A0%2C%22name%22%3A%22%22%7D%5D%7D#2025\"\u003eGenerate PDF Planner for iPad or Kindle Scribe\u003c/a\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eThe configuration includes 11 note pages per week, week numbers in the navigation, and Baden-Württemberg school holidays. You can adjust all settings in the sidebar, add your own iCal calendar feeds, and toggle sections as needed. The URL encodes all your settings, so you can bookmark it and regenerate your planner anytime with updated dates.\u003c/p\u003e","title":"Generate a PDF Planner for Kindle Scribe or iPad"},{"content":"A collection of resources for setting up Nix on macOS with nix-darwin and home-manager.\nThe nix-darwin-kickstarter repository is a good starting point for macOS.\nRun programs without installing them with nix-shell -p cowsay.\nRemote Deployment: nixos-rebuild announcement\nExample configurations:\njdheyburn/nixos-configs\nGreat examples for:\nCaddy web server\nFreshRSS\ndeploy-rs\nTailscale integration\nwimpysworld/nix-config - Production-ready configuration\nRemote installation with nixos-anywhere: nixos-anywhere uses kexec. If kexec doesn\u0026rsquo;t work, boot with NixOS ISO instead.\nWireless in initrd: Unlocking encrypted drives over WiFi\nSecrets management with SOPS: SOPS works with evaluation-time secrets (for config files) and runtime secrets (using sops-nix or agenix). Decryption can use any private key in .sops.yaml, but creating/updating encrypted files requires the master key. Update keys with sops updatekeys secrets/epsilon.yaml.\nAdditional resources:\nFull disk encryption unlock via USB Auto-mounting encrypted drives ","permalink":"https://rustikus.codeberg.page/tech/nix-setup-on-macos-overview/","summary":"\u003cp\u003eA collection of resources for setting up Nix on macOS with nix-darwin and home-manager.\u003c/p\u003e\n\u003cp\u003eThe \u003cstrong\u003e\u003ca href=\"https://github.com/ryan4yin/nix-darwin-kickstarter\"\u003enix-darwin-kickstarter\u003c/a\u003e\u003c/strong\u003e repository is a good starting point for macOS.\u003c/p\u003e\n\u003cp\u003eRun programs without installing them with \u003ccode\u003enix-shell -p cowsay\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRemote Deployment\u003c/strong\u003e: \u003ca href=\"https://www.haskellforall.com/2023/01/announcing-nixos-rebuild-new-deployment.html\"\u003enixos-rebuild announcement\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eExample configurations\u003c/strong\u003e:\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/jdheyburn/nixos-configs/tree/main\"\u003ejdheyburn/nixos-configs\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\nGreat examples for:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003eCaddy web server\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFreshRSS\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003edeploy-rs\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eTailscale integration\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/wimpysworld/nix-config\"\u003ewimpysworld/nix-config\u003c/a\u003e\u003c/strong\u003e - Production-ready configuration\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003eRemote installation with nixos-anywhere\u003c/strong\u003e: \u003ca href=\"https://github.com/nix-community/nixos-anywhere\"\u003enixos-anywhere\u003c/a\u003e uses kexec. If kexec doesn\u0026rsquo;t work, \u003ca href=\"https://github.com/nix-community/nixos-anywhere/blob/main/docs/howtos/no-os.md\"\u003eboot with NixOS ISO instead\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWireless in initrd\u003c/strong\u003e: \u003ca href=\"https://discourse.nixos.org/t/wireless-connection-within-initrd/38317/12\"\u003eUnlocking encrypted drives over WiFi\u003c/a\u003e\u003c/p\u003e","title":"Nix Setup on macOS: An Overview"},{"content":"Kanata provides more readable keyboard remapping configurations than Karabiner-Elements. To run it automatically on macOS startup, set it up as a LaunchDaemon.\nInstall Kanata via Homebrew:\n1 brew install kanata Download and install the Karabiner VirtualHIDDevice driver. Kanata needs this to interact with macOS.\nCreate your Kanata configuration at ~/.config/kanata/config.kbd. See the documentation for examples. Add a key combo to reload your configuration (command: lrld) to avoid manually restarting.\nCreate two .plist files in /Library/LaunchDaemons/ to launch Kanata at startup.\nFirst, org.custom.karabiner.plist for the Karabiner driver:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;org.custom.karabiner\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-VirtualHIDDevice-Daemon.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Daemon\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; Next, create org.custom.kanata.plist to launch Kanata:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 \u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;org.custom.kanata\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/opt/homebrew/bin/kanata\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;--cfg\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;/Users/YOUR_USERNAME/.config/kanata/config.kbd\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; Important: Replace /Users/YOUR_USERNAME/.config/kanata/config.kbd with your actual .kbd file path. To find the correct Kanata binary path, run which kanata in your terminal.\nLoad both daemons with these commands:\n1 2 sudo launchctl load /Library/LaunchDaemons/org.custom.karabiner.plist sudo launchctl load /Library/LaunchDaemons/org.custom.kanata.plist Kanata should start running in the background within a few seconds. It will now launch automatically on every boot.\nStop Kanata with sudo launchctl unload /Library/LaunchDaemons/org.custom.kanata.plist. Emergency escape: press Left Control + Space + Escape at their original positions.\nCredit:\nSetup based on configuration shared by u/matklad on Reddit ","permalink":"https://rustikus.codeberg.page/tech/running-kanata-on-macos-with-launchdaemon/","summary":"\u003cp\u003eKanata provides more readable keyboard remapping configurations than Karabiner-Elements. To run it automatically on macOS startup, set it up as a LaunchDaemon.\u003c/p\u003e\n\u003cp\u003eInstall Kanata via Homebrew:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ebrew install kanata\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eDownload and install the \u003ca href=\"https://github.com/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/releases\"\u003eKarabiner VirtualHIDDevice driver\u003c/a\u003e. Kanata needs this to interact with macOS.\u003c/p\u003e\n\u003cp\u003eCreate your Kanata configuration at \u003ccode\u003e~/.config/kanata/config.kbd\u003c/code\u003e. See the \u003ca href=\"https://github.com/jtroo/kanata\"\u003edocumentation\u003c/a\u003e for examples. Add a key combo to reload your configuration (command: \u003ccode\u003elrld\u003c/code\u003e) to avoid manually restarting.\u003c/p\u003e\n\u003cp\u003eCreate two \u003ccode\u003e.plist\u003c/code\u003e files in \u003ccode\u003e/Library/LaunchDaemons/\u003c/code\u003e to launch Kanata at startup.\u003c/p\u003e","title":"Running Kanata on macOS with LaunchDaemon"},{"content":" Note: This article is heavily based on The Uncommitted Changes Blog by Pankaj Raghav. All credit goes to the original author.\nSetting up auto-mounting for a LUKS-encrypted external drive on NixOS requires coordinating LUKS decryption, udev device detection, and systemd mounting. Here\u0026rsquo;s how to configure it so the drive automatically decrypts and mounts when connected.\nFirst, encrypt the drive if you haven\u0026rsquo;t already:\n1 2 3 4 5 6 cryptsetup luksFormat /dev/sda1 ## Enter password cryptsetup open /dev/sda1 vault dd bs=512 count=4 if=/dev/random of=/root/keyfile.key iflag=fullblock cryptsetup luksAddKey /dev/sda1 /root/keyfile.key sudo mkfs.btrfs -L vault /dev/mapper/vault The keyfile allows automatic decryption without typing a password each time. Your device layout will look like:\n1 2 3 4 $ lsblk -f nvme1n1 └─nvme1n1p1 crypto_LUKS 2 1342cc60-7514-4d70-8d1b-303b009cea34 └─vault btrfs vault e04b44ad-1beb-4902-9b91-e5e6ed43e51c 369.6G 20% /mnt/vault Configure /etc/crypttab to automatically decrypt the drive. The noauto option prevents systemd from trying to unlock at boot:\n1 2 3 environment.etc.crypttab.text = \u0026#39;\u0026#39; vault UUID=1342cc60-7514-4d70-8d1b-303b009cea34 /root/mykeyfile.key noauto \u0026#39;\u0026#39;; Note that the UUID here is the partition\u0026rsquo;s UUID (the LUKS container), not the filesystem UUID. This generates a systemd service systemd-cryptsetup@vault.service that you can test manually:\n1 sudo systemctl start systemd-cryptsetup@vault.service Use a udev rule to trigger decryption when the drive is connected. First, identify your device\u0026rsquo;s unique identifier:\n1 udevadm info /dev/sda1 Create a udev rule that starts the cryptsetup service when your specific device is detected:\n1 2 3 4 services.udev.extraRules = \u0026#39;\u0026#39; SUBSYSTEM==\u0026#34;block\u0026#34; ENV{ID_WWN}==\u0026#34;nvme.144d-933432304e50305234983030383659-53616d73756e6720506f727461626c6520535344205835-00000001\u0026#34;,\\ ENV{SYSTEMD_WANTS}=\u0026#34;systemd-cryptsetup@vault.service\u0026#34; \u0026#39;\u0026#39;; Replace ID_WWN with your device\u0026rsquo;s unique identifier from udevadm info. Then configure the mount point for the decrypted filesystem:\n1 2 3 4 5 6 7 8 9 10 11 fileSystems.\u0026#34;/mnt/vault\u0026#34; = { device = \u0026#34;/dev/disk/by-uuid/e04b44ad-1beb-4902-9b91-e5e6ed43e51c\u0026#34;; fsType = \u0026#34;btrfs\u0026#34;; options = [ \u0026#34;defaults\u0026#34; \u0026#34;noatime\u0026#34; \u0026#34;x-systemd.automount\u0026#34; \u0026#34;x-systemd.device-timeout=5\u0026#34; \u0026#34;noauto\u0026#34; ]; }; This UUID is the filesystem UUID (the decrypted /dev/mapper/vault), not the partition UUID. The noauto option prevents mounting at boot, while x-systemd.automount handles automatic mounting when the device is detected.\nHere\u0026rsquo;s the complete external-disk.nix configuration:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 { environment.etc.crypttab.text = \u0026#39;\u0026#39; vault UUID=1342cc60-7514-4d70-8d1b-303b009cea34 /root/mykeyfile.key noauto \u0026#39;\u0026#39;; # The above crypttab creates a systemd cryptsetup vault service, which the below udev rule depends on services.udev.extraRules = \u0026#39;\u0026#39; SUBSYSTEM==\u0026#34;block\u0026#34; ENV{ID_WWN}==\u0026#34;nvme.144d-933432304e50305234983030383659-53616d73756e6720506f727461626c6520535344205835-00000001\u0026#34;, ENV{SYSTEMD_WANTS}=\u0026#34;systemd-cryptsetup@vault.service\u0026#34; \u0026#39;\u0026#39;; fileSystems.\u0026#34;/mnt/vault\u0026#34; = { device = \u0026#34;/dev/disk/by-uuid/e04b44ad-1beb-4902-9b91-e5e6ed43e51c\u0026#34;; fsType = \u0026#34;btrfs\u0026#34;; options = [ \u0026#34;defaults\u0026#34; \u0026#34;noatime\u0026#34; \u0026#34;x-systemd.automount\u0026#34; \u0026#34;x-systemd.device-timeout=5\u0026#34; \u0026#34;noauto\u0026#34; ]; }; } Now when you connect your external drive, it will automatically decrypt and mount—perfect for automated backup workflows with btrfs snapshots.\nCredit:\nAuto-mounting encrypted external drive in NixOS by Pankaj Raghav ","permalink":"https://rustikus.codeberg.page/tech/auto-mounting-encrypted-external-drive-nixos/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e This article is heavily based on \u003ca href=\"https://blog.pankajraghav.com/2024/09/17/AUTOMOUNT.html\"\u003eThe Uncommitted Changes Blog\u003c/a\u003e by Pankaj Raghav. All credit goes to the original author.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003cp\u003eSetting up auto-mounting for a LUKS-encrypted external drive on NixOS requires coordinating LUKS decryption, udev device detection, and systemd mounting. Here\u0026rsquo;s how to configure it so the drive automatically decrypts and mounts when connected.\u003c/p\u003e\n\u003cp\u003eFirst, encrypt the drive if you haven\u0026rsquo;t already:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e6\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-sh\" data-lang=\"sh\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecryptsetup luksFormat /dev/sda1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e## Enter password\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecryptsetup open /dev/sda1 vault\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003edd \u003cspan class=\"nv\"\u003ebs\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"m\"\u003e512\u003c/span\u003e \u003cspan class=\"nv\"\u003ecount\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"m\"\u003e4\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e/dev/random \u003cspan class=\"nv\"\u003eof\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e/root/keyfile.key \u003cspan class=\"nv\"\u003eiflag\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003efullblock\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecryptsetup luksAddKey /dev/sda1 /root/keyfile.key\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo mkfs.btrfs -L vault /dev/mapper/vault\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThe keyfile allows automatic decryption without typing a password each time. Your device layout will look like:\u003c/p\u003e","title":"Auto-mounting Encrypted External Drive in NixOS"},{"content":"Some monitors don\u0026rsquo;t properly report their capabilities (EDID - Extended Display Identification Data) to Linux systems during early boot, causing incorrect resolutions or no video output. You can fix this by incorporating custom EDID data into the initrd image.\nFirst, extract the EDID from a working system. Install the required tools:\n1 sudo apt install read-edid edid-decode Extract and verify the EDID:\n1 2 sudo get-edid \u0026gt; desktop_edid.bin edid-decode desktop_edid.bin Transfer the file to your target system and copy it to the firmware directory:\n1 sudo cp desktop_edid.bin /usr/lib/firmware/desktop_edid.bin Create a hook script to add the EDID firmware to the initrd:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 cat \u0026lt;\u0026lt;EOF | sudo tee /etc/initramfs-tools/hooks/desktop_edid.sh #!/bin/sh # Copy local EDID monitor description data PREREQ=\u0026#34;\u0026#34; prereqs() { echo \u0026#34;\\$PREREQ\u0026#34; } case \\$1 in prereqs) prereqs exit 0 ;; esac . /usr/share/initramfs-tools/hook-functions EDID_DATA=\u0026#34;/usr/lib/firmware/desktop_edid.bin\u0026#34; if [ ! -f \u0026#34;\\$EDID_DATA\u0026#34; ]; then exit 0 fi add_firmware \u0026#34;\\$(basename \\$EDID_DATA)\u0026#34; exit 0 EOF Make it executable and regenerate initramfs:\n1 2 sudo chmod +x /etc/initramfs-tools/hooks/desktop_edid.sh sudo update-initramfs -u -v Configure the kernel to use the custom EDID. For GRUB, edit /etc/default/grub and add to GRUB_CMDLINE_LINUX:\n1 drm.edid_firmware=edid/desktop_edid.bin Or for specific connectors:\n1 video=HDMI-A-1:D drm.edid_firmware=HDMI-A-1:edid/desktop_edid.bin Update GRUB and reboot:\n1 sudo update-grub Your display will now use the correct EDID from early boot, ensuring proper resolution and video output.\nResources:\nHow to incorporate EDID into initrd image by sleeplessbeastie ","permalink":"https://rustikus.codeberg.page/tech/incorporating-edid-into-initrd-linux/","summary":"\u003cp\u003eSome monitors don\u0026rsquo;t properly report their capabilities (EDID - Extended Display Identification Data) to Linux systems during early boot, causing incorrect resolutions or no video output. You can fix this by incorporating custom EDID data into the initrd image.\u003c/p\u003e\n\u003cp\u003eFirst, extract the EDID from a working system. Install the required tools:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt install read-edid edid-decode\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eExtract and verify the EDID:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo get-edid \u0026gt; desktop_edid.bin\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eedid-decode desktop_edid.bin\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eTransfer the file to your target system and copy it to the firmware directory:\u003c/p\u003e","title":"Incorporating EDID into initrd Image on Linux"},{"content":"Using shell variables like $HOME in LazyVim\u0026rsquo;s nvim-lint configuration doesn\u0026rsquo;t work as expected—the string gets passed literally instead of being expanded. To use a custom markdownlint-cli2 config file with a machine-agnostic path, use Lua\u0026rsquo;s os.getenv() function:\n1 2 3 4 5 6 7 8 9 10 11 12 local HOME = os.getenv(\u0026#34;HOME\u0026#34;) return { \u0026#34;mfussenegger/nvim-lint\u0026#34;, optional = true, opts = { linters = { [\u0026#34;markdownlint-cli2\u0026#34;] = { args = { \u0026#34;--config\u0026#34;, HOME .. \u0026#34;/.markdownlint-cli2.yaml\u0026#34;, \u0026#34;--\u0026#34; }, }, }, }, } Create your ~/.markdownlint-cli2.yaml configuration file with your preferred rules:\n1 2 config: MD013: false Resources:\nLazyVim Discussion #4094 ","permalink":"https://rustikus.codeberg.page/tech/configuring-markdownlint-in-lazyvim/","summary":"\u003cp\u003eUsing shell variables like \u003ccode\u003e$HOME\u003c/code\u003e in LazyVim\u0026rsquo;s nvim-lint configuration doesn\u0026rsquo;t work as expected—the string gets passed literally instead of being expanded. To use a custom markdownlint-cli2 config file with a machine-agnostic path, use Lua\u0026rsquo;s \u003ccode\u003eos.getenv()\u003c/code\u003e function:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-lua\" data-lang=\"lua\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003elocal\u003c/span\u003e \u003cspan class=\"n\"\u003eHOME\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos.getenv\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;HOME\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"s2\"\u003e\u0026#34;mfussenegger/nvim-lint\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eoptional\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"n\"\u003eopts\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elinters\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;markdownlint-cli2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eargs\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;--config\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eHOME\u003c/span\u003e \u003cspan class=\"o\"\u003e..\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/.markdownlint-cli2.yaml\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;--\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eCreate your \u003ccode\u003e~/.markdownlint-cli2.yaml\u003c/code\u003e configuration file with your preferred rules:\u003c/p\u003e","title":"Configuring markdownlint-cli2 in LazyVim"},{"content":"To access an encrypted Btrfs Arch Linux system from a live USB, you need to unlock the LUKS partition and mount each Btrfs subvolume before chrooting:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 # Unlock the encrypted partition cryptsetup open /dev/nvme0n1 cryptlvm # Mount the root subvolume mount -o subvol=@ /dev/mapper/cryptlvm /mnt # Mount the home subvolume mount -o subvol=@home /dev/mapper/cryptlvm /mnt/home # Mount the log subvolume with nodatacow option mount -o nodatacow,subvol=@var_log /dev/mapper/cryptlvm /mnt/var/log # Chroot into the system sudo arch-chroot /mnt The cryptsetup open command unlocks the encrypted partition and maps it to /dev/mapper/cryptlvm. Each Btrfs subvolume needs to be mounted separately. The nodatacow option on /var/log prevents copy-on-write for log files. The arch-chroot command automatically handles mounting /proc, /sys, and /dev.\n","permalink":"https://rustikus.codeberg.page/tech/mounting-encrypted-btrfs-arch-linux/","summary":"\u003cp\u003eTo access an encrypted Btrfs Arch Linux system from a live USB, you need to unlock the LUKS partition and mount each Btrfs subvolume before chrooting:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Unlock the encrypted partition\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ecryptsetup open /dev/nvme0n1 cryptlvm\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Mount the root subvolume\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emount -o \u003cspan class=\"nv\"\u003esubvol\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e@ /dev/mapper/cryptlvm /mnt\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Mount the home subvolume\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emount -o \u003cspan class=\"nv\"\u003esubvol\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e@home /dev/mapper/cryptlvm /mnt/home\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Mount the log subvolume with nodatacow option\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emount -o nodatacow,subvol\u003cspan class=\"o\"\u003e=\u003c/span\u003e@var_log /dev/mapper/cryptlvm /mnt/var/log\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Chroot into the system\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo arch-chroot /mnt\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003ecryptsetup open\u003c/code\u003e command unlocks the encrypted partition and maps it to \u003ccode\u003e/dev/mapper/cryptlvm\u003c/code\u003e. Each Btrfs subvolume needs to be mounted separately. The \u003ccode\u003enodatacow\u003c/code\u003e option on \u003ccode\u003e/var/log\u003c/code\u003e prevents copy-on-write for log files. The \u003ccode\u003earch-chroot\u003c/code\u003e command automatically handles mounting \u003ccode\u003e/proc\u003c/code\u003e, \u003ccode\u003e/sys\u003c/code\u003e, and \u003ccode\u003e/dev\u003c/code\u003e.\u003c/p\u003e","title":"Mounting an Encrypted Btrfs System in Arch Linux"},{"content":" It is not the critic who counts: not the man who points out how the strong man stumbles or where the doer of deeds could have done better. The credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood, who strives valiantly, who errs and comes up short again and again, because there is no effort without error or shortcoming, but who knows the great enthusiasms, the great devotions, who spends himself in a worthy cause; who, at the best, knows, in the end, the triumph of high achievement, and who, at the worst, if he fails, at least he fails while daring greatly, so that his place shall never be with those cold and timid souls who knew neither victory nor defeat.\n—Theodore Roosevelt\nSpeech at the Sorbonne, Paris, April 23, 1910\n","permalink":"https://rustikus.codeberg.page/posts/the-man-in-the-arena/","summary":"\u003cblockquote\u003e\n\u003cp\u003eIt is not the critic who counts: not the man who points out how the strong man stumbles or where the doer of deeds could have done better. The credit belongs to the man who is actually \u003cstrong\u003ein the arena\u003c/strong\u003e, whose face is marred by dust and sweat and blood, who strives valiantly, who errs and comes up short again and again, because there is no effort without error or shortcoming, but who knows the great enthusiasms, the great devotions, who spends himself in a worthy cause; who, at the best, knows, in the end, the triumph of high achievement, and who, at the worst, if he fails, at least he fails while daring greatly, so that his place shall never be with those cold and timid souls who knew neither victory nor defeat.\u003c/p\u003e","title":"The Man in the Arena"}]