m3ta-home — Portable User Profiles for NixOS
Centralized, portable Home-Manager configurations. Manages user identities, preferences, feature sets, and assets across multiple NixOS hosts — from desktops and laptops to headless servers.
One repo, all your machines. Change once, deploy everywhere.
Architecture
m3ta-home/
├── flake.nix ← Entry point: inputs, lib, homeManagerModules
├── lib/
│ ├── default.nix ← Re-exports mkHome
│ └── mkHome.nix ← Composition engine (WHO + WHERE + WHAT)
├── modules/
│ ├── default.nix ← Module aggregation for flake export
│ └── paths.nix ← "m3ta-home".paths.srcRoot (pure eval compatibility)
├── profiles/
│ ├── base/ ← Always loaded: shell, CLI tools, secrets, nix settings
│ ├── contexts/
│ │ ├── desktop/ ← GUI host: WM, apps, theme, terminal, XDG
│ │ └── server/ ← Headless: minimal
│ └── sets/ ← Optional feature bundles
│ ├── coding/ ← core, editors, languages, LSP, agents
│ ├── gaming/ ← Steam, Gamescope, GPU tools
│ └── media/ ← OBS, ffmpeg, kdenlive, yt-dlp
├── users/
│ └── m3tam3re/
│ ├── identities/
│ │ ├── private.nix ← Personal: git (m3tm3re), SSH, JJ
│ │ └── work.nix ← Work: git (sascha.koenig), SSH, JJ
│ └── preferences/ ← Identity-independent: cliphist, difftastic
└── assets/
└── wallpapers/ ← Wallpaper collection (referenced via srcRoot)
The Three Dimensions
mkHome composes a complete Home-Manager configuration from three axes:
| Dimension | Options | Description |
|---|---|---|
| WHO | user + identity |
Which user, which identity (private/work) |
| WHERE | context |
desktop or server — mutually exclusive |
| WHAT | sets |
coding, gaming, media — freely combinable |
mkHome {
user = "m3tam3re";
identity = "private"; # ← "private" or "work"
context = "desktop"; # ← "desktop" or "server"
sets = ["coding" "gaming" "media"];
}
How it Works
- Base is always imported (shell, CLI tools, nix settings, nix-colors/dracula theme)
- Context is loaded based on
context— exactly one ofdesktoporserver - Sets are loaded on top — any combination of
coding,gaming,media - Identity overrides git username/email, SSH match blocks, and Jujutsu config
- Feature flags in the consuming host selectively enable/disable individual modules
Assertions
gamingandmediasets requiredesktopcontext (enforced via Nix assertions)codingworks in bothdesktopandservercontexts
Usage
In nixos-config (flake.nix)
inputs.m3ta-home = {
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home";
inputs.nixpkgs.follows = "nixpkgs";
};
Per-Host Configuration
The consuming repo (nixos-config) maps hosts to profiles:
# hosts/common/users/m3tam3re.nix
hostProfiles = {
m3-kratos = { context = "desktop"; sets = ["coding" "gaming" "media"]; };
m3-atlas = { context = "server"; sets = ["coding"]; };
m3-helios = { context = "server"; sets = []; };
};
Then enables specific feature flags per host:
home-manager.users.m3tam3re = {
imports = [
(m3ta-lib.mkHome {
user = "m3tam3re";
identity = "private";
inherit (profile) context sets;
})
hostFlags # ← per-host enable/disable of individual modules
];
};
Host-Specific Overrides
Monitor layouts, window rules, and XDG/MIME settings stay in nixos-config:
hosts/m3-kratos/home.nix— dual DP monitors, Hyprland workspaceshosts/m3-ares/home.nix— laptop eDP + HDMI, tuxedo-backlight- Server hosts need no
home.nix(no desktop config)
Identity System: Private vs. Work
The identity system allows switching between personal and work contexts on any machine — same dotfiles, different git identity, SSH config, and Jujutsu settings.
How Identities Work
| Private | Work | |
|---|---|---|
| Git user | m3tm3re | sascha.koenig |
| Git email | p@m3ta.dev | sascha.koenig@azintec.com |
| JJ email | m@m3tam3re.com | sascha.koenig@azintec.com |
| SSH hosts | code.m3ta.dev, github.com, private infra | git.az-gruppe.com, AZ servers |
Use Case: Work Laptop
If you bring a work NixOS machine into your fleet, you can use the same m3ta-home repo with a different identity:
# On the work machine's nixos-config:
(m3ta-lib.mkHome {
user = "m3tam3re";
identity = "work"; # ← switches git, SSH, JJ to work profile
context = "desktop";
sets = ["coding"];
})
This gives you:
- Your familiar shell setup (fish, starship, nushell)
- Your CLI tools (fzf, bat, eza, zoxide, etc.)
- Your editor configs (neovim, zed)
- Work git identity and SSH configuration automatically
- No gaming, media, or personal apps
Adding a New Identity
- Create
users/<username>/identities/<identity>.nix - Define git, SSH, and JJ settings for that identity
- Reference it via
identity = "<identity>"in mkHome
Feature Flags
Every module in m3ta-home has an enable option. Base modules default to true, everything else defaults to false. Flags are set per-host in the consuming repo.
Available Flags
base.shell.fish.enable # Fish shell
base.shell.nushell.enable # Nushell
base.shell.starship.enable # Starship prompt
base.cliTools.fzf.enable # Fuzzy finder
base.cliTools.bat.enable # Cat replacement
base.cliTools.eza.enable # Ls replacement
base.cliTools.zellij.enable # Terminal multiplexer
base.cliTools.zoxide.enable # Smarter cd
base.cliTools.direnv.enable # Directory-based env
base.cliTools.television.enable # TV fuzzy finder
base.cliTools.nitch.enable # System info
base.secrets.enable # pass-wayland, pinentry
desktop.wm.hyprland.enable # Hyprland window manager
desktop.wm.rofi.enable # Rofi launcher
desktop.wm.wayland.enable # Wayland tools (grim, slurp, etc.)
desktop.apps.obsidian.enable # Obsidian
desktop.apps.office.enable # LibreOffice
desktop.apps.crypto.enable # Bisq2, Monero, Trezor
desktop.theme.fonts.enable # Desktop fonts
desktop.theme.wallpapers.enable # Wallpaper collection
coding.editors.neovim.enable # Neovim
coding.editors.zed.enable # Zed editor (desktop only)
coding.lsp.enable # Language servers
coding.packages.enable # Bruno, Insomnia
coding.languages.python.enable
coding.languages.javascript.enable
coding.languages.typescript.enable
coding.languages.rustToolchain.enable
coding.languages.go.enable
profiles.gaming.steam.enable
profiles.gaming.gamescope.enable
profiles.gaming.gpu.enable
profiles.media.obs.enable
profiles.media.ffmpeg.enable
profiles.media.kdenlive.enable
profiles.media.ytDlp.enable
profiles.media.handbrake.enable
Adding a New Module
- Create the
.nixfile in the appropriate directory underprofiles/ - Define an
optionsblock withmkEnableOption - Define a
configblock withmkIf cfg.enable - Add the import to the parent
default.nix
Example:
# profiles/sets/coding/languages/elixir.nix
{ config, lib, pkgs, ... }:
with lib; let
cfg = config.coding.languages.elixir;
in {
options.coding.languages.elixir.enable = mkEnableOption "Elixir language support";
config = mkIf cfg.enable {
home.packages = with pkgs; [ elixir ];
};
}
Then add ./elixir to profiles/sets/coding/languages/default.nix.
Assets
Static assets (wallpapers, etc.) live in assets/. They are referenced via the "m3ta-home".paths.srcRoot option, which points to the flake source root and works in pure evaluation mode:
source = "${config."m3ta-home".paths.srcRoot}/assets/wallpapers";
Dependencies
| Input | Purpose |
|---|---|
nixpkgs |
Package set (follows nixos-config's nixpkgs) |
home-manager |
Home-Manager modules |
nix-colors |
Dracula theme palette (used everywhere) |
m3ta-nixpkgs |
Custom packages (zellij-ps, rofi-project-opener) and HM modules |
agenix |
Secret management |
NUR |
Additional packages |
Important Notes
- Overlays are managed at the NixOS level in
nixos-config, not here.home-manager.useGlobalPkgs = trueensures HM sees them. - Host-specific config (monitor layouts, window rules, XDG/MIME) belongs in
nixos-config/hosts/<name>/home.nix, not here. - Pure evaluation: All file references use
"m3ta-home".paths.srcRootinstead of relative../..paths.