Many fixes
This commit is contained in:
parent
3bfbd935fc
commit
a2ad25a8a4
81
README.md
81
README.md
|
|
@ -49,25 +49,88 @@ Partition the disk:
|
||||||
parted --script /dev/nvme0n1 -- mklabel gpt mkpart esp fat32 1MiB 512MiB mkpart primary 512MiB 100% set 1 boot on
|
parted --script /dev/nvme0n1 -- mklabel gpt mkpart esp fat32 1MiB 512MiB mkpart primary 512MiB 100% set 1 boot on
|
||||||
```
|
```
|
||||||
|
|
||||||
Create an encrypted pool:
|
Create an encrypted pool. The `-O` flags set filesystem properties that every
|
||||||
|
dataset inherits, so they only need to be given once here:
|
||||||
```bash
|
```bash
|
||||||
zpool create -f -o ashift=12 -O encryption=on -O keyformat=passphrase -O mountpoint=none rpool /dev/nvme0n1p2
|
zpool create -f \
|
||||||
|
-o ashift=12 \
|
||||||
|
-o autotrim=on \
|
||||||
|
-O encryption=on -O keyformat=passphrase \
|
||||||
|
-O compression=zstd \
|
||||||
|
-O dnodesize=auto \
|
||||||
|
-O xattr=sa \
|
||||||
|
-O acltype=posixacl \
|
||||||
|
-O relatime=on \
|
||||||
|
-O redundant_metadata=most \
|
||||||
|
-O mountpoint=none \
|
||||||
|
rpool /dev/nvme0n1p2
|
||||||
```
|
```
|
||||||
|
|
||||||
Create a root partition:
|
Create the datasets. Keeping `/nix` separate from `/` means the store can be
|
||||||
|
excluded from snapshot and rollback policy, which is what makes an
|
||||||
|
erase-your-darlings setup possible later:
|
||||||
```bash
|
```bash
|
||||||
zfs create -o mountpoint=legacy rpool/root
|
zfs create -o mountpoint=legacy rpool/root
|
||||||
mkdir -p /mnt
|
zfs create -o mountpoint=legacy rpool/nix
|
||||||
mount -t zfs rpool/root /mnt
|
zfs create -o mountpoint=legacy rpool/home
|
||||||
|
zfs create -o mountpoint=legacy -o recordsize=1M rpool/Storage
|
||||||
|
zfs create -o mountpoint=none -o refreservation=2G rpool/reserved
|
||||||
```
|
```
|
||||||
|
|
||||||
And home partition:
|
Mount them:
|
||||||
```bash
|
```bash
|
||||||
zfs create -o mountpoint=legacy -o compression=on rpool/home
|
mkdir -p /mnt
|
||||||
mkdir -p /mnt/home
|
mount -t zfs rpool/root /mnt
|
||||||
|
mkdir -p /mnt/nix /mnt/home /mnt/Storage
|
||||||
|
mount -t zfs rpool/nix /mnt/nix
|
||||||
mount -t zfs rpool/home /mnt/home
|
mount -t zfs rpool/home /mnt/home
|
||||||
|
mount -t zfs rpool/Storage /mnt/Storage
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Why these properties
|
||||||
|
|
||||||
|
Some of these can only be chosen when the pool or dataset is created, so they
|
||||||
|
are worth getting right the first time.
|
||||||
|
|
||||||
|
Creation-only, no way to change later without recreating the dataset:
|
||||||
|
|
||||||
|
- `encryption` and `keyformat` enable native ZFS encryption. This cannot be
|
||||||
|
turned on afterwards.
|
||||||
|
- `dnodesize=auto` allows dnodes larger than 512 bytes. Combined with
|
||||||
|
`xattr=sa` this keeps extended attributes inline instead of spilling into
|
||||||
|
separate blocks, which matters for a Nix store holding a hundred thousand
|
||||||
|
small files. Existing files keep whatever size they were written with.
|
||||||
|
- `ashift=12` matches the 4K physical sector size of modern NVMe drives.
|
||||||
|
|
||||||
|
Changeable later, but only applied to newly written blocks:
|
||||||
|
|
||||||
|
- `compression=zstd` gives a better ratio than the `lz4` that plain
|
||||||
|
`compression=on` selects. The Nix store compresses particularly well.
|
||||||
|
- `acltype=posixacl` is needed for systemd-journald to grant journal access
|
||||||
|
through ACLs. With the default of `off` that mechanism silently does nothing.
|
||||||
|
- `redundant_metadata=most` reduces the number of duplicate metadata writes.
|
||||||
|
- `recordsize=1M` suits bulk file storage. Leave it at the 128K default for
|
||||||
|
general purpose datasets, and drop it to 16K for databases or VM images.
|
||||||
|
- `autotrim=on` issues TRIM continuously. The `zpool-trim.timer` unit that
|
||||||
|
NixOS enables covers this periodically instead, so either approach works.
|
||||||
|
|
||||||
|
The `rpool/reserved` dataset never gets mounted. Its `refreservation` holds
|
||||||
|
space back so that the pool can be brought below its full mark by destroying
|
||||||
|
that one dataset. ZFS performance degrades noticeably above roughly 80%
|
||||||
|
capacity.
|
||||||
|
|
||||||
|
On a single disk pool there is no redundancy, so ZFS can detect corruption but
|
||||||
|
never repair it. Setting `copies=2` on the datasets that hold irreplaceable
|
||||||
|
data stores a second copy of each block at the cost of double the space:
|
||||||
|
```bash
|
||||||
|
zfs set copies=2 rpool/home
|
||||||
|
```
|
||||||
|
|
||||||
|
Snapshots are configured through `services.sanoid` in `hosts/Common/zfs.nix`.
|
||||||
|
ZFS dataset names are case sensitive, so an entry there has to match the
|
||||||
|
dataset exactly. A name that does not resolve leaves that dataset with no
|
||||||
|
snapshots at all while the service still reports success.
|
||||||
|
|
||||||
Format the boot partition:
|
Format the boot partition:
|
||||||
```bash
|
```bash
|
||||||
mkfs.fat -F 32 -n BOOT /dev/nvme0n1p1
|
mkfs.fat -F 32 -n BOOT /dev/nvme0n1p1
|
||||||
|
|
@ -112,7 +175,7 @@ sudo nixos-install --impure --flake git+https://git.thomasave.be/thomasave/Dotfi
|
||||||
|
|
||||||
Clean-up:
|
Clean-up:
|
||||||
```bash
|
```bash
|
||||||
umount /mnt/{home,boot}
|
umount /mnt/{home,nix,Storage,boot}
|
||||||
umount /mnt
|
umount /mnt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
({pkgs, ...}: {
|
({pkgs, ...}: {
|
||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
networking.hostName = host;
|
networking.hostName = host;
|
||||||
time.timeZone = "Europe/Brussels";
|
time.timeZone = "Asia/Bangkok";
|
||||||
nix.settings = {
|
nix.settings = {
|
||||||
substituters = [
|
substituters = [
|
||||||
"https://nix-community.cachix.org"
|
"https://nix-community.cachix.org"
|
||||||
|
|
@ -56,6 +56,13 @@
|
||||||
keep-derivations = true;
|
keep-derivations = true;
|
||||||
auto-optimise-store = true;
|
auto-optimise-store = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Resolve the bare "nixpkgs" flakeref to the exact revision this system
|
||||||
|
# was built from. Dev shells that use it then reuse the store paths the
|
||||||
|
# system already has, rather than each locking its own nixos-unstable
|
||||||
|
# snapshot and pulling in a duplicate toolchain.
|
||||||
|
nix.registry.nixpkgs.flake = nixpkgs;
|
||||||
|
nix.nixPath = ["nixpkgs=${nixpkgs}"];
|
||||||
users.users.${user} = {
|
users.users.${user} = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
extraGroups = ["wheel" "video"]; # Enable 'sudo' for the user.
|
extraGroups = ["wheel" "video"]; # Enable 'sudo' for the user.
|
||||||
|
|
|
||||||
|
|
@ -26,20 +26,11 @@ in {
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
jetbrains.pycharm
|
jetbrains.pycharm
|
||||||
rclone
|
|
||||||
opencode
|
|
||||||
claude-code
|
|
||||||
# zed-editor
|
|
||||||
uv
|
|
||||||
google-cloud-sdk
|
google-cloud-sdk
|
||||||
awscli2
|
awscli2
|
||||||
distrobox
|
distrobox
|
||||||
gnome-disk-utility
|
|
||||||
moonlight-qt
|
|
||||||
vscode
|
vscode
|
||||||
texliveFull
|
texliveFull
|
||||||
gnome-power-manager
|
|
||||||
smile
|
|
||||||
podman-compose
|
podman-compose
|
||||||
vesktop
|
vesktop
|
||||||
sox
|
sox
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
import { Astal, Gdk, Gtk } from "ags/gtk4";
|
import { Astal, Gdk, Gtk } from "ags/gtk4";
|
||||||
import app from "ags/gtk4/app";
|
import app from "ags/gtk4/app";
|
||||||
import { createBinding, createState, For, With, Accessor } from "ags";
|
import {
|
||||||
|
createBinding,
|
||||||
|
createComputed,
|
||||||
|
createState,
|
||||||
|
For,
|
||||||
|
With,
|
||||||
|
Accessor,
|
||||||
|
} from "ags";
|
||||||
import { createPoll } from "ags/time";
|
import { createPoll } from "ags/time";
|
||||||
import { subprocess, execAsync } from "ags/process";
|
import { subprocess, execAsync } from "ags/process";
|
||||||
import Pango from "gi://Pango";
|
import Pango from "gi://Pango";
|
||||||
|
|
@ -275,56 +282,55 @@ function Icons() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Volume(): JSX.Element {
|
function SpeakerButton({ speaker }: { speaker: Wp.Endpoint }): JSX.Element {
|
||||||
if (!wirePlumber) return <box />;
|
const volume = createBinding(speaker, "volume");
|
||||||
|
const mute = createBinding(speaker, "mute");
|
||||||
|
|
||||||
const audio = wirePlumber.audio;
|
const icon = volume.as((vol) => {
|
||||||
const icon = createBinding(audio.default_speaker, "volume").as((volume) => {
|
const percentage = vol * 100;
|
||||||
const vol = volume * 100;
|
const level = [
|
||||||
const icon = [
|
|
||||||
[101, "overamplified"],
|
[101, "overamplified"],
|
||||||
[67, "high"],
|
[67, "high"],
|
||||||
[34, "medium"],
|
[34, "medium"],
|
||||||
[1, "low"],
|
[1, "low"],
|
||||||
[0, "muted"],
|
[0, "muted"],
|
||||||
].find(([threshold]) => Number(threshold) <= vol)?.[1];
|
].find(([threshold]) => Number(threshold) <= percentage)?.[1];
|
||||||
return `audio-volume-${icon}-symbolic`;
|
return `audio-volume-${level}-symbolic`;
|
||||||
});
|
});
|
||||||
const css = createBinding(audio.default_speaker, "mute").as((mute) => {
|
const label = createComputed(() =>
|
||||||
return mute ? "margin-left:0;" : "margin-left: 0.7em;";
|
mute() ? "" : `${Math.floor(volume() * 100)}%`,
|
||||||
});
|
);
|
||||||
let volume = createBinding(audio.default_speaker, "volume");
|
const css = mute.as((muted) =>
|
||||||
let mute = createBinding(audio.default_speaker, "mute");
|
muted ? "margin-left: 0;" : "margin-left: 0.7em;",
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button class="item blue" onClicked={() => (speaker.mute = !speaker.mute)}>
|
||||||
class="item blue"
|
|
||||||
onClicked={() =>
|
|
||||||
(audio.default_speaker.mute = !audio.default_speaker.mute)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<box>
|
<box>
|
||||||
<image iconName={icon} />
|
<image iconName={icon} />
|
||||||
<With value={volume}>
|
<label label={label} css={css} />
|
||||||
{(vol) => (
|
|
||||||
<box>
|
|
||||||
<With value={mute}>
|
|
||||||
{(muted) => {
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
label={muted ? "" : `${Math.floor(vol * 100)}%`}
|
|
||||||
css={css}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</With>
|
|
||||||
</box>
|
|
||||||
)}
|
|
||||||
</With>
|
|
||||||
</box>
|
</box>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Volume(): JSX.Element {
|
||||||
|
if (!wirePlumber) return <box />;
|
||||||
|
|
||||||
|
// WirePlumber swaps out the whole endpoint object whenever the default
|
||||||
|
// sink changes, e.g. when a Bluetooth headset connects. Binding to the
|
||||||
|
// endpoint that happened to be default at startup leaves the widget stuck
|
||||||
|
// on that device forever, so track the property and rebuild on every swap.
|
||||||
|
const speaker = createBinding(wirePlumber.audio, "defaultSpeaker");
|
||||||
|
return (
|
||||||
|
<With value={speaker}>
|
||||||
|
{(endpoint: Wp.Endpoint | null) =>
|
||||||
|
endpoint ? <SpeakerButton speaker={endpoint} /> : <box />
|
||||||
|
}
|
||||||
|
</With>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function Workspaces({ connector }: { connector: string }): JSX.Element {
|
function Workspaces({ connector }: { connector: string }): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<box class="workspaces">
|
<box class="workspaces">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
sha256 = "sha256-qzc135IP5F2btxtOMUGMz+0azJhYL9KI0lcPG2KjcxU=";
|
sha256 = "sha256-qzc135IP5F2btxtOMUGMz+0azJhYL9KI0lcPG2KjcxU=";
|
||||||
};
|
};
|
||||||
extraPkgs = pkgs: [pkgs.tzdata];
|
extraPkgs = pkgs: [pkgs.tzdata];
|
||||||
profile = "export TZ=Europe/Brussels";
|
profile = "export TZ=Asia/Bangkok";
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
xdg.desktopEntries = {
|
xdg.desktopEntries = {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@
|
||||||
ncdu
|
ncdu
|
||||||
jq
|
jq
|
||||||
rclone
|
rclone
|
||||||
|
claude-code
|
||||||
|
uv
|
||||||
];
|
];
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
XDG_CONFIG_HOME = "${config.xdg.configHome}";
|
XDG_CONFIG_HOME = "${config.xdg.configHome}";
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,12 @@
|
||||||
"hyprland/window" = {max-length = 50;};
|
"hyprland/window" = {max-length = 50;};
|
||||||
tray = {spacing = 10;};
|
tray = {spacing = 10;};
|
||||||
"clock#clock2" = {
|
"clock#clock2" = {
|
||||||
timezone = "Europe/Brussels";
|
timezone = "Asia/Bangkok";
|
||||||
format = "{:%H:%M:%S}";
|
format = "{:%H:%M:%S}";
|
||||||
interval = 1;
|
interval = 1;
|
||||||
};
|
};
|
||||||
clock = {
|
clock = {
|
||||||
timezone = "Europe/Brussels";
|
timezone = "Asia/Bangkok";
|
||||||
format = "{:%Y-%m-%d}";
|
format = "{:%Y-%m-%d}";
|
||||||
interval = 3600;
|
interval = 3600;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,11 @@ in {
|
||||||
];
|
];
|
||||||
authKeyFile = "/home/user/.secrets/Tailscale/Aloria/authkey";
|
authKeyFile = "/home/user/.secrets/Tailscale/Aloria/authkey";
|
||||||
};
|
};
|
||||||
|
programs.winbox = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
|
||||||
services.usbmuxd.enable = true;
|
services.usbmuxd.enable = true;
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
libcamera
|
libcamera
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@
|
||||||
vulkan-loader
|
vulkan-loader
|
||||||
vulkan-validation-layers
|
vulkan-validation-layers
|
||||||
vulkan-extension-layer
|
vulkan-extension-layer
|
||||||
|
wiremix
|
||||||
|
gnome-disk-utility
|
||||||
|
moonlight-qt
|
||||||
|
gnome-power-manager
|
||||||
|
smile
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
# programs.hyprland = {
|
# programs.hyprland = {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
autoprune = true;
|
autoprune = true;
|
||||||
autosnap = true;
|
autosnap = true;
|
||||||
};
|
};
|
||||||
"rpool/storage" = {
|
"rpool/Storage" = {
|
||||||
frequently = 8;
|
frequently = 8;
|
||||||
yearly = 0;
|
yearly = 0;
|
||||||
monthly = 0;
|
monthly = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue