skip to content

In systemd, what is a template unit such as [email protected], how do you start and enable one instance of it, and what do the %i and %I specifiers expand to inside the unit file?

level: middleimportance: nice to knowfreq 30%

answer

  1. one file, many activatable copies
  2. an @ before the suffix
  3. the name between @ and suffix
  4. two specifiers, escaped and unescaped
  5. the template itself is not runnable

basics

~20 s

A template unit has an @ before its suffix and is never run directly; you run instances of it, such as [email protected]. Inside the file, %i expands to the instance name as written and %I to its unescaped form.

solid answer

~50 s

A template is a unit file whose name ends in `@` before the suffix — `[email protected]`, `[email protected]`. It is a pattern, not a runnable unit. You activate an instance by putting a name between the `@` and the suffix: `systemctl start [email protected]`, and the same for enable. systemd loads `[email protected]` from disk and substitutes specifiers as it parses: `%i` is the instance name exactly as given, `%I` is the same string with escaping undone, `%p` is the prefix before the `@`, and `%n` the full unit name. That is what lets one file serve every TTY or every configured worker. Because instance names live in a unit name, path-like instances must be escaped — `systemd-escape -p /var/log` produces the encoded form. To enable the template itself rather than a named instance, its `[Install]` section needs `DefaultInstance=`.

code

bash · 3 lines
bash
systemctl start [email protected]
systemctl enable [email protected]
systemd-escape -p /var/log

go deeper

for a junior

Recognise an @ in a unit name as a template instance, and know that you start it by naming the instance, as in [email protected].

for a middle

Explain that the template file is loaded for any instance, what %i, %I, %p and %n expand to, and why a path-shaped instance name has to be escaped.

for a senior

Show the operational payoff: per-instance drop-ins over a shared template, enabling instances from configuration management, and diagnosing an instance whose specifier expanded to an escaped string.

for a principal

Judge when a template is the right model at all — many similar workloads differing only in one parameter — versus when the copies have diverged enough that separate units, or an orchestrator, is the honest answer.

## The problem templates solve You have one program that must run several times with a per-copy argument: a getty on each TTY, a tunnel per remote endpoint, a worker per shard, a user manager per logged-in user. Copying a unit file per instance means every change has to be applied N times and forgotten once. A template unit is systemd's answer: one file on disk, many activatable instances derived from it. ## Naming A template's file name carries an `@` immediately before the suffix, with nothing after it: ``` /usr/lib/systemd/system/[email protected] /etc/systemd/system/[email protected] ``` An *instance* is a unit name with text between the `@` and the suffix: `[email protected]`, `[email protected]`. When you refer to an instance, systemd finds no file with that exact name, strips the instance part, and loads the template file instead — recording the instance name so it can be substituted while parsing. The template itself is not runnable. `systemctl start [email protected]` has no instance name and cannot resolve one. ## Specifiers Specifiers are `%`-escapes expanded when the unit is loaded. The ones that matter for templates: - `%i` — the instance name exactly as it appears in the unit name, still in escaped form. - `%I` — the instance name with escaping undone, so a path that was encoded as `var-log` comes back as `/var/log`. - `%p` — the prefix, the part of the unit name before the `@` (`backup`). - `%n` — the full unit name including the suffix; `%N` is the same, unescaped. A typical use: ```ini [Unit] Description=Backup job for %i [Service] Type=oneshot ExecStart=/usr/local/bin/backup --target %i ``` Starting `[email protected]` runs `/usr/local/bin/backup --target db`. Note that `%i` is substituted into the argument vector as a single argument — it is not re-split by a shell, because `ExecStart=` never goes through one. A literal percent sign in a unit file is written `%%`. ## Escaping Unit names cannot contain `/`, so an instance that is a path must be encoded: `/` becomes `-`, and other awkward characters become `\x` escapes. `systemd-escape -p /var/log` prints the encoded form, and the `-u` option reverses it. This is the same encoding that gives mount units names like `var-log.mount`. Inside a template that takes a path as its instance, use `%I` rather than `%i` so the original path is restored before it reaches the command line. ## Enabling instances Enabling works per instance: ``` systemctl enable [email protected] ``` This creates a symlink named for the *instance* in the appropriate `.wants/` directory, pointing at the *template* file. Several enabled instances therefore share one target file, which is exactly the intent. Enabling the template itself is a different request, and it only works if the `[Install]` section supplies `DefaultInstance=` — otherwise systemd has no instance name to build a symlink from and reports that the name is missing an instance. Distributions use this for units like `[email protected]` that need a sensible default. ## Where you meet templates in practice - `[email protected]` and `[email protected]` — one login prompt per terminal device. - `[email protected]` — the per-user service manager, instanced by UID. - Socket-activated per-connection services, where the instance is derived from the connection. Recognising these on a live host matters: seeing `[email protected]` in `systemctl status` output and knowing to look for `[email protected]` on disk is the difference between finding the configuration in seconds and concluding the file is missing. ## Drop-ins and templates Drop-in directories follow the same pattern. `/etc/systemd/system/[email protected]/` applies to every instance; `/etc/systemd/system/[email protected]/` applies to that one instance only. That gives you a per-instance override without abandoning the shared template — the reason to reach for a template rather than N copies in the first place.

  • Why would a template use %I rather than %i for an instance that names a filesystem path?
    Because unit names cannot contain `/`, a path instance is stored escaped — `/var/log` becomes `var-log`. `%i` hands the command that escaped string, which is not a valid path. `%I` undoes the escaping first, so `ExecStart=` receives `/var/log`. Getting this backwards produces a unit that starts and then fails on a path that does not exist.
  • How do you override a setting for just one instance of a template?
    Use an instance-specific drop-in directory: `/etc/systemd/system/[email protected]/override.conf` applies only to that instance, while `/etc/systemd/system/[email protected]/` applies to all of them. Both are merged over the template file, so you keep one shared definition and carry only the per-instance delta.
  • What happens if you run systemctl enable on the template unit itself?
    It fails unless the `[Install]` section defines `DefaultInstance=`, because there is no instance name from which to build the symlink. With `DefaultInstance=` set, systemd enables that named instance instead. Enabling a specific instance always works and creates a symlink named for the instance pointing at the template file.

A template unit is a stencil: the file on disk is the cut-out, and each instance name is the surface you press it onto to get one concrete unit.

saying these in an interview costs you the question

  • You must copy the template once per instance
  • systemctl start on the template itself runs all instances
  • %i and %I are interchangeable
  • An instance name may contain a slash directly
  • Each enabled instance needs its own unit file on disk

context