<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-AU">
<title>Michael Murphy</title>
<subtitle>Personal blog of Michael Murphy</subtitle>
<link rel="self" type="application/atom+xml" href="https://mich-murphy.com/atom.xml"/>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/"/>
<id>https://mich-murphy.com/atom.xml</id>
<updated>2025-10-30T00:00:00+00:00</updated>
<author><name>Michael Murphy</name></author>
<generator uri="https://gohugo.io/" version="0.166.0">Hugo</generator>
<entry>
<title>Syncing Plex Watch State to Jellyfin</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/syncing-plex-watch-state/"/>
<id>https://mich-murphy.com/syncing-plex-watch-state/</id>
<published>2024-04-08T00:00:00+00:00</published>
<updated>2024-04-08T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I have been using &lt;a href=&#34;https://plex.tv&#34;&gt;Plex&lt;/a&gt; to manage and serve the media on my homelab for the&#xA;last few years. After recent &lt;a href=&#34;https://www.pcgamer.com/self-hosted-media-app-starts-narcing-on-its-own-users-anime-and-x-rated-habits-with-an-opt-out-service-and-its-going-terribly/&#34;&gt;worrying trends&lt;/a&gt; have started to emerge on Plex I have started exploring a migration to &lt;a href=&#34;https://jellyfin.org&#34;&gt;Jellyfin&lt;/a&gt; instead.&lt;/p&gt;&#xA;&lt;p&gt;In this post I&amp;rsquo;ll detail how I am syncing my watch state between both Plex and Jellyfin as I decide which one to stick with.&lt;/p&gt;&#xA;&lt;h2 id=&#34;plex-services&#34;&gt;Plex Services&lt;/h2&gt;&#xA;&lt;p&gt;I currently use Plex for management of my media, which includes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Movies&lt;/li&gt;&#xA;&lt;li&gt;TV&lt;/li&gt;&#xA;&lt;li&gt;Music - specifically &lt;a href=&#34;https://www.plex.tv/plexamp/&#34;&gt;Plexamp&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Audiobooks - utilising &lt;a href=&#34;https://github.com/djdembeck/Audnexus.bundle&#34;&gt;audNexus&lt;/a&gt; as my metadata provider&lt;/li&gt;&#xA;&lt;li&gt;YouTube&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In addition to Plex, I setup &lt;a href=&#34;https://tautulli.com/&#34;&gt;Tautulli&lt;/a&gt; for tracking server usage and &lt;a href=&#34;https://overseerr.dev/&#34;&gt;Overseerr&lt;/a&gt; for managing media requests.&lt;/p&gt;&#xA;&lt;p&gt;Configuration for all of this is managed by NixOS - &lt;a href=&#34;https://github.com/mich-murphy/nix-config/blob/a945a6ed39917bfbe51de433f2b51318db4ba885/nixos/modules/media/plex.nix&#34;&gt;as seen in my Plex module&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;jellyfin-configuration&#34;&gt;Jellyfin Configuration&lt;/h2&gt;&#xA;&lt;p&gt;I run a much more simple setup when it comes to Jellyfin, with it managing:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Movies&lt;/li&gt;&#xA;&lt;li&gt;TV&lt;/li&gt;&#xA;&lt;li&gt;Music - I will look to switch to a different solution if this proves too basic&lt;/li&gt;&#xA;&lt;li&gt;YouTube&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I manage audiobooks via &lt;a href=&#34;https://www.audiobookshelf.org/&#34;&gt;Audiobookshelf&lt;/a&gt; as a&#xA;dedicated service, as it does a much better job than Plex at managing audiobook&#xA;librarires.&lt;/p&gt;&#xA;&lt;p&gt;My Jellyfin configuration is also managed by NixOS and is &lt;a href=&#34;https://github.com/mich-murphy/nix-config/blob/a945a6ed39917bfbe51de433f2b51318db4ba885/nixos/modules/media/jellyfin.nix&#34;&gt;available on GitHub&lt;/a&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;The Jellyfin setup is a bit more involved when it comes to hardware transcoding.&#xA;I have included a few useful notes in my linked config with helpful resources.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h2 id=&#34;syncing-watch-status&#34;&gt;Syncing Watch Status&lt;/h2&gt;&#xA;&lt;p&gt;After doing some initial research I came across a project for this purpose aptly named &lt;a href=&#34;https://github.com/arabcoders/watchstate&#34;&gt;watchstate&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;watchstatus-configuration&#34;&gt;Watchstatus Configuration&lt;/h3&gt;&#xA;&lt;p&gt;This was pretty easy to setup via &lt;code&gt;docker-compose&lt;/code&gt;. I followed the instructions on GitHub and first created a&#xA;&lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;YAML · docker-compose.yml&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;YAML code, docker-compose.yml&#34;&gt;&lt;code&gt;services:&#xA;  watchstate:&#xA;    image: ghcr.io/arabcoders/watchstate:latest&#xA;    network_mode: &amp;#34;host&amp;#34; # enable connection to host vpn&#xA;    user: &amp;#34;1000:1000&amp;#34;&#xA;    container_name: watchstate&#xA;    restart: unless-stopped&#xA;    environment:&#xA;      - WS_TZ=Australia/Melbourne&#xA;    ports:&#xA;      - &amp;#34;8001:8080&amp;#34; # avoid clashing with other service on 8080&#xA;    volumes:&#xA;      - ./data:/config:rw # mount current directory to container /config directory.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Following this I created a &lt;code&gt;data&lt;/code&gt; folder (passed as a volume to container) in the same&#xA;directory with: &lt;code&gt;mkdir -p data&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Finally I started the container by running:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# install docker-compose for the current shell in NixOS&#xA;nix shell nixpkgs#docker-compose&#xA;&#xA;# start the container in the background&#xA;sudo docker-compose up -d&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;adding-backend&#34;&gt;Adding Backend&lt;/h3&gt;&#xA;&lt;p&gt;With the container running I was then able to add a new backend. This refers to setting&#xA;up the connection to a media service such as Plex or Jellyfin.&lt;/p&gt;&#xA;&lt;p&gt;This was done by running the following commands and completing the prompts:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;docker exec -ti watchstate console config:add&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;You will need to extract a &lt;a href=&#34;https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/&#34;&gt;Plex token&lt;/a&gt; and retrieve&#xA;you Jellyfin API as part of this setup: Go to Dashboard &amp;gt; Advanced &amp;gt; API keys &amp;gt; then create new api keys&lt;/p&gt;&#xA;&lt;p&gt;The only issue I ran into was due to me being unfamiliar with the terminology.&#xA;I wanted to sync my watch status from Plex to Jellyfin. According to Watchstate&#xA;terminology this means I want to &lt;code&gt;import&lt;/code&gt; my Plex watch state to the database&#xA;and &lt;code&gt;export&lt;/code&gt; the database state into Jellyfin.&lt;/p&gt;&#xA;&lt;p&gt;Once I understood this I was able to setup backends for both Plex and Jellyfin.&lt;/p&gt;&#xA;&lt;h3 id=&#34;syncing-backends&#34;&gt;Syncing Backends&lt;/h3&gt;&#xA;&lt;p&gt;With both backends configured I first exported my Plex state:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# I named my plex backend plex_media&#xA;sudo docker exec -ti watchstate console state:import -v -s plex_media&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Then finally I export the database state to Jellyfin, following initial instructions&#xA;to forcibly sync the initial export:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# I named my jellyfin backend jellyfin_media&#xA;sudo docker exec -ti watchstate console state:import -vvifs jellyfin_media&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Following this I checked Jellyfin and sure enough, everything was in sync!&lt;/p&gt;&#xA;&lt;h3 id=&#34;scheduling-regular-sync&#34;&gt;Scheduling Regular Sync&lt;/h3&gt;&#xA;&lt;p&gt;The project mentioned the option of setting up cron jobs for regular syncing&#xA;ongoing. I chose not to implement this, as I am happy to manage this manually&#xA;myself. I&amp;rsquo;ll evaluate Jellyfin as a replacement to Plex and stick with one or&#xA;the other.&lt;/p&gt;&#xA;&lt;p&gt;Should I decide to run this services long-term then I&amp;rsquo;d create a NixOS module for it.&lt;/p&gt;&#xA;&lt;p&gt;If you decide to schedule syncing its just a matter of setting the following&#xA;environment variable in &lt;code&gt;docker-compose.yml&lt;/code&gt;: &lt;code&gt;WS_CRON_IMPORT=1&lt;/code&gt; and &lt;code&gt;WS_CRON_EXPORT=1&lt;/code&gt;&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Configuring a Minecraft Server Using NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/nixos-minecraft-server/"/>
<id>https://mich-murphy.com/nixos-minecraft-server/</id>
<published>2024-04-02T00:00:00+00:00</published>
<updated>2024-04-03T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;Minecraft can be configured simply by using the provided &lt;a href=&#34;https://search.nixos.org/options?size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=minecraft&#34;&gt;NixOS modules&lt;/a&gt;. Tailscale have published a &lt;a href=&#34;https://tailscale.com/kb/1096/nixos-minecraft&#34;&gt;blog post&lt;/a&gt; showing how to utilise this and get a working server.&lt;/p&gt;&#xA;&lt;p&gt;This setup is a bit limited when it comes to installing mods and mod toolchains declaratively. &lt;a href=&#34;https://github.com/Infinidoge/nix-minecraft&#34;&gt;Nix-minecraft&lt;/a&gt; was developed for this purpose.&lt;/p&gt;&#xA;&lt;h2 id=&#34;nixos-minecraft-configuration-with-mods&#34;&gt;NixOS Minecraft Configuration With Mods&lt;/h2&gt;&#xA;&lt;p&gt;To use Nix-minecraft, add the following to an existing &lt;code&gt;flake.nix&lt;/code&gt;, or create a new one like so:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · flake.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, flake.nix&#34;&gt;&lt;code&gt;{&#xA;  description = &amp;#34;NixOS Minecraft Server&amp;#34;;&#xA;&#xA;  inputs = {&#xA;    nixpkgs.url = &amp;#34;github:nixos/nixpkgs/nixos-unstable&amp;#34;;&#xA;    # Add nix-minecraft input&#xA;    nix-minecraft.url = &amp;#34;github:Infinidoge/nix-minecraft&amp;#34;;&#xA;  };&#xA;&#xA;  outputs = {&#xA;    self,&#xA;    nixpkgs,&#xA;    # Add nix-minecraft input&#xA;    nix-minecraft,&#xA;    ...&#xA;  } @ inputs: {&#xA;    nixosConfigurations.media = nixpkgs.lib.nixosSystem {&#xA;      specialArgs = {inherit inputs;};&#xA;      modules = [&#xA;        ./minecraft.nix&#xA;        agenix.nixosModules.default&#xA;        {&#xA;          # Add overlay for nix-minecraft&#xA;          nixpkgs.overlays = [&#xA;            nix-minecraft.overlay&#xA;          ];&#xA;        }&#xA;      ];&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;minecraft-configuration-file&#34;&gt;Minecraft Configuration File&lt;/h3&gt;&#xA;&lt;p&gt;Then create a new nix file for server configuration, the following shows an example of how to do this using a nix module format.&lt;/p&gt;&#xA;&lt;p&gt;Available mods for install can be found at &lt;a href=&#34;https://modrinth.com/&#34;&gt;Modrinth&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Nix-minecraft provides a helper scrip to identify the &lt;code&gt;fetchurl&lt;/code&gt; and &lt;code&gt;sha512&lt;/code&gt; hash needed to install a mod. To use it, find a mod on Modrinth and click on the version you want. In the displayed information, there is a Version ID string. Click on it to copy the version ID. Then run the following on a system with nix flakes enabled, replacing with the copied version ID: &lt;code&gt;nix run github:Infinidoge/nix-minecraft#nix-modrinth-prefetch -- versionid&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Once you&amp;rsquo;ve got the details for mods to be install, you can finalise server configuration like so:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · minecraft.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, minecraft.nix&#34;&gt;&lt;code&gt;{&#xA;  lib,&#xA;  config,&#xA;  pkgs,&#xA;  # Used to import nix-minecraft&#xA;  inputs,&#xA;  ...&#xA;}:&#xA;with lib; let&#xA;  cfg = config.common.minecraft;&#xA;  # Target minecraft version&#xA;  mcVersion = &amp;#34;1.20.1&amp;#34;;&#xA;  # Version of toolchain (fabric in this case)&#xA;  fabricVersion = &amp;#34;0.15.7&amp;#34;;&#xA;  # Format minecraft version, replacing . with _&#xA;  serverVersion = lib.replaceStrings [&amp;#34;.&amp;#34;] [&amp;#34;_&amp;#34;] &amp;#34;fabric-${mcVersion}&amp;#34;;&#xA;in {&#xA;  # Import nix-minecraft to provide path to mod toolchain packages&#xA;  imports = [&#xA;    inputs.nix-minecraft.nixosModules.minecraft-servers&#xA;  ];&#xA;&#xA;  options.common.minecraft = {&#xA;    enable = mkEnableOption &amp;#34;Enable Minecraft Server&amp;#34;;&#xA;  };&#xA;&#xA;  config = mkIf cfg.enable {&#xA;    services = {&#xA;      # Server configuration module options&#xA;      minecraft-servers = {&#xA;        enable = true;&#xA;        eula = true; # accepting EULA is required for server to run&#xA;        # Rename and clear out /srv/minecraft to setup new server&#xA;        servers.server-name = {&#xA;          enable = true;&#xA;          package = pkgs.fabricServers.${serverVersion}.override {loaderVersion = fabricVersion;};&#xA;          serverProperties = {&#xA;            # Adjust server properties to fit needs&#xA;            server-port = 25565;&#xA;            gamemode = &amp;#34;peaceful&amp;#34;;&#xA;            motd = &amp;#34;NixOS Pokemon&amp;#34;; # Server name&#xA;            max-players = 2;&#xA;            level-seed = &amp;#34;10292758&amp;#34;; # Seed for level generation&#xA;          };&#xA;          symlinks = {&#xA;            # List all mods to be installed&#xA;            mods = pkgs.linkFarmFromDrvs &amp;#34;mods&amp;#34; (builtins.attrValues {&#xA;              # Example values returned by nix run command&#xA;              FabricApi = pkgs.fetchurl {&#xA;                url = &amp;#34;https://cdn.modrinth.com/data/P7dR8mSH/versions/YG53rBmj/fabric-api-0.92.0%2B1.20.1.jar&amp;#34;;&#xA;                sha512 = &amp;#34;53ce4cb2bb5579cef37154c928837731f3ae0a3821dd2fb4c4401d22d411f8605855e8854a03e65ea4f949dfa0e500ac1661a2e69219883770c6099b0b28e4fa&amp;#34;;&#xA;              };&#xA;              Pokemon = pkgs.fetchurl {&#xA;                url = &amp;#34;https://cdn.modrinth.com/data/MdwFAVRL/versions/uWAkNUxZ/Cobblemon-fabric-1.4.1%2B1.20.1.jar&amp;#34;;&#xA;                sha512 = &amp;#34;6955c8ad187d727cbfc51761312258600c5480878983cfe710623070c90eb437e419c140ff3c77e5066164876ecfe1e31b87f58f5ef175f0758efcff246b85a8&amp;#34;;&#xA;              };&#xA;            });&#xA;          };&#xA;        };&#xA;      };&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;additional-resources&#34;&gt;Additional Resources&lt;/h3&gt;&#xA;&lt;p&gt;Another helpful reference was &lt;a href=&#34;https://github.com/Misterio77/nix-config/tree/0ed82f3d63a366eafbacb8eee27985afe30b249a/hosts/celaeno/services/minecraft&#34;&gt;Misterio77&amp;rsquo;s NixOS Minecraft config&lt;/a&gt;&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Remote Deployment of NixOS Using nixos-anywhere and Disko</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/nixos-anywhere-and-disko/"/>
<id>https://mich-murphy.com/nixos-anywhere-and-disko/</id>
<published>2024-03-27T00:00:00+00:00</published>
<updated>2024-03-27T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;Now that the use cases for my homelab are growing, I&amp;rsquo;m finding a need to separate different services into their own virtual machines. This has been a bit tedious in the past, as each time I&amp;rsquo;ve had to format disk and create a new NixOS configuration.&lt;/p&gt;&#xA;&lt;p&gt;Nixos-anywhere and Disko combine together to make this simple. This post explains how to use these tools.&lt;/p&gt;&#xA;&lt;h2 id=&#34;nixos-anywhere--disko&#34;&gt;NixOS Anywhere &amp;amp; Disko&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/nix-community/nixos-anywhere&#34;&gt;Nixos-anywhere&lt;/a&gt; allows for remote deployment of NixOS via &lt;code&gt;kexec&lt;/code&gt; on any Linux based operating system. This provides a quick method to convert any virtual machine to NixOS and build the new system according to a specified NixOS configuration.&lt;/p&gt;&#xA;&lt;p&gt;Nixos-anywhere utilises &lt;a href=&#34;https://github.com/nix-community/disko&#34;&gt;Disko&lt;/a&gt;, which allows for declarative disk preparation and formatting. This means we can deploy without the usual &lt;code&gt;fdisk/gparted&lt;/code&gt; preparation.&lt;/p&gt;&#xA;&lt;h3 id=&#34;disko-configuration&#34;&gt;Disko Configuration&lt;/h3&gt;&#xA;&lt;p&gt;First start by specifying how you want Disko to configure your disks. You can find &lt;a href=&#34;https://github.com/nix-community/disko/blob/master/example/multi-device-no-deps.nix&#34;&gt;an example Disko config&lt;/a&gt; in the Disko repository&lt;/p&gt;&#xA;&lt;p&gt;The following configuration is what I used to format an SSD drive, mounted at &lt;code&gt;/dev/sda&lt;/code&gt;, creating partitions for EFI boot and operating system storage:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · hosts/services/disk-config.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, hosts/services/disk-config.nix&#34;&gt;&lt;code&gt;{...}: {&#xA;  disko.devices = {&#xA;    disk = {&#xA;      # To specify an additional drive, create another entry e.g. disk.data&#xA;      main = {&#xA;        device = &amp;#34;/dev/sda&amp;#34;;&#xA;        type = &amp;#34;disk&amp;#34;;&#xA;        content = {&#xA;          type = &amp;#34;gpt&amp;#34;;&#xA;          partitions = {&#xA;            # Boot partition formatted for EFI&#xA;            boot = {&#xA;              size = &amp;#34;1M&amp;#34;;&#xA;              type = &amp;#34;EF02&amp;#34;; # for grub MBR&#xA;            };&#xA;            ESP = {&#xA;              name = &amp;#34;ESP&amp;#34;;&#xA;              size = &amp;#34;512M&amp;#34;;&#xA;              type = &amp;#34;EF00&amp;#34;;&#xA;              content = {&#xA;                type = &amp;#34;filesystem&amp;#34;;&#xA;                format = &amp;#34;vfat&amp;#34;;&#xA;                mountpoint = &amp;#34;/boot&amp;#34;;&#xA;              };&#xA;            };&#xA;            # Optional swap partition&#xA;            swap = {&#xA;              size = &amp;#34;8G&amp;#34;;&#xA;              content = {&#xA;                type = &amp;#34;swap&amp;#34;;&#xA;                randomEncryption = true;&#xA;                resumeDevice = true; # resume from hiberation from this device&#xA;              };&#xA;            };&#xA;            # Root partition for operating system storage&#xA;            root = {&#xA;              size = &amp;#34;100%&amp;#34;;&#xA;              content = {&#xA;                type = &amp;#34;filesystem&amp;#34;;&#xA;                format = &amp;#34;ext4&amp;#34;;&#xA;                mountpoint = &amp;#34;/&amp;#34;;&#xA;              };&#xA;            };&#xA;          };&#xA;        };&#xA;      };&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;nixos-anywhere-configuration&#34;&gt;NixOS Anywhere Configuration&lt;/h3&gt;&#xA;&lt;p&gt;With the Disko configuration complete, we now need to update our flake and add inputs for Disko. We also need to add the Disko module within our &lt;code&gt;nixosConfiguration&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Here is an example:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · flake.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, flake.nix&#34;&gt;&lt;code&gt;{&#xA;  description = &amp;#34;Nixos-anywhere deployed flake&amp;#34;;&#xA;&#xA;  inputs = {&#xA;    nixpkgs.url = &amp;#34;github:nixos/nixpkgs/nixos-unstable&amp;#34;;&#xA;&#xA;    disko.url = &amp;#34;github:nix-community/disko&amp;#34;;&#xA;    disko.inputs.nixpkgs.follows = &amp;#34;nixpkgs&amp;#34;;&#xA;&#xA;    # Optional: used for secret sharing&#xA;    agenix.url = &amp;#34;github:ryantm/agenix&amp;#34;;&#xA;    agenix.inputs.nixpkgs.follows = &amp;#34;nixpkgs&amp;#34;;&#xA;    agenix.inputs.home-manager.follows = &amp;#34;home-manager&amp;#34;;&#xA;&#xA;    # Optional: used for deployment of multiple systems&#xA;    deploy-rs.url = &amp;#34;github:serokell/deploy-rs&amp;#34;;&#xA;    deploy-rs.inputs.nixpkgs.follows = &amp;#34;nixpkgs&amp;#34;;&#xA;  };&#xA;&#xA;  outputs = {&#xA;    self,&#xA;    nixpkgs,&#xA;    disko,&#xA;    # Optional: as above, these aren&amp;#39;t required for nixos-anywhere&#xA;    agenix,&#xA;    deploy-rs,&#xA;    ...&#xA;  } @ inputs: {&#xA;    # Specify hostname e.g. services&#xA;    nixosConfigurations.services = nixpkgs.lib.nixosSystem {&#xA;      # Allow inputs to be available from within module scope&#xA;      specialArgs = {inherit inputs;};&#xA;      modules = [&#xA;        # Specify system configuration to load&#xA;        ./hosts/services/configuration.nix&#xA;        # Optional: import secret management module&#xA;        agenix.nixosModules.default&#xA;        # Importing the disko module for formatting disks&#xA;        disko.nixosModules.disko&#xA;      ];&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;The most important part in the above is adding an input for &lt;code&gt;disko&lt;/code&gt; and loading the disko module within the &lt;code&gt;nixosConfiguration&lt;/code&gt;: &lt;code&gt;disko.nixosModules.disko&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Importantly we must also add additional details to the configuration file specified. In the case of the above we provided the path: &lt;code&gt;./hosts/services/configuration&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Here are the details we need to add:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · hosts/services/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, hosts/services/configuration.nix&#34;&gt;&lt;code&gt;{ modulesPath, config, lib, pkgs, ... }: {&#xA;  imports = [&#xA;    (modulesPath + &amp;#34;/installer/scan/not-detected.nix&amp;#34;)&#xA;    # Specify qemu guest, relevant for virtual machines&#xA;    (modulesPath + &amp;#34;/profiles/qemu-guest.nix&amp;#34;)&#xA;    # Import path to disko configuration, covered above&#xA;    ./disk-config.nix&#xA;  ];&#xA;  boot.loader.grub = {&#xA;    # Specify boot devices, should match disko configuration&#xA;    devices = [&amp;#34;/dev/sda&amp;#34;];&#xA;    # Whether to support EFI boot&#xA;    efiSupport = true;&#xA;    efiInstallAsRemovable = true;&#xA;  };&#xA;  # Enable connection via SSH post nixos-anywhere deployment&#xA;  services.openssh.enable = true;&#xA;&#xA;  environment.systemPackages = [&#xA;    # Specify any packages you want installed by default&#xA;    pkgs.vim&#xA;  ];&#xA;&#xA;  users.users.root.openssh.authorizedKeys.keys = [&#xA;    # Specify SSH key for connection post nixos-anywhere deployment&#xA;    &amp;#34;ssh-ed25519 AAAAC3NzaC1lZDI4uJE5AAAAIEVyN0R5mTtfcbkmVXjicuvSRotJY4IuT7h3H&amp;#34;&#xA;  ];&#xA;&#xA;  # Default recommended with NixOS install&#xA;  system.stateVersion = &amp;#34;23.11&amp;#34;;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Here are references to a couple of example configurations I found useful: &lt;a href=&#34;https://github.com/nix-community/nixos-anywhere-examples/blob/main/flake.nix&#34;&gt;nixos-anywhere example flake&lt;/a&gt;, &lt;a href=&#34;https://github.com/nix-community/nixos-anywhere-examples/blob/main/configuration.nix&#34;&gt;nixos-anywhere example config&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;deployment&#34;&gt;Deployment&lt;/h3&gt;&#xA;&lt;p&gt;With all of the above configuration in place, it&amp;rsquo;s time to deploy. I normally create a bare bones virtual machine on my homelab using a &lt;a href=&#34;https://nixos.org/download/&#34;&gt;minimal nixos iso&lt;/a&gt; - we don&amp;rsquo;t have to use a NixOS iso here, we could use Debian, Ubuntu etc. After starting the virtual machine we can complete the following steps from within the console to prepare for deployment:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;We need to be able to run the nixos-anywhere command and connect via SSH to the new virtual machine. The NixOS iso sets up &lt;code&gt;openssh&lt;/code&gt; by default, we just need to set the root password to authenticate a connection: &lt;code&gt;sudo passwd root&lt;/code&gt; and follow the prompts&lt;/li&gt;&#xA;&lt;li&gt;Confirm the IP address of the virtual machine so that we can connect: &lt;code&gt;ip a&lt;/code&gt; and look for the valid IP&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Now we can complete the deployment. From a machine with nix installed and flakes enable we can run the following command:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;nix run github:nix-community/nixos-anywhere -- --flake &amp;lt;path-to-flake&amp;gt;#&amp;lt;flake-name&amp;gt; root@&amp;lt;ip-address&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s an example with details added - note you can optionally specify &lt;code&gt;--build-on-remote&lt;/code&gt; if the host architecture is different on your target:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;nix run github:nix-community/nixos-anywhere -- --flake .#services root@192.168.1.254 --build-on-remote&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;agenix-post-deployment-setup&#34;&gt;Agenix Post Deployment Setup&lt;/h3&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve explained in &lt;a href=&#34;https://mich-murphy.com/encrypting-secrets-nixos/&#34;&gt;another post&lt;/a&gt; how I use Agenix to manage secrets. Assuming you are doing this on your new NixOS host there are a couple of steps you need to take to configure Agenix.&lt;/p&gt;&#xA;&lt;p&gt;First is to find the &lt;code&gt;ssh host key&lt;/code&gt; for your new NixOS host: &lt;code&gt;ssh-keyscan &amp;lt;host-ip&amp;gt;&lt;/code&gt;. This will show each available ssh key on your host. You can use this information to add the new ssh key to your Agenix configuration:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · secrets/secrets.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, secrets/secrets.nix&#34;&gt;&lt;code&gt;let&#xA;  # Existing configuration&#xA;  existing_system = &amp;#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH&amp;#34;;&#xA;  # Newly added SSH key discovered by ssh-keyscan&#xA;  new_system = &amp;#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILI6jSq53F/3hEmSs+oq9L4TwOo1PrDMAgcA1uo1CCV/&amp;#34;;&#xA;  all_systems = [ existing_system new_system ];&#xA;in&#xA;{&#xA;  # Configured secrets&#xA;  &amp;#34;secret1.age&amp;#34;.publicKeys = all_systems;&#xA;  &amp;#34;secret2.age&amp;#34;.publicKeys = [ existing_system ];&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Now that we have added a new SSH key to our &lt;code&gt;secrets.nix&lt;/code&gt; file we must &lt;a href=&#34;https://github.com/ryantm/agenix?tab=readme-ov-file#rekeying&#34;&gt;rekey&lt;/a&gt; our existing secrets, and specify our existing ssh key: &lt;code&gt;nix run github:ryantm/agenix --rekey -i ~/.ssh/existing_key&lt;/code&gt;.&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Restoring Nextcloud from Backup on NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/restore-nextcloud-from-backup-nixos/"/>
<id>https://mich-murphy.com/restore-nextcloud-from-backup-nixos/</id>
<published>2024-03-25T00:00:00+00:00</published>
<updated>2024-03-25T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;Occasionally when self hosting I&amp;rsquo;ve found a need to backup and restore the Nextcloud&#xA;database. I have a regularly scheduled Postgres backup thanks to the following&#xA;nix config:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Nix&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code&#34;&gt;&lt;code&gt;{ lib, pkgs, ... }:&#xA;&#xA;{&#xA;  services.postgresqlBackup = {&#xA;    enable = true;&#xA;    # path where backup should be saved&#xA;    location = &amp;#34;/data/backups/postgresql&amp;#34;;&#xA;    # specifying nextcloud as the database to backup&#xA;    databases = [&amp;#34;nextcloud&amp;#34;];&#xA;    # cron schedule for backup to be performed&#xA;    startAt = &amp;#34;*-*-* 23:15:00&amp;#34;;&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;This post will detail the process I follow to do a restore from the backup&#xA;created by the above config.&lt;/p&gt;&#xA;&lt;h2 id=&#34;restoring-nextcloud-database&#34;&gt;Restoring Nextcloud Database&lt;/h2&gt;&#xA;&lt;p&gt;The first thing to understand is how to execute database commands from within&#xA;the NixOS environment. My Nextcloud instance in NixOS is managed by the &lt;code&gt;nextcloud&lt;/code&gt;&#xA;user, this same user is configured for the database as shown in my &lt;a href=&#34;https://mich-murphy.com/configure-nextcloud-nixos/&#34;&gt;Nextcloud post&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I can execute &lt;a href=&#34;https://wiki.nixos.org/wiki/Nextcloud#Nextcloudcmd&#34;&gt;commands against the database&lt;/a&gt; (&lt;a href=&#34;https://wiki.nixos.org/wiki/PostgreSQL&#34;&gt;PostgreSQL in my case&lt;/a&gt;) like so:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo runuser -u nextcloud -- psql -U nextcloud &amp;lt;options&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Using the information above we can drop the old database which we are replacing&#xA;and then create a new one, in which we will later restore data to. These steps&#xA;are documented on the &lt;a href=&#34;https://docs.nextcloud.com/server/latest/admin_manual/maintenance/restore.html#postgresql&#34;&gt;Nextcloud website&lt;/a&gt;. The commands&#xA;I used are below:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo runuser -u nextcloud -- psql -U nextcloud -c &amp;#34;DROP DATABASE \&amp;#34;nextcloud\&amp;#34;;&amp;#34;&#xA;sudo runuser -u nextcloud -- psql -U nextcloud -c &amp;#34;CREATE DATABASE \&amp;#34;nextcloud\&amp;#34;;&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;You can then restore the backup into the newly created Nextcloud database with&#xA;the following command:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo runuser -u nextcloud -- psql -U nextcloud -d nextcloud -f nextcloud-sqlbkp.bak&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;I discovered the above command didn&amp;rsquo;t work for me, as the scheduled backup&#xA;utilizes &lt;code&gt;pg_dump&lt;/code&gt;, which creates a &lt;code&gt;backup.sql.gz&lt;/code&gt; file. After some searching&#xA;I came across a &lt;a href=&#34;https://dba.stackexchange.com/questions/258910/how-to-do-a-restore-of-a-large-postgresql-database&#34;&gt;Stack Exchange post&lt;/a&gt; which lead me to the final solution:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo runuser -u nextcloud -- pg_restore -U nextcloud -d nextcloud nextcloud.sql.gz&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;post-restore-steps&#34;&gt;Post Restore Steps&lt;/h2&gt;&#xA;&lt;p&gt;After the database has been restored head to the Nextcloud admin console:&#xA;&lt;code&gt;https://nextcloud.example.com/settings/admin/overview&lt;/code&gt; and see if the self&#xA;check reveals any issues with the new database. Chances are you will need&#xA;to resolve some issues with the database indices. The following command&#xA;will add missing indices to the database:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo -i nextcloud-occ db:add-missing-indices&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;</content>
</entry>
<entry>
<title>Troubleshooting Proxmox Installation</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/resolving-proxmox-installation-issues/"/>
<id>https://mich-murphy.com/resolving-proxmox-installation-issues/</id>
<published>2023-07-18T00:00:00+00:00</published>
<updated>2025-10-30T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I encountered some issues when installing Proxmox on a new homelab. One method of solving these would have been to &lt;a href=&#34;https://pve.proxmox.com/wiki/Install_Proxmox_VE_on_Debian_11_Bullseye&#34;&gt;install Debian and then Proxmox&lt;/a&gt; from there. I decided to try my hand at troubleshooting the issues instead.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-proxmox&#34;&gt;Why Proxmox&lt;/h2&gt;&#xA;&lt;p&gt;Before describing the issues I faced and how I resolved them, I though I should explain why I&amp;rsquo;m using Proxmox in the first place - why persevere through the pain?&lt;/p&gt;&#xA;&lt;p&gt;The end state for my homelab is to have a number of different Virtual Machines (VMs) running segregated services. There are a few reasons I want to run multiple VMs:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;To segment any data used for services to the specific VM I&amp;rsquo;m running it in&lt;/li&gt;&#xA;&lt;li&gt;Most VMs will be running NixOS - I want to manage them remotely and become more familiar with tools like &lt;a href=&#34;https://github.com/serokell/deploy-rs&#34;&gt;deploy-rs&lt;/a&gt; and &lt;a href=&#34;https://github.com/numtide/nixos-anywhere/&#34;&gt;nixos-anywhere&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;I want to be able to use VMs from any machine to easily experiment with new Linux distributions&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;I mostly want to use VMs over containers as currently &lt;a href=&#34;https://wiki.nixos.org/wiki/Proxmox_Linux_Container&#34;&gt;NixOS doesn&amp;rsquo;t work so well with LXC&lt;/a&gt;, it&amp;rsquo;s possible, but I haven&amp;rsquo;t been thrilled with the result. I will use LXC for running some services like [Home Assistant].&lt;/p&gt;&#xA;&lt;h2 id=&#34;initial-boot&#34;&gt;Initial Boot&lt;/h2&gt;&#xA;&lt;p&gt;The first issue I had was getting the Proxmox install step to boot from USB. I kept getting an error saying: &lt;code&gt;/dev fully populated&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;I managed to find a &lt;a href=&#34;https://forum.proxmox.com/threads/solved-fresh-proxmox-install-with-dev-to-be-fully-populated-error.20211/&#34;&gt;post on the Proxmox forum&lt;/a&gt; which suggested changing the boot flags to resolve the error:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# e at boot menu add nomodeset&#xA;GRUB_CMDLINE_LINUX_DEFAULT=&amp;#34;quiet nomodeset&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;This let me proceed to the install step for Proxmox.&lt;/p&gt;&#xA;&lt;h2 id=&#34;booting-after-install&#34;&gt;Booting After Install&lt;/h2&gt;&#xA;&lt;p&gt;After installing Proxmox I rebooted my machine and found I hit a similar error again - adding the aforementioned &lt;code&gt;nomodeset&lt;/code&gt; didn&amp;rsquo;t work as I found Proxmox was using systemd-boot rather than grub.&lt;/p&gt;&#xA;&lt;p&gt;This was resolved by the following thanks to a &lt;a href=&#34;https://www.reddit.com/r/Proxmox/comments/is00kp/any_way_to_set_the_proxmox_installer_to_use_safe/&#34;&gt;post on Reddit&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Text&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Text code&#34;&gt;&lt;code&gt;for EFI boot (systemd-boot) add `nomodeset` to /etc/kernel/cmdline after installation; run `pve-efiboot-tool refresh` to update&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;setting-safe-mode-to-boot-from-intel-graphics&#34;&gt;Setting Safe Mode to Boot from Intel Graphics&lt;/h2&gt;&#xA;&lt;p&gt;I thought I was finally done and ready to start setting up some VMs - but no, I hit a fatal server error, which required me to change my vga settings. I found a &lt;a href=&#34;https://www.youtube.com/watch?v=-6fRTpmmuHs&#34;&gt;YouTube video which explained the changes&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;lspci | grep -i vga&#xA;mkdir -p /usr/share/X11/xorg.conf.d&#xA;vi /usr/share/X11/xorg.conf.d/10-fbdev.conf&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Enter the following text in &lt;code&gt;10-fbdev.conf&lt;/code&gt;&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Text · /usr/share/X11/xorg.conf.d/10-fbdev.conf&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Text code, /usr/share/X11/xorg.conf.d/10-fbdev.conf&#34;&gt;&lt;code&gt;Section &amp;#34;Device&amp;#34;&#xA;    Identifier &amp;#34;Card0&amp;#34;&#xA;    Driver &amp;#34;fbdev&amp;#34;&#xA;    BusID &amp;#34;pci0:2:0:0&amp;#34;&#xA;EndSection&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Finally the system booted and I was able to start setting up Proxmox.&lt;/p&gt;&#xA;&lt;h2 id=&#34;bonus-tip---gpu-passthrough&#34;&gt;Bonus Tip - GPU Passthrough&lt;/h2&gt;&#xA;&lt;p&gt;Finally I wanted to leave some references I found helpful for configuring GPU passthrough for a VM in Proxmox. I needed to do this for my Jellyfin service, so I could take advantage of Intel QuickSync. For further reference on setting up and testing hardware transcoding checkout the &lt;a href=&#34;https://wiki.archlinux.org/title/Hardware_video_acceleration#Configuring_VA-API&#34;&gt;Jellyfin documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;You can pass through an Intel GPU via PCI passthrough, as detailed in &lt;a href=&#34;https://pve.proxmox.com/wiki/PCI_Passthrough&#34;&gt;Proxmox documentation&lt;/a&gt;&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Useful References for Understanding Nix/NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/nix-useful-references/"/>
<id>https://mich-murphy.com/nix-useful-references/</id>
<published>2023-05-08T00:00:00+00:00</published>
<updated>2023-09-16T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I recently used Nix to &lt;a href=&#34;https://github.com/mich-murphy/nix-config&#34;&gt;configure my M2 Macbook Air&lt;/a&gt;, and manage all of my dotfiles. In the process I found a lot of useful references, detailing the ins and outs of Nix and NixOS.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m currently building out a similar solution for my homelab, and thought to document a list of useful references here.&lt;/p&gt;&#xA;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://nix.dev/&#34;&gt;Nix Dev&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://nixos.org/manual/nix/unstable/introduction.html&#34;&gt;Nix Manual&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://nixos.org/manual/nixos/stable/index.html#nixos-manual&#34;&gt;NixOS Manual&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://nix-community.github.io/home-manager/options.html&#34;&gt;Home Manager Manual&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://daiderd.com/nix-darwin/manual/index.html#sec-options&#34;&gt;Nix Darwin Manual&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://devenv.sh/getting-started&#34;&gt;Devenv Developer Environments&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://zero-to-nix.com&#34;&gt;Determinate Systems Zero to Nix&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/playlist?list=PLyzwHTVJlRc8yjlx4VR4LU5A5O44og9in&#34;&gt;The Nix Hour - YouTube Playlist&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;useful-posts&#34;&gt;Useful Posts&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/MatthiasBenaets/nixos-config&#34;&gt;Matthias Benaet&amp;rsquo;s Dotfiles&lt;/a&gt;: this is where I first began, Matthias has a really good video series introduction as well, you can find the details in the linked repo&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://jdisaacs.com/blog/nixos-config/&#34;&gt;Jordan Isaac on Nix Flakes&lt;/a&gt;: comprehensive introduction into creating a Flake in Nix&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://serokell.io/blog/practical-nix-flakes&#34;&gt;Alexander Bantyev on Nix Flakes&lt;/a&gt;: great beginner friendly guide&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://bobvanderlinden.me/customizing-packages-in-nix/&#34;&gt;Bob Vanderlinden on Customising Nix Packages&lt;/a&gt;: as you get deeper into the rabbit hole of Nix, you will likely need to customise Nix packages, this post clearly explains the different ways you can accomplish this, and why you would use one over another&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://xeiaso.net/blog/paranoid-nixos-2021-07-18&#34;&gt;Xe Iaso on Securing NixOS&lt;/a&gt;: details how to create a more secure NixOS when running a production server&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://xeiaso.net/blog/paranoid-nixos-aws-2021-08-11&#34;&gt;Xe Iaso on Creating a Secure AWS NixOS Image&lt;/a&gt;: detailed walk through of code to create a secure NixOS image in AWS&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://grahamc.com/blog/erase-your-darlings&#34;&gt;Graham Christensen on Impermanence in NixOS&lt;/a&gt;: explains how to setup a NixOS system that erases at boot, and why the approach is useful - also checkout the &lt;a href=&#34;https://github.com/nix-community/impermanence&#34;&gt;nix-community impermanence repo&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/&#34;&gt;Elis Hirwing on tmpfs as root&lt;/a&gt;: alternative method of impermanence on NixOS&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content>
</entry>
<entry>
<title>Using Dev Containers to Standardise Development Environments</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/dev-containers-vscode/"/>
<id>https://mich-murphy.com/dev-containers-vscode/</id>
<published>2023-04-11T00:00:00+00:00</published>
<updated>2023-04-11T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;After running my own homelab for a while, I&amp;rsquo;ve become pretty familiar with using containers - mostly via Docker, though I may have to make the switch to podman after a &lt;a href=&#34;https://www.servethehome.com/docker-abruptly-starts-charging-many-users-for-docker-desktop/&#34;&gt;recent announcement&lt;/a&gt;. One thing I wasn&amp;rsquo;t aware of was the ability to run containers for the specific purpose of creating a development environment.&lt;/p&gt;&#xA;&lt;h2 id=&#34;configuring-a-dev-container&#34;&gt;Configuring a Dev Container&lt;/h2&gt;&#xA;&lt;p&gt;I first came across this feature in Visual Studio Code (I try to avoid it, but sometimes its a necessary evil :P), there is a lot of great &lt;a href=&#34;https://code.visualstudio.com/docs/devcontainers/containers&#34;&gt;documentation&lt;/a&gt; which covers the topic in far more detail than I plan to.&lt;/p&gt;&#xA;&lt;p&gt;The pre-requisites for setting up a Dev Container are Visual Studio Code (VSCode) and the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&#34;&gt;Dev Containers&lt;/a&gt; plugin.&lt;/p&gt;&#xA;&lt;p&gt;The basic idea is that you create a &lt;code&gt;Dockerfile&lt;/code&gt; or &lt;code&gt;docker-compose&lt;/code&gt; file, which sets up a container, you can then specify a &lt;code&gt;devcontainer.json&lt;/code&gt; file which lets you configure additional settings unique to VSCode e.g. plugins that you want installed inside of the container, and/or VSCode settings you want active whilst in the container.&lt;/p&gt;&#xA;&lt;p&gt;VSCode will even walk you through the process of creating the container itself if you don&amp;rsquo;t have a &lt;code&gt;Dockerfile&lt;/code&gt; already on hand: &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; -&amp;gt; &lt;code&gt;Dev Containers: New Dev Container...&lt;/code&gt; will get you started.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://containers.dev/implementors/spec/#devcontainerjson&#34;&gt;Follow this link&lt;/a&gt; for a list of all the options available when configuring &lt;code&gt;devcontainer.json&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Here is an example of a quick setup I configured:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;JSON&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;JSON code&#34;&gt;&lt;code&gt;{&#xA;  &amp;#34;name&amp;#34;: &amp;#34;Python 3&amp;#34;,&#xA;  // Base image provided by Microsoft or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile&#xA;  &amp;#34;image&amp;#34;: &amp;#34;mcr.microsoft.com/devcontainers/python:0-3.9&amp;#34;,&#xA;  // Install pre-configured features into the container - in this case the fish shell&#xA;  &amp;#34;features&amp;#34;: {&#xA;    &amp;#34;ghcr.io/meaningful-ooo/devcontainer-features/fish:1&amp;#34;: {&#xA;      &amp;#34;fisher&amp;#34;: true&#xA;    }&#xA;  },&#xA;  // Specify extensions which should be made available inside of the container&#xA;  &amp;#34;customizations&amp;#34;: {&#xA;    &amp;#34;vscode&amp;#34;: {&#xA;      &amp;#34;extensions&amp;#34;: [&#xA;        &amp;#34;vscodevim.vim&amp;#34;,&#xA;        &amp;#34;ms-python.python&amp;#34;,&#xA;        &amp;#34;ms-python.pylance&amp;#34;,&#xA;        &amp;#34;ms-python.black&amp;#34;,&#xA;        &amp;#34;ms-python.flake8&amp;#34;,&#xA;        &amp;#34;ms-python.isort&amp;#34;&#xA;      ],&#xA;      &amp;#34;settings&amp;#34;: {&#xA;        &amp;#34;python.linting.flake8Enabled&amp;#34;: true,&#xA;        &amp;#34;python.linting.flake8Path&amp;#34;: &amp;#34;/usr/local/bin/flake8&amp;#34;,&#xA;        &amp;#34;python.formatting.blackPath&amp;#34;: &amp;#34;/usr/local/bin/black&amp;#34;&#xA;      }&#xA;    }&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;adding-dotfiles&#34;&gt;Adding Dotfiles&lt;/h2&gt;&#xA;&lt;p&gt;One feature which I thought was really handy is the ability to specify a repository containing &lt;code&gt;dotfiles&lt;/code&gt; (linux personalised configuration settings, such as &lt;a href=&#34;https://github.com/mich-murphy/dwm-dotfiles&#34;&gt;my dwm dotfiles&lt;/a&gt;), which can be &lt;a href=&#34;https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories&#34;&gt;cloned and configured&lt;/a&gt; once the Dev Container is up and running.&lt;/p&gt;&#xA;&lt;p&gt;This means you can create a Dev Contianer and share it across a team, so that everyone has a standardised development environment. Then you can add your own configuration after the container has been started by using the dotfiles functionality - no one is subjected to your strange configuration (e.g. running the vim extension).&lt;/p&gt;&#xA;&lt;h2 id=&#34;use-of-dev-containers-in-neovim&#34;&gt;Use of Dev Containers in Neovim&lt;/h2&gt;&#xA;&lt;p&gt;Naturally the first thing I did after discovering this feature in VSCode was to explore whether it is also available in Neovim (my editor of choice) - sure enough &lt;a href=&#34;https://github.com/esensar/nvim-dev-container&#34;&gt;I found a plugin&lt;/a&gt;. I need to play around with this more to get it configured, but I&amp;rsquo;m glad to know the option is there.&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Useful References for Understanding ZFS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/zfs-useful-references/"/>
<id>https://mich-murphy.com/zfs-useful-references/</id>
<published>2023-03-06T00:00:00+00:00</published>
<updated>2023-03-06T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I&amp;rsquo;ve been looking to upgrade my existing homelab (currently a 2012 Mac Mini which I had lying around). I plan to use ZFS when I do upgrade, as it has a lot of great features for data integrity, redundancy and backups.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve found a number of useful references in the process, which I thought to include here:&lt;/p&gt;&#xA;&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/FreeBSD-Mastery-ZFS-7/dp/1642350001&#34;&gt;FreeBSD Mastery: ZFS&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/FreeBSD-Mastery-Advanced-ZFS/dp/164235001X/ref=d_pd_sbs_sccl_2_1/136-3988813-2250128?pd_rd_w=iJSYf&amp;amp;content-id=amzn1.sym.3676f086-9496-4fd7-8490-77cf7f43f846&amp;amp;pf_rd_p=3676f086-9496-4fd7-8490-77cf7f43f846&amp;amp;pf_rd_r=F6P3A70FCX3JWKZ8FAKN&amp;amp;pd_rd_wg=fJtXN&amp;amp;pd_rd_r=1c62b972-1d2f-4b21-9b74-4149b0a66303&amp;amp;pd_rd_i=164235001X&amp;amp;psc=1&#34;&gt;FreeBSD Mastery: Advanced ZFS&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://openzfs.github.io/openzfs-docs/&#34;&gt;OpenZFS Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;useful-posts&#34;&gt;Useful Posts&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://arstechnica.com/information-technology/2020/05/zfs-101-understanding-zfs-storage-and-performance/&#34;&gt;ZFS 101 - Understanding ZFS Storage &amp;amp; Performance&lt;/a&gt;: great write up explaining what ZFS is and how it works, this article goes into a good level of depth too&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://jrs-s.net/2015/02/06/zfs-you-should-use-mirror-vdevs-not-raidz/&#34;&gt;ZFS: You Should User Mirror VDEVs, Not RAIDZ&lt;/a&gt;: recommendations on how to structure ZFS pools - spoiler alert mirroring is a great option&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://forum.level1techs.com/t/zfs-metadata-special-device-z/159954&#34;&gt;ZFS Metadata Special Device&lt;/a&gt;: explains how to add a metadata device in order to increase random read performance on a ZFS pool&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content>
</entry>
<entry>
<title>Encrypting Secrets in NixOS with Agenix</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/encrypting-secrets-nixos/"/>
<id>https://mich-murphy.com/encrypting-secrets-nixos/</id>
<published>2023-02-22T00:00:00+00:00</published>
<updated>2023-02-23T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;After playing around with NixOS and creating declarative configurations I quickly came across a need to provide encrypted secrets, rather than clear text. This is a requirement to be able to save configurations on GitHub and is recommended anyway, given that clear text secrets are made available on the &lt;a href=&#34;https://github.com/NixOS/nixpkgs/issues/24288&#34;&gt;world readable Nix store&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There are a number of &lt;a href=&#34;https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes&#34;&gt;solutions available&lt;/a&gt; to encrypt secrets in NixOS, I landed on &lt;a href=&#34;https://github.com/ryantm/agenix&#34;&gt;Agenix&lt;/a&gt;, as it&amp;rsquo;s relatively easy to configure and works with my deployment tool of choice - &lt;a href=&#34;https://github.com/serokell/deploy-rs&#34;&gt;Deploy-rs&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-agenix-works&#34;&gt;How Agenix Works&lt;/h2&gt;&#xA;&lt;p&gt;Agenix uses pre-existing SSH keys to encrypt secrets using the &lt;a href=&#34;https://github.com/FiloSottile/age&#34;&gt;Age&lt;/a&gt; encryption tool. These secrets are then decrypted using the &lt;em&gt;private&lt;/em&gt; SSH &lt;em&gt;host&lt;/em&gt; key on the target system.&lt;/p&gt;&#xA;&lt;p&gt;There are a couple of points worth considering when using Agenix:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Agenix doesn&amp;rsquo;t officially have support for Home Manager - though an alternative is &lt;a href=&#34;https://github.com/jordanisaacs/homeage&#34;&gt;available&lt;/a&gt; (I haven&amp;rsquo;t tried this)&lt;/li&gt;&#xA;&lt;li&gt;Agenix provides a secret file, rather than a string. This means that if there is no option in an existing module to pass a filepath for a secret, we need to have the service read the clear text file path at runtime&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;using-agenix&#34;&gt;Using Agenix&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;ll run through a simple example here, should you want more detail I recommend reading through the Agenix &lt;a href=&#34;https://github.com/ryantm/agenix&#34;&gt;readme&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;agenix-configuration&#34;&gt;Agenix Configuration&lt;/h3&gt;&#xA;&lt;p&gt;Create a directory for secrets and secret config - this should be somewhere that makes sense e.g. &lt;code&gt;/etc/nixos/secrets&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Next we need to create a secret configuration file &lt;code&gt;secrets.nix&lt;/code&gt;. This tells Agenix which SSH keys to use when encrypting secrets, which secrets are available, and which systems/users should have access to each secret.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;mkdir -p /etc/nixos/secrets&#xA;vim /etc/nixos/secrets/secrets.nix&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;The contents of the file should look something like this:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/secrets/secrets.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/secrets/secrets.nix&#34;&gt;&lt;code&gt;let&#xA;  system = &amp;#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH&amp;#34;;&#xA;in&#xA;{&#xA;  &amp;#34;secretpassword.age&amp;#34;.publicKeys = [ system ];&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;creating-a-secret&#34;&gt;Creating a Secret&lt;/h3&gt;&#xA;&lt;p&gt;We are now ready to create out first secret &lt;code&gt;secretpassword&lt;/code&gt;. This next part will differ depending on how you have installed Agenix.&lt;/p&gt;&#xA;&lt;p&gt;Given I&amp;rsquo;m using Nix Flakes, I can simply run:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;nix run github:ryantm/agenix -- -e secretpassword.age&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;The above assumes my SSH keys have been added to &lt;code&gt;~/.ssh/&lt;/code&gt;. If this is not the case we can specify which SSH keys to use:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;nix run github:ryantm/agenix -- -e secretpassword.age -i /path/to/ssh/key&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;adding-secret-to-nixos&#34;&gt;Adding Secret to NixOS&lt;/h3&gt;&#xA;&lt;p&gt;Once the above has been completed you can now add secrets to your NixOS configurations like so:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  users.users.captainsecure = {&#xA;    isNormalUser = true;&#xA;    home = &amp;#34;/home/captainsecure&amp;#34;;&#xA;    passwordFile = config.age.secrets.secretpassword.path;&#xA;  };&#xA;&#xA;  age.secrets.secretpassword.file = ../../secrets/secretpassword.age;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;secrets-folder-structure&#34;&gt;Secrets Folder Structure&lt;/h3&gt;&#xA;&lt;p&gt;I discovered an interesting technique for organising Agenix secrets into directories, thanks to Charlotte Van Petegem&amp;rsquo;s &lt;a href=&#34;https://git.chvp.be/chvp/nixos-config/src/branch/main/secrets&#34;&gt;dotfiles&lt;/a&gt;. I didn&amp;rsquo;t see this explained in the Agenix manual, but it looks like we can nest age secrets within the secrets directory e.g.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Text · /etc/nixos/secrets&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Text code, /etc/nixos/secrets&#34;&gt;&lt;code&gt;.&#xA;├── secrets.nix&#xA;├── passwords&#xA;│   └── secretpassword.age&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;When we need to refer to these nested secrets in our Nix configuration, we specify the path to each age file e.g.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  users.users.captainsecure = {&#xA;    isNormalUser = true;&#xA;    home = &amp;#34;/home/captainsecure&amp;#34;;&#xA;    passwordFile = config.age.secrets.&amp;#34;passwords/secretpassword&amp;#34;.path;&#xA;  };&#xA;&#xA;  age.secrets.&amp;#34;passwords/secretpassword&amp;#34;.file = ../../secrets/passwords/secretpassword.age;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;</content>
</entry>
<entry>
<title>How to Configure Nextcloud on NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/configure-nextcloud-nixos/"/>
<id>https://mich-murphy.com/configure-nextcloud-nixos/</id>
<published>2023-02-21T00:00:00+00:00</published>
<updated>2023-02-24T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;&lt;a href=&#34;https://nextcloud.com/&#34;&gt;Nextcloud&lt;/a&gt; provides a very polished experience when looking for a solution to self host your personal files, and replace tooling such as Google Drive or Dropbox. In this post I run through how I configured Nextcloud for use on NixOS.&lt;/p&gt;&#xA;&lt;p&gt;Jacob Neplokh wrote an incredibly helpful &lt;a href=&#34;https://web.archive.org/web/20221206033424/https://jacobneplokh.com/how-to-setup-nextcloud-on-nixos/&#34;&gt;blog post&lt;/a&gt; which forms the basis of this guide, I recommend having a read through for a more in depth explanation of everything. The &lt;a href=&#34;https://wiki.nixos.org/wiki/Nextcloud&#34;&gt;NixOS Wiki&lt;/a&gt; also had some very helpful information.&lt;/p&gt;&#xA;&lt;p&gt;There are several services that need to be configured to setup Nextcloud:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Nextcloud&lt;/li&gt;&#xA;&lt;li&gt;Postgresql (optional but recommended)&lt;/li&gt;&#xA;&lt;li&gt;Redis (optional)&lt;/li&gt;&#xA;&lt;li&gt;Nginx&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;nextcloud-setup&#34;&gt;Nextcloud Setup&lt;/h2&gt;&#xA;&lt;p&gt;Nextcloud itself is relatively straightforward to setup, I found knowing all the additional services to be the tricky part. The module has a multitude of options to configure, for more information about each one take a look at &lt;a href=&#34;https://search.nixos.org/options?from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=services.nextcloud&#34;&gt;the module&amp;rsquo;s options&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Here is a look at my config - you&amp;rsquo;ll note that secrets are managed with Agenix, which I&amp;rsquo;ve &lt;a href=&#34;https://mich-murphy.com/encrypting-secrets-nixos/&#34;&gt;written about before&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  services.nextcloud = {&#xA;    enable = true;&#xA;    # specify either a domain you own or localhost&#xA;    hostName = &amp;#34;nextcloud.yourdomain.com&amp;#34;;&#xA;    autoUpdateApps.enable = true;&#xA;    https = true;&#xA;    # only specify caching if using redis or alternative service&#xA;    caching.redis = true;&#xA;    config = {&#xA;      # only specify dbtype if using postgresql db&#xA;      dbtype = &amp;#34;pgsql&amp;#34;;&#xA;      dbname = &amp;#34;nextcloud&amp;#34;;&#xA;      dbuser = &amp;#34;nextcloud&amp;#34;;&#xA;      # default directory for postgresql, ensures automatic setup of db&#xA;      dbhost = &amp;#34;/run/postgresql&amp;#34;;&#xA;      adminuser = &amp;#34;admin&amp;#34;;&#xA;      # specified using agenix, provide path to file as alternative&#xA;      adminpassFile = config.age.secrets.nextcloudPass.path;&#xA;      # error thrown unless specified&#xA;      defaultPhoneRegion = &amp;#34;AU&amp;#34;;&#xA;    };&#xA;    # specify only if you want redis caching&#xA;    extraOptions = {&#xA;      redis = {&#xA;        host = &amp;#34;127.0.0.1&amp;#34;;&#xA;        port = 31638;&#xA;        dbindex = 0;&#xA;        timeout = 1.5;&#xA;      };&#xA;    };&#xA;  };&#xA;&#xA;  # only needed if using agenix for secret encryption&#xA;  age.secrets = {&#xA;    nextcloudPass = {&#xA;      file = ../../secrets/nextcloudPass.age;&#xA;      # allow default nextcloud user access to secret&#xA;      owner = &amp;#34;nextcloud&amp;#34;;&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;postgresql-setup&#34;&gt;Postgresql Setup&lt;/h2&gt;&#xA;&lt;p&gt;It&amp;rsquo;s highly recommended that you replace the default Sqlite database with Postgresql, here are the steps involved in making the change:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  services = {&#xA;    postgresql = {&#xA;      enable = true;&#xA;      ensureDatabases = [ &amp;#34;nextcloud&amp;#34; ];&#xA;      ensureUsers = [{&#xA;        name = &amp;#34;nextcloud&amp;#34;;&#xA;        ensurePermissions.&amp;#34;DATABASE nextcloud&amp;#34; = &amp;#34;ALL PRIVILEGES&amp;#34;;&#xA;      }];&#xA;    };&#xA;    # optional backup for postgresql db&#xA;    postgresqlBackup = {&#xA;      enable = true;&#xA;      location = &amp;#34;/data/backup/nextclouddb&amp;#34;;&#xA;      databases = [ &amp;#34;nextcloud&amp;#34; ];&#xA;      # time to start backup in systemd.time format&#xA;      startAt = &amp;#34;*-*-* 23:15:00&amp;#34;;&#xA;    };&#xA;  };&#xA;&#xA;  # ensure postgresql db is started with nextcloud&#xA;  systemd = {&#xA;    services.&amp;#34;nextcloud-setup&amp;#34; = {&#xA;      requires = [ &amp;#34;postgresql.service&amp;#34; ];&#xA;      after = [ &amp;#34;postgresql.service&amp;#34; ];&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;As mentioned in the comments above, I&amp;rsquo;ve included an optional backup service, which will export the database to the specified location. Following this I backup the specified folder to using &lt;a href=&#34;https://mich-murphy.com/backup-solutions-nixos/&#34;&gt;BorgBackup&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;redis-caching&#34;&gt;Redis Caching&lt;/h2&gt;&#xA;&lt;p&gt;This allows for caching of frequently used files in Nextcloud, which likely provides a more snappy user experience. Here&amp;rsquo;s my configuration:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  services = {&#xA;    redis.servers.nextcloud = {&#xA;      enable = true;&#xA;      port = 31638;&#xA;      bind = &amp;#34;127.0.0.1&amp;#34;;&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;nginx-configuration&#34;&gt;Nginx Configuration&lt;/h2&gt;&#xA;&lt;p&gt;In order to login once Nextcloud is configured, HTTPS must be setup. My Nextcloud service is run behind a &lt;a href=&#34;https://tailscale.com/&#34;&gt;Tailscale&lt;/a&gt; VPN, so I had a couple of different options for &lt;a href=&#34;https://tailscale.com/kb/1153/enabling-https/&#34;&gt;configuring HTTPS&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Having Tailscale do it with &lt;code&gt;tailscale cert&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Creating an A record to point an existing domain to my Tailscale IP, using my DNS provider, and configuring HTTPS via ACME&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;I opted for the second option, as such I also had to configure ACME to automatically generate my SSL certificate. NixOS has builtin tooling using a service called &lt;a href=&#34;https://github.com/go-acme/lego&#34;&gt;lego&lt;/a&gt; behind the scenes to create certificates. Here&amp;rsquo;s my config:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  services.nginx = {&#xA;    enable = true;&#xA;    recommendedGzipSettings = true;&#xA;    recommendedOptimisation = true;&#xA;    recommendedProxySettings = true;&#xA;    recommendedTlsSettings = true;&#xA;    # pulls in the hostname we set for nextcloud above (nextcloud.yourdomain.com)&#xA;    virtualHosts.${config.services.nextcloud.hostName} = {&#xA;      enableACME = true;&#xA;      acmeRoot = null;&#xA;      addSSL = true;&#xA;      # directs traffic to the appropriate port for nextcloud&#xA;      locations.&amp;#34;/&amp;#34; = {&#xA;        proxyPass = &amp;#34;http://localhost:8080&amp;#34;;&#xA;        proxyWebsockets = true;&#xA;      };&#xA;    };&#xA;  };&#xA;&#xA;  security.acme = {&#xA;    acceptTerms = true;&#xA;    preliminarySelfsigned = false;&#xA;    defaults = {&#xA;      email = &amp;#34;acme@yourdomain.com&amp;#34;;&#xA;      dnsProvider = &amp;#34;cloudflare&amp;#34;;&#xA;      # API for authentication to DNA provider e.g.&#xA;      # CF_API_KEY=&amp;lt;insert-global-key&amp;gt;&#xA;      # CF_API_EMAIL=&amp;lt;insert-dns-account-email&amp;gt;&#xA;      # specify file if not using agenix&#xA;      credentialsFile = config.age.secrets.acmeCredentials.path;&#xA;    };&#xA;  };&#xA;&#xA;  # allow nginx to configure acme&#xA;  users.users.nginx.extraGroups = [ &amp;#34;acme&amp;#34; ];&#xA;&#xA;  # if providing credentialsFile via agenix&#xA;  age.secrets.acmeCredentials.file = ../../secrets/acmeCredentials.age;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;I pieced together this setup thanks to the &lt;a href=&#34;https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns-with-vhosts&#34;&gt;documentation in the NixOS Manual&lt;/a&gt;, I recommend giving it a read to understand what&amp;rsquo;s going on here.&lt;/p&gt;&#xA;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;As you can see above, there are a few steps involved in properly configuring Nextcloud. Regarding Nextcloud apps, I found it easier to be able to install and manage these via Nextcloud itself.&lt;/p&gt;&#xA;&lt;p&gt;I ended up putting all of the above config into its own module, for reference you can &lt;a href=&#34;https://github.com/mich-murphy/nix-config/blob/82e8fd5f96a6960dfc9e29ef83b1c764ccc86bdd/common/nixos/nextcloud.nix&#34;&gt;view the module on GitHub&lt;/a&gt;.&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Configuring Backup Solutions in NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/backup-solutions-nixos/"/>
<id>https://mich-murphy.com/backup-solutions-nixos/</id>
<published>2023-02-13T00:00:00+00:00</published>
<updated>2023-04-22T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;One of my first consideration in setting up a homelab has been about a backup strategy - it&amp;rsquo;s difficult to have trust in a self-hosted instance of &lt;a href=&#34;https://nextcloud.com/&#34;&gt;Nextcloud&lt;/a&gt; when I don&amp;rsquo;t know whether data will exist after a hardware fault. One common backup strategy is the &lt;a href=&#34;https://web.archive.org/web/20251230003356/https://www.seagate.com/au/en/blog/what-is-a-3-2-1-backup-strategy/&#34;&gt;3-2-1 approach&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;3 copies of the data being backup up&lt;/li&gt;&#xA;&lt;li&gt;2 different media types for storage&lt;/li&gt;&#xA;&lt;li&gt;1 off-site backup&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This post details 2 methods of creating encrypted off-site backups in NixOS using &lt;a href=&#34;https://www.duplicati.com/&#34;&gt;Duplicati&lt;/a&gt; or &lt;a href=&#34;https://www.borgbackup.org/&#34;&gt;BorgBackup&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;duplicati&#34;&gt;Duplicati&lt;/h2&gt;&#xA;&lt;p&gt;NixOS provides a fairly bare bones &lt;a href=&#34;https://search.nixos.org/options?from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=services.duplicati&#34;&gt;module&lt;/a&gt; for setting up Duplicati. Here&amp;rsquo;s how I configured it:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  services = {&#xA;    duplicati = {&#xA;      enable = true;&#xA;      # Allow web access for machines other than localhost&#xA;      interface = &amp;#34;0.0.0.0&amp;#34;;&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;You can optionally specify a different user, group, port and data directory should you wish.&lt;/p&gt;&#xA;&lt;p&gt;After running &lt;code&gt;nixos-rebuild switch&lt;/code&gt; you can manually configure backups via the web interface at &lt;code&gt;&amp;lt;serverip&amp;gt;:8200&lt;/code&gt;. Duplicati has many options for configuring backups, I decided to backup to an S3 object store. Backups can be scheduled, and rules can be set to delete old backups at the target location. Duplicati will also capture logs and display warnings if it encounters any problems during the backup. The only issue I encountered was initially with access rights to the directories I wanted to backup. You can also set encryption keys for backups, with options for different encryption methods.&lt;/p&gt;&#xA;&lt;p&gt;I found Duplicati worked quite well, and I did have to test a restore in the brief time I used it. There was one major factor that made me continue looking for a different solution: I wanted to avoid the need for manual configuration, rather having Nix manage as much as possible for me declaratively.&lt;/p&gt;&#xA;&lt;h2 id=&#34;borgbackup&#34;&gt;BorgBackup&lt;/h2&gt;&#xA;&lt;p&gt;Thanks to an episode of the &lt;a href=&#34;https://www.jupiterbroadcasting.com/show/linux-unplugged/494/&#34;&gt;Linux Unplugged Podcast&lt;/a&gt; I came across BorgBackup. I found that the NixOS &lt;a href=&#34;https://search.nixos.org/options?from=0&amp;amp;size=50&amp;amp;sort=relevance&amp;amp;type=packages&amp;amp;query=services.borgbackup.jobs&#34;&gt;module&lt;/a&gt; was pretty extensive, and I learnt that a number of cloud hosting options existed for storing these backups, and they seemed to be pretty competitively priced (&lt;a href=&#34;https://www.borgbase.com/&#34;&gt;BorgBase&lt;/a&gt; and &lt;a href=&#34;https://rsync.net/&#34;&gt;Rsync.net&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;After doing some further digging I came across a very helpful &lt;a href=&#34;https://xeiaso.net/blog/borg-backup-2021-01-09&#34;&gt;blog post&lt;/a&gt; which detailed all the steps in configuring BorgBackup on NixOS. After looking through the modules and reading the blog post I ended up with the following configuration:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  services.borgbackup.jobs = {&#xA;    &amp;#34;media&amp;#34; = {&#xA;      paths = [&#xA;        &amp;#34;/data/media&amp;#34;&#xA;        &amp;#34;/data/backup&amp;#34;&#xA;        &amp;#34;/var/lib/nextcloud&amp;#34;&#xA;      ];&#xA;      # Note: you will need to edit the SSH url provided by BorgBase to match the below format&#xA;      repo = &amp;#34;o6h6zl22@o6h6zl22.repo.borgbase.com:repo&amp;#34;;&#xA;      encryption = {&#xA;        mode = &amp;#34;repokey-blake2&amp;#34;;&#xA;        passCommand = &amp;#34;cat ${config.age.secrets.mediaBorgPass.path}&amp;#34;;&#xA;      };&#xA;      environment.BORG_RSH = &amp;#34;ssh -i ${config.age.secrets.borgSSHKey.path}&amp;#34;;&#xA;      compression = &amp;#34;auto,lzma&amp;#34;;&#xA;      startAt = &amp;#34;daily&amp;#34;;&#xA;    };&#xA;  };&#xA;   &#xA;  age.secrets.mediaBorgPass.file = ../../secrets/mediaBorgPass.age;&#xA;  age.secrets.borgSSHKey.file = ../../secrets/borgSSHKey.age;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;The above configures a backup to my BorgBase repo which runs daily at midnight, backing up the specified directories. The backup is encrypted and compressed, and the repo requires an authorized SSH key for access. This continually monitors and updates a single backup, however you can optionally configure BorgBackup for &lt;a href=&#34;https://borgbackup.readthedocs.io/en/stable/usage/notes.html#append-only-mode-forbid-compaction&#34;&gt;append-only mode&lt;/a&gt; which allows for some delay in deletion of older files.&lt;/p&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;In the above I&amp;rsquo;ve used &lt;a href=&#34;https://github.com/ryantm/agenix&#34;&gt;Agenix&lt;/a&gt; for managing my secrets (SSH key and encryption key). I&amp;rsquo;ve written &lt;a href=&#34;https://mich-murphy.com/encrypting-secrets-nixos/&#34;&gt;a post on configuring it&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;You&amp;rsquo;ll likely have to run an initial SSH connection to the Borg repo to add its keys to &lt;code&gt;known_hosts&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;ssh -i /var/run/agenix/borgSSHKey o6h6zl22@o6h6zl22.repo.borgbase.com&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h3 id=&#34;monitoring-initial-borgbackup&#34;&gt;Monitoring Initial BorgBackup&lt;/h3&gt;&#xA;&lt;p&gt;I thought to add some additional notes on how to monitor the initial backup, as this process is not as straight forward as when using a tool such as Duplicati. Credit for the commands in this and the next section go to the blog post listed earlier.&lt;/p&gt;&#xA;&lt;p&gt;After adding the above configuration and running &lt;code&gt;nixos-rebuild switch&lt;/code&gt; a Systemd service is configured to connect to the BorgBase repo and begin a backup. You can manually start the backup by running:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;systemctl start borgbackup-job-&amp;lt;name-of-job&amp;gt;.service&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;You can then keep an eye on the backup by monitoring it via &lt;code&gt;journalctl&lt;/code&gt; (keep in mind the backup will take a long time initially, subsequent backups are incremental and much faster):&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;journalctl -fu borgbackup-job-&amp;lt;name-of-job&amp;gt;.service&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;All being well you should see a successful connection being established and a successful disconnection once the backup has completed.&lt;/p&gt;&#xA;&lt;h3 id=&#34;restoring-from-borgbackup&#34;&gt;Restoring From BorgBackup&lt;/h3&gt;&#xA;&lt;p&gt;We first need to create a directory and mount the Borg repo inside:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;mkdir mount&#xA;borg-job-&amp;lt;name-of-job&amp;gt; mount o6h6zl22@o6h6zl22.repo.borgbase.com:repo ./mount&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;By default borg looks for any ssh keys in the location and format of &lt;code&gt;~/.ssh/id_*&lt;/code&gt;, if you need to pass in an alternate ssh key you can use the following format:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;borg-job-&amp;lt;name-of-job&amp;gt; mount --rsh &amp;#39;ssh -i /var/run/agenix/borgSSHKey&amp;#39; o6h6zl22@o6h6zl22.repo.borgbase.com:repo ./mount&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;We are then presented with a list of snapshot dates to choose from, and can select the time period at which we wish to restore. Copy across files as normal e.g. using the &lt;code&gt;cp&lt;/code&gt; command&lt;/p&gt;&#xA;&lt;p&gt;When everything has been restored we can unmount with the below command:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;borg-job-&amp;lt;name-of-job&amp;gt; umount /mount&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;I ended up using BorgBackup as it meant I could configure almost everything once in my NixOS configuration and not have to think about it again. I setup email alerts in BorgBase to notify me if no backups are completed after a certain period of time, so that I don&amp;rsquo;t have to constantly monitor them. Another benefit of BorgBackup was that with my configuration the backups are performed as root, so I didn&amp;rsquo;t have to worry about file permissions when backing up e.g. &lt;code&gt;/var/lib/nextcloud&lt;/code&gt; (obviously you need to decide whether this is the appropriate approach for you).&lt;/p&gt;&#xA;&lt;p&gt;If you want an intuitive UI, without the fuss of the initial configuration then Duplicati is also a great solution. It also has the advantage of providing a much larger choice for the backup targets, with BorgBackup you need to store everything in a Borg repo (which you have the option of self hosting).&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>Installing NixOS on a Temporary File System</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/nixos-tmpfs-installation/"/>
<id>https://mich-murphy.com/nixos-tmpfs-installation/</id>
<published>2023-02-06T00:00:00+00:00</published>
<updated>2024-04-02T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;In this post I explain how to install NixOS using a temporary file system (tmpfs). This is a precursor to the post I wrote about &lt;a href=&#34;https://mich-murphy.com/nixos-impermanence/&#34;&gt;configuring NixOS for impermanence&lt;/a&gt;, and walks through the installation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;useful-references--notes&#34;&gt;Useful References &amp;amp; Notes&lt;/h2&gt;&#xA;&lt;p&gt;The following is summarised from a combination of 2 different blog posts: one detailing how to &lt;a href=&#34;https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/&#34;&gt;configure a tmpfs on NixOS&lt;/a&gt; and another showing how to &lt;a href=&#34;https://xeiaso.net/blog/paranoid-nixos-2021-07-18&#34;&gt;harden the NixOS install&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The instructions setup a very simple file system, without a swap partition, just a 512MB boot partition and a root partition on the remaining space.&lt;/p&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;This method is used to create a legacy boot partition, as I use it to create a virtual machine inside of Proxmox. Refer to the linked post above on configuring a tmpfs for UEFI instructions.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Before proceeding you will need a copy of the &lt;a href=&#34;https://nixos.org/download.html#nix-more&#34;&gt;NixOS Minimal ISO&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;partitioning--labelling-drives&#34;&gt;Partitioning &amp;amp; Labelling Drives&lt;/h2&gt;&#xA;&lt;p&gt;If unsure first check which drives are available, and confirm which to install on with &lt;code&gt;lsblk&lt;/code&gt;. I will be installing on &lt;code&gt;/dev/sda&lt;/code&gt;, replace this with the relevant drive if it&amp;rsquo;s different.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# Create legacy boot partition of 512MB&#xA;parted /dev/sda -- mklabel msdos&#xA;parted /dev/sda -- mkpart primary ext4 1M 512M&#xA;parted /dev/sda -- set 1 boot on&#xA;&#xA;# Create root partition on remaining storage&#xA;parted /dev/sda -- mkpart primary ext4 512MiB 100%&#xA;&#xA;# Label both partitions&#xA;mkfs.ext4 -L boot /dev/sda1&#xA;mkfs.ext4 -L nix /dev/sda2&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;mount-drive--create-folders&#34;&gt;Mount Drive &amp;amp; Create Folders&lt;/h2&gt;&#xA;&lt;p&gt;We need to setup the following folder structure on the drive we prepared before starting a NixOS install.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# Create root mount with tmpfs&#xA;mount -t tmpfs none /mnt&#xA;&#xA;# Create folder structure to persist in /nix/persist - srv is optional, used as home directory for services&#xA;mkdir -p /mnt/{boot,nix,etc/{nixos,ssh},var/{lib,log},srv}&#xA;&#xA;# Mount relevant partitions to each folder&#xA;mount /dev/sda1 /mnt/boot&#xA;mount /dev/sda2 /mnt/nix&#xA;&#xA;# Create matching folders in /mnt/nix/persist&#xA;mkdir -p /mnt/nix/persist/{etc/{nixos,ssh},var/{lib,log},srv}&#xA;&#xA;# Create temporary bind mounts (later replaced with impermanence - refer to post linked above)&#xA;mount -o bind /mnt/nix/persist/etc/nixos /mnt/etc/nixos&#xA;mount -o bind /mnt/nix/persist/var/log /mnt/var/log&#xA;&#xA;# Generate a base config&#xA;nixos-generate-config --root /mnt&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;editing-configuration-files&#34;&gt;Editing Configuration Files&lt;/h2&gt;&#xA;&lt;p&gt;Before completing the final install we need to make some changes to the generated configuration:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /mnt/etc/nixos/hardware-configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /mnt/etc/nixos/hardware-configuration.nix&#34;&gt;&lt;code&gt;{&#xA;  fileSystems.&amp;#34;/&amp;#34; = {&#xA;    device = &amp;#34;none&amp;#34;;&#xA;    fsType = &amp;#34;tmpfs&amp;#34;;&#xA;    # add the line below - limits root storage to 2G maximum&#xA;    options = [ &amp;#34;defaults&amp;#34; &amp;#34;size=2G&amp;#34; &amp;#34;mode=755&amp;#34; ];&#xA;  };&#xA;&#xA;  # Update both /boot and /nix to use labels rather than UUID&#xA;  fileSystems.&amp;#34;/boot&amp;#34; = {&#xA;    device = &amp;#34;/dev/disk/by-label/boot&amp;#34;;&#xA;    fsType = &amp;#34;ext4&amp;#34;;&#xA;  };&#xA;&#xA;  fileSystems.&amp;#34;/nix&amp;#34; = {&#xA;    device = &amp;#34;/dev/disk/by-label/nix&amp;#34;;&#xA;    fsType = &amp;#34;ext4&amp;#34;;&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;You will also need to edit &lt;code&gt;/mnt/etc/nixos/configuration.nix&lt;/code&gt;, how you go about this depends on your needs. Personally I only need to prepare the configuration for deployment via &lt;a href=&#34;https://github.com/serokell/deploy-rs&#34;&gt;deploy-rs&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · /mnt/etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /mnt/etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{ config, pkgs, ... }:&#xA;&#xA;{&#xA;  imports = [ ./hardware-configuration.nix ];&#xA;&#xA;  boot.loader.grub.enable = true;&#xA;  boot.loader.grub.version = 2;&#xA;  boot.loader.grub.device = &amp;#34;/dev/sda&amp;#34;;&#xA;&#xA;  # Enable SSH&#xA;  services.openssh.enable = true;&#xA;&#xA;  # Configure user with sudo access and SSH key authentication&#xA;  users.mutableUsers = false; # prevents any changes to users outside of config file&#xA;  users.users.mm = {&#xA;    isNormalUser = true;&#xA;    extraGroups = [ &amp;#34;wheel&amp;#34; ];&#xA;    openssh.authorizedKeys.keys = [&#xA;      &amp;#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMne13aa88i97xAUqU33dk2FNz+w8OIMGi8LH4BCRFaN&amp;#34;&#xA;    ];&#xA;  };&#xA;&#xA;  # Persist host ssh keys to enable decrycption of agenix secrets (see below)&#xA;  environment.etc.&amp;#34;ssh/ssh_host_rsa_key&amp;#34;.source&#xA;    = &amp;#34;/nix/persist/etc/ssh/ssh_host_rsa_key&amp;#34;;&#xA;  environment.etc.&amp;#34;ssh/ssh_host_rsa_key.pub&amp;#34;.source&#xA;    = &amp;#34;/nix/persist/etc/ssh/ssh_host_rsa_key.pub&amp;#34;;&#xA;  environment.etc.&amp;#34;ssh/ssh_host_ed25519_key&amp;#34;.source&#xA;    = &amp;#34;/nix/persist/etc/ssh/ssh_host_ed25519_key&amp;#34;;&#xA;  environment.etc.&amp;#34;ssh/ssh_host_ed25519_key.pub&amp;#34;.source&#xA;    = &amp;#34;/nix/persist/etc/ssh/ssh_host_ed25519_key.pub&amp;#34;;&#xA;&#xA;  # enable nix flakes&#xA;  nix.extraOptions = &amp;#39;&amp;#39;&#xA;    experimental-features = nix-command flakes&#xA;  &amp;#39;&amp;#39;;&#xA;&#xA;  # allow for deployment without entering password&#xA;  security.sudo.wheelNeedsPassword = false;&#xA;&#xA;  system.stateVersion = &amp;#34;22.11&amp;#34;;&#xA;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;If you plan on using &lt;code&gt;/etc/nixos/ocnfiguration.nix&lt;/code&gt; for configuration, then you will want to ensure your created user also has a password. You may also want to edit the timezone, network settings, hostname and bootloader.&lt;/p&gt;&#xA;&lt;h2 id=&#34;complete-nixos-install&#34;&gt;Complete NixOS Install&lt;/h2&gt;&#xA;&lt;p&gt;Finally we run the following to install NixOS:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;nixos-install --no-root-passwd&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;deploy-rs--agenix-preparation&#34;&gt;Deploy-rs &amp;amp; Agenix Preparation&lt;/h3&gt;&#xA;&lt;p&gt;This final section is likely irrelevant to most users. I plan on writing about these tools in a future post. Deploy-rs (linked above) allows for deployment of nix flakes to remote machines, and &lt;a href=&#34;https://github.com/ryantm/agenix&#34;&gt;Agenix&lt;/a&gt; encrypts any secrets used in the flakes using SSH keys (I&amp;rsquo;ve &lt;a href=&#34;https://mich-murphy.com/encrypting-secrets-nixos/&#34;&gt;written about it&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;To ensure the host has the correct SSH keys to allow for decryption of secrets be sure to update &lt;code&gt;secrets.nix&lt;/code&gt; with the new host key &lt;code&gt;/etc/ssh/ssh_host_ed25516_key.pub&lt;/code&gt;. Agenix will then need to be rekeyed to allow any new SSH keys to decrypt secrets:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;To find out the new host key&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;ssh-keyscan -t ssh-ed25519 hostname&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;To rekey Agenix secrets&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;nix run github:ryantm/agenix -- --rekey -i /path/to/original/ssh/key&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;</content>
</entry>
<entry>
<title>Configuring NixOS for Impermanence</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/nixos-impermanence/"/>
<id>https://mich-murphy.com/nixos-impermanence/</id>
<published>2023-01-18T00:00:00+00:00</published>
<updated>2023-02-24T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;After reading posts from both &lt;a href=&#34;https://grahamc.com/blog/erase-your-darlings&#34;&gt;Graham Christensen&lt;/a&gt; and &lt;a href=&#34;https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/&#34;&gt;Elis Hirwing&lt;/a&gt; about the idea of impermanence in NixOS I decided to try setting it up on my homelab. The basic idea is that we configure the system to use a temporary file system, where everything is wiped after a reboot, unless specified otherwise.&lt;/p&gt;&#xA;&lt;p&gt;One of the major benefits of this kind of setup is that it forces you to properly declare the configuration, as everything will break after a reboot otherwise.&lt;/p&gt;&#xA;&lt;h2 id=&#34;initial-setup&#34;&gt;Initial Setup&lt;/h2&gt;&#xA;&lt;p&gt;For the initial installation of NixOS I followed Elis&amp;rsquo; walk through (linked above) and configured root using a temporary file system. Once this was complete I added the nix community tool &lt;a href=&#34;https://github.com/nix-community/impermanence&#34;&gt;impermanence&lt;/a&gt; to &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt; to allow for a declarative configuration going forward.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{ ... }:&#xA;&#xA;let&#xA;  impermanence = builtins.fetchTarball {&#xA;    url = &amp;#34;https://github.com/nix-community/impermanence/archive/master.tar.gz&amp;#34;;&#xA;  };&#xA;in&#xA;{&#xA;  imports = [&#xA;    ./hardware-configuration.nix # should be present by default&#xA;    &amp;#34;${impermanence}/nixos.nix&amp;#34;&#xA;  ];&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;configuring-persistence&#34;&gt;Configuring Persistence&lt;/h2&gt;&#xA;&lt;p&gt;With the above complete I was able to configure directories I needed to persist from within &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{ ... }:&#xA;&#xA;{&#xA;  environment = {&#xA;    persistence.&amp;#34;/nix/persist&amp;#34; = {&#xA;      directories = [&#xA;        &amp;#34;/etc/nixos&amp;#34; # configuration files&#xA;        &amp;#34;/srv&amp;#34; # service data&#xA;        &amp;#34;/var/log&amp;#34; # where journald dumps logs&#xA;        &amp;#34;/var/lib&amp;#34; # system service persistant data&#xA;        &amp;#34;/data&amp;#34; # where I store local media&#xA;      ];&#xA;      files = [&#xA;        &amp;#34;/etc/machine-id&amp;#34; # ensures logs are retained after reboot&#xA;        &amp;#34;/users/admin&amp;#34; # directory where I store passwordFile for user&#xA;      ];&#xA;    };&#xA;    etc = {&#xA;      # ensures SSH keys can be correctly generated&#xA;      &amp;#34;ssh/ssh_host_rsa_key&amp;#34;.source = &amp;#34;/nix/persist/etc/ssh/ssh_host_rsa_key&amp;#34;;&#xA;      &amp;#34;ssh/ssh_host_rsa_key.pub&amp;#34;.source = &amp;#34;/nix/persist/etc/ssh/ssh_host_rsa_key.pub&amp;#34;;&#xA;      &amp;#34;ssh/ssh_host_ed25519_key&amp;#34;.source = &amp;#34;/nix/persist/etc/ssh/ssh_host_ed25519_key&amp;#34;;&#xA;      &amp;#34;ssh/ssh_host_ed25519_key.pub&amp;#34;.source = &amp;#34;/nix/persist/etc/ssh/ssh_host_ed25519_key.pub&amp;#34;;&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Initially I ran into issues configuring &lt;a href=&#34;https://tailscale.com/&#34;&gt;Tailscale&lt;/a&gt; as I would be logged out after each reboot, I found persisting &lt;code&gt;/var/lib&lt;/code&gt; fixed the issue as it ensures the Tailscale state, located at &lt;code&gt;/var/lib/tailscale/tailscale.state&lt;/code&gt;, is always available.&lt;/p&gt;&#xA;&lt;p&gt;I setup &lt;code&gt;/srv&lt;/code&gt; as the directory I keep data relating to other services I run on the machine, for services which allow me to specify it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;managing-users&#34;&gt;Managing Users&lt;/h2&gt;&#xA;&lt;p&gt;You may have noticed in the configuration above I also persist &lt;code&gt;/users/admin&lt;/code&gt;, this allows me to store a hashed password for the user inside of the directory, which is read from within &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;. The alternative is to either include a password directly, which would then be available for everyone to see in the nix store, or to add a temporary password and change it on each reboot:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{ ... }:&#xA;&#xA;{&#xA;  users = {&#xA;    mutableUsers = false;&#xA;    users = {&#xA;      admin = {&#xA;        isNormalUser = true;&#xA;        home = &amp;#34;/home/admin&amp;#34;;&#xA;        # generate with: nix-shell --run &amp;#39;mkpasswd -m SHA-512 -s&amp;#39; -p mkpasswd&#xA;        passwordFile = &amp;#34;/nix/persist/users/admin&amp;#34;;&#xA;        extraGroups = [ &amp;#34;wheel&amp;#34; ];&#xA;      };&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;</content>
</entry>
<entry>
<title>How to Configure systemd Services and Timers in NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/systemd-services-and-timers-nixos/"/>
<id>https://mich-murphy.com/systemd-services-and-timers-nixos/</id>
<published>2023-01-12T00:00:00+00:00</published>
<updated>2023-02-13T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;Whilst migrating my &lt;a href=&#34;https://github.com/mich-murphy/nix-config/blob/939f3be13ded2a770ab542987a0a106e276f52b5/hosts/homelab/configuration.nix&#34;&gt;homelab&lt;/a&gt; from Ubuntu to Nixos I came across the need to schedule a cron job, which I had setup to sync my media collection from a remote server. After doing a bit of research I discovered that rather than using a cron job, the preferred method is to &lt;a href=&#34;https://paperless.blog/systemd-services-and-timers-in-nixos&#34;&gt;create a systemd service&lt;/a&gt; and schedule it using a systemd timer. The major benefits of doing so are:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Jobs can be isolated to specific users&lt;/li&gt;&#xA;&lt;li&gt;Environment variables and paths can be explicitly declared&lt;/li&gt;&#xA;&lt;li&gt;Logging is easily viewed by &lt;code&gt;systemctl start example&lt;/code&gt; followed by &lt;code&gt;journalctl --unit=example&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Timers let you know when the service will next run &lt;code&gt;systemctl status example.timer&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;initial-setup&#34;&gt;Initial Setup&lt;/h2&gt;&#xA;&lt;p&gt;The first step is to create a dedicated user and group which we can use to isolate the service to, I edited my &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt; to look like this:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;&#xA;  users = {&#xA;    groups.server-sync = {};&#xA;    users = {&#xA;      server-sync = {&#xA;        group = &amp;#34;server-sync&amp;#34;;&#xA;        isSystemUser = true;&#xA;        createHome = true;&#xA;        home = &amp;#34;/srv/server-sync&amp;#34;;&#xA;      };&#xA;    };&#xA;  };&#xA;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;I store all relevant files for my services in &lt;code&gt;/srv&lt;/code&gt;, this is how I like to organise my system.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;I also created a directory to store all of my media &lt;code&gt;mkdir -p /data/media/music&lt;/code&gt;, and one to store my backups (for roon-server at this point) &lt;code&gt;mkdir -p /data/backups/roon-server&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;Finally correct ownership of the new directories to be as follows:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;chown root:root /data&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;chown -R server-sync:server-sync /data/media&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;chown -R roon-server:roon-server /data/backups/roon-server&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;creating-service--timer&#34;&gt;Creating Service &amp;amp; Timer&lt;/h2&gt;&#xA;&lt;p&gt;I then add the following to &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;, which defines the service and the timer:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{&#xA;&#xA;  systemd = {&#xA;    services.server-sync = {&#xA;      # specify all packages required by the script being scheduled&#xA;      path = [&#xA;        pkgs.rsync&#xA;        pkgs.openssh&#xA;      ];&#xA;      serviceConfig = {&#xA;        Type = &amp;#34;oneshot&amp;#34;;&#xA;        # specify the user and group we setup earlier&#xA;        User = &amp;#34;server-sync&amp;#34;;&#xA;        Group = &amp;#34;server-sync&amp;#34;;&#xA;        # security settings to prevent service from having too many priviliges&#xA;        ProtectSystem = &amp;#34;full&amp;#34;;&#xA;        ProtectHome = true;&#xA;        NoNewPriviliges = true;&#xA;        ReadWritePaths = &amp;#34;/data/media&amp;#34;;&#xA;      };&#xA;      # the action taken when the service runs&#xA;      script = builtins.readFile ./server-sync.bash;&#xA;    };&#xA;    timers.server-sync = {&#xA;      wantedBy = [ &amp;#34;timers.target&amp;#34; ];&#xA;      timerConfig = {&#xA;        # frequency of the service&#xA;        OnCalendar = &amp;#34;hourly&amp;#34;;&#xA;        # the service to associate the timer with&#xA;        Unit = &amp;#34;server-sync.service&amp;#34;;&#xA;      };&#xA;    };&#xA;  };&#xA;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;If it makes sense for your use case, you may simply want to add the script to be run inside of &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If you want to read more about the systemd security settings above you can find &lt;a href=&#34;https://xeiaso.net/blog/paranoid-nixos-2021-07-18&#34;&gt;a high level explanation&lt;/a&gt; and details of each individual setting at &lt;a href=&#34;https://man7.org/linux/man-pages/man5/systemd.exec.5.html&#34;&gt;systemd.exec(5)&lt;/a&gt;. A helpful command you can run to being reviewing security of services is &lt;code&gt;systemd-analyze security example.service&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;separating-service-script&#34;&gt;Separating Service Script&lt;/h2&gt;&#xA;&lt;p&gt;In case it&amp;rsquo;s helpful I thought to detail what I have inside of the script being read into the service.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m using &lt;code&gt;rsync&lt;/code&gt; to pull my music from a remote server to my homelab. I specify the path to an SSH key for authentication to the server by &lt;code&gt;-e &amp;quot;ssh -i /srv/server-sync/.ssh/server&amp;quot;&lt;/code&gt; and then the folders which should be kept in sync.&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;rsync -nat -e &amp;#34;ssh -i /srv/server-sync/.ssh/server&amp;#34; user@hostname:/home/mm/music/ /data/media/music/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;If you run into any errors with SSH, its likely due to incorrect permissions being set for &lt;code&gt;/srv/server-sync&lt;/code&gt;. Working permissions are as follows:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;chmod 700 /srv/server-sync/.ssh&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;chmod 644 /srv/server-sync/.ssh/server.pub&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;chmod 600 /srv/server-sync/.ssh/server&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;chmod 755 /srv/server-sync&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content>
</entry>
<entry>
<title>Configuring Proxmox Package Repositories</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/proxmox-package-repositories/"/>
<id>https://mich-murphy.com/proxmox-package-repositories/</id>
<published>2022-12-22T00:00:00+00:00</published>
<updated>2023-02-06T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I use &lt;a href=&#34;https://www.proxmox.com/en/&#34;&gt;Proxmox&lt;/a&gt; to create and manage VMs on my existing homelab (2012 Mac Mini). I use a combination of both VMs and containers for running applications and services, as I don&amp;rsquo;t always like to force everything to run in a container. For a deeper discussion on this topic I recommend listening to &lt;a href=&#34;https://thehomelab.show/2022/11/30/the-homelab-show-ep-79-virtualization-vs-containers/&#34;&gt;this podcast&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;failed-update-error&#34;&gt;Failed Update Error&lt;/h2&gt;&#xA;&lt;p&gt;I recently opened up Proxmox to begin configuring a &lt;a href=&#34;https://nixos.org/&#34;&gt;Nixos&lt;/a&gt; VM, and noticed I had a bunch of errors when updating the package database &lt;code&gt;Error: command apt-get update failed: exit code 100&lt;/code&gt;. I SSH&amp;rsquo;d into the machine and tried to update manually, this provided a more detailed output, it appeared I was trying to pull updates from a repo requiring a Proxmox subscription (which I don&amp;rsquo;t have, as I use it for personal use).&lt;/p&gt;&#xA;&lt;h2 id=&#34;updating-source-list&#34;&gt;Updating Source List&lt;/h2&gt;&#xA;&lt;p&gt;I managed to find a &lt;a href=&#34;https://pve.proxmox.com/wiki/Package_Repositories&#34;&gt;Proxmox wiki page&lt;/a&gt; which provided some clarity. In summary I fixed the issue by commenting out the following repository inside of &lt;code&gt;/etc/apt/sources.list.d/pve-enterprise.list&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;# deb https://enterprise.proxmox.com/debian/pve bullseye pve-enterprise&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;I then edited &lt;code&gt;/etc/apt/sources.list&lt;/code&gt; to look like the following, adding the pve-no-subscription repo:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;deb http://ftp.debian.org/debian bullseye main contrib&#xA;deb http://ftp.debian.org/debian bullseye-updates main contrib&#xA;&#xA;# PVE pve-no-subscription repository provided by proxmox.com,&#xA;# NOT recommended for production use&#xA;deb http://download.proxmox.com/debian/pve bullseye pve-no-subscription&#xA;&#xA;# security updates&#xA;deb http://security.debian.org/debian-security bullseye-security main contrib&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;After the above changes I ran &lt;code&gt;apt update&lt;/code&gt; and &lt;code&gt;apt dist-upgrade&lt;/code&gt;, everything updated without issue.&lt;/p&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;Proxmox highlight in the linked wiki that the pve-no-subscription repo isn&amp;rsquo;t tested as thoroughly as the repo for the paid subscription. For me this was an acceptable compromise, this may be different for you.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;h2 id=&#34;automated-script&#34;&gt;Automated Script&lt;/h2&gt;&#xA;&lt;p&gt;Tteck has provided a number of &lt;a href=&#34;https://github.com/tteck/Proxmox&#34;&gt;useful Proxmox scripts&lt;/a&gt;, one of them completes the above automatically, as well as removing subscription notices and optionally adding beta repositories. Have a read through &lt;a href=&#34;https://github.com/tteck/Proxmox/blob/main/misc/post-pve-install.sh&#34;&gt;the script&lt;/a&gt; before running:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;bash -c &amp;#34;$(wget -qLO - https://github.com/tteck/Proxmox/raw/main/misc/post-pve-install.sh)&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;</content>
</entry>
<entry>
<title>How to Mount S3 Object Storage in NixOS</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/s3-object-storage-nixos/"/>
<id>https://mich-murphy.com/s3-object-storage-nixos/</id>
<published>2022-12-14T00:00:00+00:00</published>
<updated>2023-01-20T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I previously wrote about how I mounted s3 object storage to my &lt;a href=&#34;https://mich-murphy.com/s3-object-storage/&#34;&gt;homelab running Ubuntu&lt;/a&gt;. I&amp;rsquo;ve since been configuring my &lt;a href=&#34;https://github.com/mich-murphy/nix-config/blob/939f3be13ded2a770ab542987a0a106e276f52b5/hosts/homelab/configuration.nix&#34;&gt;homelab&lt;/a&gt; to run using &lt;a href=&#34;https://nixos.org/&#34;&gt;NixOS&lt;/a&gt;. NixOS has a major advantage over Ubuntu for me, in that the entire system is configured declaratively, meaning once you have a working configuration you can use it to rebuild everything exactly how it was setup before.&lt;/p&gt;&#xA;&lt;h2 id=&#34;updating-nixos-configuration&#34;&gt;Updating NixOS Configuration&lt;/h2&gt;&#xA;&lt;p&gt;Whilst you could in theory install the required tools and manually mount an s3 object store in NixOS, a better approach is to specify mount instruction in the NixOS configuration file.&lt;/p&gt;&#xA;&lt;p&gt;After a default install of NixOS the configuration file is available to edit at &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;, to mount an s3 bucket we will expand on the &lt;code&gt;configuration.nix&lt;/code&gt; by adding a new &lt;code&gt;module&lt;/code&gt;, called &lt;code&gt;s3fs&lt;/code&gt;. We will do this by importing a separate &lt;code&gt;s3fs.nix&lt;/code&gt; file, which we will create shortly, for now edit &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt; and add the following:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/configuration.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/configuration.nix&#34;&gt;&lt;code&gt;{ config, pkgs, ... }:&#xA;&#xA;{&#xA;  imports = [&#xA;    ./hardware-configuration.nix # should be present by default&#xA;    ./modules/s3fs.nix&#xA;  ];&#xA;&#xA;  services.s3fs.enable = true;&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;add-modules-directory&#34;&gt;Add Modules Directory&lt;/h2&gt;&#xA;&lt;p&gt;Next we need to create the modules folder and create the &lt;code&gt;s3fs.nix&lt;/code&gt; file:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo mkdir /etc/nixos/modules &amp;amp;&amp;amp; sudo touch s3fs.nix&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h2 id=&#34;add-s3fs-module&#34;&gt;Add s3fs Module&lt;/h2&gt;&#xA;&lt;p&gt;Finally we can edit &lt;code&gt;/etc/nixos/modules/s3fs.nix&lt;/code&gt; and add the following:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window long&#34;&gt;&lt;figcaption&gt;Nix · /etc/nixos/modules/s3fs.nix&lt;/figcaption&gt;&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Nix code, /etc/nixos/modules/s3fs.nix&#34;&gt;&lt;code&gt;{ config, lib, pkgs, ... }:&#xA;&#xA;with lib;&#xA;&#xA;let&#xA;  cfg = config.services.s3fs;&#xA;in {&#xA;  options.services.s3fs = {&#xA;    enable = mkEnableOption &amp;#34;Mounts s3 object storage using s3fs&amp;#34;;&#xA;    keyPath = mkOption {&#xA;      type = types.str;&#xA;      default = &amp;#34;/etc/passwd-s3fs&amp;#34;;&#xA;    };&#xA;    mountPath = mkOption {&#xA;      type = types.str;&#xA;      default = &amp;#34;/mnt/data&amp;#34;;&#xA;    };&#xA;    bucket = mkOption {&#xA;      type = types.str;&#xA;      default = &amp;#34;data&amp;#34;;&#xA;    };&#xA;    url = mkOption {&#xA;      type = types.str;&#xA;      default = &amp;#34;https://ap-south-1.linodeobjects.com/&amp;#34;;&#xA;    };&#xA;  };&#xA;&#xA;  config = mkIf cfg.enable {&#xA;    systemd.services.s3fs = {&#xA;      description = &amp;#34;Linode object storage s3fs&amp;#34;;&#xA;      wantedBy = [ &amp;#34;multi-user.target&amp;#34; ];&#xA;      serviceConfig = {&#xA;        ExecStartPre = [&#xA;          &amp;#34;${pkgs.coreutils}/bin/mkdir -m 777 -pv ${cfg.mountPath}&amp;#34;&#xA;          &amp;#34;${pkgs.e2fsprogs}/bin/chattr +i ${cfg.mountPath}&amp;#34; # stop files being written to unmounted dir&#xA;        ];&#xA;        ExecStart = let&#xA;          options = [&#xA;            &amp;#34;passwd_file=${cfg.keyPath}&amp;#34;&#xA;            &amp;#34;use_path_request_style&amp;#34;&#xA;            &amp;#34;allow_other&amp;#34;&#xA;            &amp;#34;url=${cfg.url}&amp;#34;&#xA;            &amp;#34;umask=0000&amp;#34;&#xA;          ];&#xA;        in&#xA;          &amp;#34;${pkgs.s3fs}/bin/s3fs ${cfg.bucket} ${cfg.mountPath} -f &amp;#34;&#xA;            + lib.concatMapStringsSep &amp;#34; &amp;#34; (opt: &amp;#34;-o ${opt}&amp;#34;) options;&#xA;        ExecStopPost = &amp;#34;-${pkgs.fuse}/bin/fusermount -u ${cfg.mountPath}&amp;#34;;&#xA;        KillMode = &amp;#34;process&amp;#34;;&#xA;        Restart = &amp;#34;on-failure&amp;#34;;&#xA;      };&#xA;    };&#xA;  };&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;The above sets up a module for &lt;code&gt;services.s3fs&lt;/code&gt;, allowing us to enable/disable the service as well as edit the default settings. When enabled a &lt;code&gt;systemd&lt;/code&gt; service is created which mounts the s3 bucket, making it available at the specified path (&lt;code&gt;/mnt/data&lt;/code&gt; by default).&lt;/p&gt;&#xA;&lt;p&gt;The service will look for a file containing an access and API key by default in &lt;code&gt;/etc/passwd-s3fs&lt;/code&gt;, before running the service you will need to run the following to configure authentication details:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;echo &amp;#34;ACCESS_KEY:SECRET_KEY&amp;#34; | sudo tee /etc/passwd-s3fs&#xA;sudo chmod 600 /etc/passwd-s3fs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;div class=&#34;callout&#34;&gt;Note&lt;div class=&#34;frame&#34;&gt;&lt;p&gt;I recently ran into an issue when setting this up and had to comment out &lt;code&gt;&amp;quot;${pkgs.e2fsprogs}/bin/chattr +i ${cfg.mountPath}&amp;quot;&lt;/code&gt; to resolve the issue. I&amp;rsquo;ll update if I figure out a solution. For now everything is working without it.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&#xA;&lt;p&gt;Much of the credit for this post goes to a forum post on the &lt;a href=&#34;https://discourse.nixos.org/t/how-to-setup-s3fs-mount/6283&#34;&gt;NixOS Discourse&lt;/a&gt;.&lt;/p&gt;&#xA;</content>
</entry>
<entry>
<title>How to Mount S3-Compatible Object Storage on Ubuntu</title>
<link rel="alternate" type="text/html" href="https://mich-murphy.com/s3-object-storage/"/>
<id>https://mich-murphy.com/s3-object-storage/</id>
<published>2022-11-29T00:00:00+00:00</published>
<updated>2023-01-13T00:00:00+00:00</updated>
<content type="html">&lt;p&gt;I&amp;rsquo;m currently in the process of setting up a &lt;a href=&#34;https://linuxhandbook.com/homelab/&#34;&gt;homelab&lt;/a&gt;, one of the major tasks I want to manage is storage and streaming of music.&lt;/p&gt;&#xA;&lt;p&gt;For streaming music and managing metadata I&amp;rsquo;m using &lt;a href=&#34;https://roonlabs.com/&#34;&gt;Roon Server&lt;/a&gt;, this may change in the future as I explore other options. I store my music on a 2012 Mac Mini (the only computer I had lying around) which is running Ubuntu 20.04 as a VM in &lt;a href=&#34;https://www.proxmox.com/en/&#34;&gt;Proxmox&lt;/a&gt;. My Mac Mini has 2 x 250GB SSD storage, which has been quickly filling up as my music collection grows.&lt;/p&gt;&#xA;&lt;p&gt;My end goal is to build a dedicated homelab with a large amount of storage, either as &lt;a href=&#34;https://arstechnica.com/information-technology/2020/05/zfs-101-understanding-zfs-storage-and-performance/&#34;&gt;ZFS&lt;/a&gt; or a combination of &lt;a href=&#34;https://perfectmediaserver.com/02-tech-stack/mergerfs/&#34;&gt;MergerFS&lt;/a&gt; and &lt;a href=&#34;https://perfectmediaserver.com/02-tech-stack/snapraid/&#34;&gt;SnapRAID&lt;/a&gt;. Unfortunately building a dedicated homelab requires a significant financial contribution. I needed to find an interim solution - that&amp;rsquo;s where S3 compatible object storage comes in.&lt;/p&gt;&#xA;&lt;p&gt;I hadn&amp;rsquo;t previously used object storage, but had heard it worked well for storing files that don&amp;rsquo;t change frequently e.g. media files and music. After doing some quick research I discovered that I could mount an object store to a computer running Linux, macOS or FreeBSD using &lt;a href=&#34;https://github.com/s3fs-fuse/s3fs-fuse&#34;&gt;s3fs-fuse&lt;/a&gt;. I decided to test how well this worked by mounting the object store, adding some music to it and then pointing Roon Server to the object store. The steps I followed to mount the object store are detailed in &lt;a href=&#34;https://upcloud.com/resources/tutorials/mount-object-storage-cloud-server-s3fs-fuse&#34;&gt;this guide&lt;/a&gt;, my own summarised version is below.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mounting-storage&#34;&gt;Mounting Storage&lt;/h2&gt;&#xA;&lt;p&gt;The steps may vary depending on the provider you are using for object storage, for reference I am using &lt;a href=&#34;https://www.linode.com/&#34;&gt;Linode&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;install-s3fs-fuse&#34;&gt;Install s3fs-fuse&lt;/h3&gt;&#xA;&lt;p&gt;On Ubuntu this is nice and easy, the package name will likely vary depending on your Linux distro:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo apt install s3fs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;authentication&#34;&gt;Authentication&lt;/h3&gt;&#xA;&lt;p&gt;After creating an object store you will also need to create access keys. These should be a combination of:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;An &lt;code&gt;ACCESS_KEY&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;A &lt;code&gt;SECRET_KEY&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;We need to store the access key on the machine we plan to mount the object store to, as it will be needed to authenticate the connection:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;echo &amp;#34;ACCESS_KEY:SECRET_KEY&amp;#34; | sudo tee /etc/passwd-s3fs&#xA;sudo chmod 600 /etc/passwd-s3fs&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;h3 id=&#34;mounting&#34;&gt;Mounting&lt;/h3&gt;&#xA;&lt;p&gt;First we need to create the directory the object store will be mounted to:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;mkdir /mnt/my-object-storage&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Then we can mount the storage at the directory created above, filling in the placeholders with the appropriate values:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo s3fs {bucketname} {/mnt/my-object-storage} -o passwd_file=/etc/passwd-s3fs -o allow_other -o url=https://{private-network-endpoint}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;{bucketname}&lt;/code&gt; = the name of the bucket you wish to mount&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;{/mnt/my-object-storage}&lt;/code&gt; = directory to mount to the object store to (created earlier)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;{private-network-endpoint}&lt;/code&gt; = the url given by your provider to the object store e.g. &lt;a href=&#34;https://ap-south-1.linodeobjects.com&#34; class=&#34;no-print-url&#34;&gt;https://ap-south-1.linodeobjects.com&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;the &lt;code&gt;allow_other&lt;/code&gt; flag allows users other than root to have access to the bucket&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;testing&#34;&gt;Testing&lt;/h3&gt;&#xA;&lt;p&gt;Assuming you didn&amp;rsquo;t run into any errors after following the above instructions, you can now test the storage. I did this by opening a web browser and viewing the object store within my account dashboard at Linode, and copying some new files in to the object store on my homelab. If the file match between the web browser and your local machine then its a good sign that things are working. If this isn&amp;rsquo;t the case unmount the storage and try again, double checking the values provided when making the connection.&lt;/p&gt;&#xA;&lt;h3 id=&#34;persisting-after-reboot&#34;&gt;Persisting After Reboot&lt;/h3&gt;&#xA;&lt;p&gt;Now that everything has been mounted properly we can setup an entry within &lt;code&gt;/etc/fstab&lt;/code&gt;, so that the object store is mounted whenever the local machine reboots.&lt;/p&gt;&#xA;&lt;p&gt;First we need to unmount:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;sudo umount /etc/my-object-storage&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;Then we can edit &lt;code&gt;/etc/fstab&lt;/code&gt; in whichever editor you are most comfortable with (&lt;code&gt;vim&lt;/code&gt; is the correct answer), and add the following entry on a new line:&lt;/p&gt;&#xA;&lt;figure class=&#34;code-window with-legend&#34;&gt;Bash&lt;pre class=&#34;code&#34; tabindex=&#34;0&#34; role=&#34;group&#34; aria-label=&#34;Bash code&#34;&gt;&lt;code&gt;s3fs#bucketname /mnt/my-object-storage fuse _netdev,allow_other,passwd_file=/etc/passwd-s3fs,url=https://ap-south-1.linodeobjects.com/ 0 0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&#xA;&lt;p&gt;If you want more details on what each of the options do then I recommend checking out the FAQs at the &lt;a href=&#34;https://github.com/s3fs-fuse/s3fs-fuse/wiki/FAQ&#34;&gt;s3fs GitHub repo&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;How well did this work for music streaming? I was able to point Roon to music on the object store, scan tracks and stream music, though there were some limitations in performance:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;When selecting music to play there was ~3-5 second delay before the track started, when streaming from local storage or Tidal this is pretty much instant&lt;/li&gt;&#xA;&lt;li&gt;Scanning tracks and collecting metadata with Roon took significantly longer - 1 album on the object store took longer than my entire library of ~1,500 tracks on local storage&lt;/li&gt;&#xA;&lt;li&gt;Copying data across to the object store took significantly longer than an &lt;code&gt;rsync&lt;/code&gt; between machines, which I do between my local homelab and another server outside of my network&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The above thoughts seem to align with my initial research into object storage - it works well for reading data that doesn&amp;rsquo;t change frequently, once you start copying data across regularly then things slow down. If you are happy to accept a slight delay when first choosing music to play (this doesn&amp;rsquo;t seem to be an issue when moving on to play the next track in an album), then this solution could work well. Backups seem an obvious use case for object storage, I&amp;rsquo;m currently backing up my Roon settings and library to the object store, I schedule these backups at night and it is working well.&lt;/p&gt;&#xA;</content>
</entry>
</feed>

