---
render_with_liquid: false
---
{% raw %}

# Tutorial: Create a Machine Profile

Machine profiles tell chezmoi how to render templates differently on each host — display scale, keyboard layout, performance preset, available features.

## When to Use a Profile

- You have more than one host (laptop + desktop, work + personal)
- Your hardware differs materially (HiDPI laptop vs 1080p external, ARM vs x86)
- Certain features only apply to specific setups (Niri on Wayland, AeroSpace on macOS)

## Step 1: Choose or Create a Preset

Presets live in `.chezmoidata/hardware.toml`:

```toml
[hardware.macbook-t2]
display_scale = 2.0
kbd_layout = "qwerty"
modifier_mode = "left-cmd-control"
perf_profile = "laptop"
wm = "aerospace"
[hardware.macbook-t2.features]
retina = true
touchid = true

[hardware.surface-pro]
display_scale = 1.5
kbd_layout = "colemak-dh"
modifier_mode = "standard"
perf_profile = "laptop"
wm = "niri"
[hardware.surface-pro.features]
touch = true
pen = true
```

If an existing preset fits: use its name. Otherwise, add a new one.

## Step 2: Select the Preset for This Host

Edit `~/.config/chezmoi/chezmoi.toml`:

```toml
[data]
machine = "surface-pro"      # choose from .chezmoidata/hardware.toml
theme = "dome-dark"
default_shell = "fish"
terminal_font_family = "JetBrainsMono Nerd Font"
terminal_font_size = 11       # larger for HiDPI, smaller for dense screens
```

`chezmoi.toml` is **not** in the repository — each host has its own. Generated by `chezmoi init` on first install.

## Step 3: Reference the Preset in Templates

Inside any `.tmpl` file:

```go
{{- $hw := index .hardware .machine }}

# Ghostty config — uses machine-specific font size
font-family = "{{ .terminal_font_family }}"
font-size = {{ .terminal_font_size }}

# DPI-aware settings
{{ if eq $hw.display_scale 2.0 }}
window-decoration = false
{{ else }}
window-decoration = true
{{ end }}

# Feature-gated config
{{ if $hw.features.touch }}
mouse-scroll-multiplier = 3
{{ end }}
```

After editing the template, run `chezmoi apply` or `dot apply`.

## Step 4: Add a New Preset

Suppose you have a new mini PC. Add to `.chezmoidata/hardware.toml`:

```toml
[hardware.my-nuc]
display_scale = 1.0           # external 1440p
kbd_layout = "qwerty"
modifier_mode = "standard"
perf_profile = "desktop"
wm = "gnome"
[hardware.my-nuc.features]
multi-monitor = true
gaming = true
```

Then on that host:

```toml
# ~/.config/chezmoi/chezmoi.toml
[data]
machine = "my-nuc"
```

Apply:

```sh
dot apply
```

Templates automatically pick up the new preset data.

## Step 5: Per-Preset Feature Flags

Feature flags in `.chezmoidata.toml` can be gated by preset:

```toml
# .chezmoidata.toml — defaults for all hosts
[features]
dms = false
linux_desktop = false
waybar = false
```

Override per-preset in `.chezmoidata/hardware.toml`:

```toml
[hardware.surface-pro.features]
touch = true
pen = true
dms = true                    # override feature default
waybar = true
```

In templates:

```go
{{- $hw := index .hardware .machine }}
{{- $features := mergeOverwrite .features $hw.features }}

{{ if $features.waybar }}
# ... waybar-specific config ...
{{ end }}
```

## Example: Font Size by Preset

A single Ghostty config that renders correctly on every host:

```go
# dot_config/ghostty/config.tmpl
{{- $hw := index .hardware .machine }}

theme = {{ .theme }}
font-family = "{{ .terminal_font_family }}"

# DPI-aware font sizing
{{ if eq $hw.display_scale 2.0 }}
font-size = 12               # Retina MacBook
{{ else if eq $hw.display_scale 1.5 }}
font-size = 11               # Surface Pro
{{ else }}
font-size = 14               # External 1440p/4K
{{ end }}
```

On the MacBook: 12pt. On the Surface: 11pt. On the NUC: 14pt. One template.

## Example: Platform × Preset

Combine `.chezmoi.os` with the preset:

```go
{{- $hw := index .hardware .machine }}

{{ if and (eq .chezmoi.os "darwin") (eq $hw.wm "aerospace") }}
# macOS + AeroSpace specifics
{{ else if and (eq .chezmoi.os "linux") (eq $hw.wm "niri") }}
# Linux + Niri specifics
{{ end }}
```

## Listing Known Presets

```sh
chezmoi execute-template '{{- range $name, $_ := .hardware }}{{ $name }}{{ "\n" }}{{ end }}'
# macbook-t2
# surface-pro
# geekom-a9
# my-nuc
```

## Changing Presets on an Existing Host

If you reassign a host to a different preset:

```sh
# Edit ~/.config/chezmoi/chezmoi.toml
# machine = "my-nuc" (was "surface-pro")

dot apply --dry-run   # preview changes
dot apply             # apply
```

Chezmoi re-renders every template with the new preset data. No reboot needed.

## Troubleshooting

### "Machine preset 'foo' not found"

The `machine` value in `~/.config/chezmoi/chezmoi.toml` doesn't match any key under `[hardware.*]` in `.chezmoidata/hardware.toml`. Check spelling.

### Templates Silently Use Defaults

Make sure you're using `{{- $hw := index .hardware .machine }}` at the top of templates. If `machine` is empty, `index` returns `nil` — reference with `{{- with $hw }}...{{- end }}` guards.

### Feature Flag Not Taking Effect

Verify the merge order:

```sh
chezmoi execute-template '{{- $hw := index .hardware .machine -}}{{- $f := mergeOverwrite .features $hw.features -}}{{ $f | toToml }}'
```

Per-preset features must override global defaults. If the output doesn't reflect that, check template syntax.

## Summary

Profiles let one source tree produce correctly-configured output on every host you own. The machinery:

1. `.chezmoidata/hardware.toml` defines presets (declarative)
2. `~/.config/chezmoi/chezmoi.toml` selects a preset per host (per-host override)
3. `.tmpl` files read `.hardware` + `.machine` (template-time)

## Next

- [Concept: Fleet Architecture](../01-concepts/04-fleet.md)
- [Reference: Templates](../03-reference/04-templates.md)
- [Reference: Feature Flags](../03-reference/05-feature-flags.md)
{% endraw %}
