Home / Alt manpages / systemd-run-generator(8)

  • systemd-run-generator(8)
  • Admin command
  • linux

Run a One-Off Boot Command with systemd-run-generator

You will use the kernel command-line option systemd.run= to run a command as a system service during boot, then choose whether the machine or container exits when that command finishes. The most practical test target is a disposable systemd-nspawn container. Allow about 15 minutes, including a check of the installed systemd version and a harmless command test.

This guide describes the behaviour of the installed systemd 255.4-1ubuntu8.17 package. The generator is not a normal command launcher: it is run by systemd during early boot and reads the kernel command line. Do not add a command to a production boot entry until you have a recovery route and have tested the quoting in a disposable environment.

1. Check the installed generator and container tool

No command in this section changes the system. Confirm the package version and the paths used by the local installation:

$ dpkg-query -W -f='${Package} ${Version}\n' systemd
systemd 255.4-1ubuntu8.17
$ command -v systemd-nspawn
/usr/bin/systemd-nspawn
$ systemd-nspawn --version | head -n 1
systemd 255 (255.4-1ubuntu8.17)

The generator itself is normally installed at /usr/lib/systemd/system-generators/systemd-run-generator. You do not invoke that path by hand. Systemd calls generators while assembling the units for a boot, and the generator creates the transient unit from the kernel command line.

Checkpoint

You are working with systemd 255 and have a container tool available. If systemd-nspawn is absent, stop here or use a separately managed test boot. Do not experiment first on a host whose boot process you cannot repair.

2. Run a harmless command in a disposable container

For a container test, prepare a container directory that already contains a working systemd installation. Replace /var/lib/machines/testbox with the path to your own disposable image. The command below runs id during the container boot:

$ sudo systemd-nspawn -D /var/lib/machines/testbox -b 'systemd.run="/usr/bin/id"'
Spawning container testbox on ...
...
uid=0(root) gid=0(root) groups=0(root)
Container testbox exited with status 0.

sudo is needed here because starting a booted system container normally requires access to namespaces, mounts and the container tree. The exact boot messages vary. The useful result is that the command ran and the container stopped with status 0.

The outer single quotes are removed by your shell. The inner double quotes travel as part of the container kernel command line and are removed by systemd-run-generator. That second quoting level keeps /usr/bin/id together as the command text. If you add arguments, quote the complete command line inside the double quotes:

$ sudo systemd-nspawn -D /var/lib/machines/testbox -b 'systemd.run="/usr/bin/printf %s\\n generator-ok"'
generator-ok
Container testbox exited with status 0.

This test does not modify the container files. It does start a container and execute a command as its boot-time root, so use an image you can discard or restore.

3. Understand the generated service and its default exit

The generator creates a unit named kernel-command-line.service. It is a normal service with Type=oneshot. By default, it sets both SuccessAction=exit and FailureAction=exit. In a booted container, that is why the container shuts down after id or printf finishes. The command's status is propagated to the container manager, so the outer shell can see success or failure.

This default is easy to miss. A successful command does not leave a login shell running, and a failed command is not merely logged while the boot continues. If you expected a persistent container, the exit action is the first thing to check.

Run the same harmless command with both actions disabled when you want the container to remain running after the command:

$ sudo systemd-nspawn -D /var/lib/machines/testbox -b \
    'systemd.run="/usr/bin/printf %s\\n generator-ok"' \
    systemd.run_success_action=none \
    systemd.run_failure_action=none
generator-ok

The generator accepts none for these two options, leaving the system running after the command completes. The nspawn process may then continue to host the container. Stop this test container from another terminal with the normal nspawn or systemd procedure for your image. Do not kill an unrelated production container just because its name is similar.

4. Run more than one command in order

You can specify systemd.run= more than once. The generated service receives multiple ExecStart= lines and runs the commands in order. Keep the examples independent and observable so a failure is easy to locate:

$ sudo systemd-nspawn -D /var/lib/machines/testbox -b \
    'systemd.run="/usr/bin/printf %s\\n first"' \
    'systemd.run="/usr/bin/printf %s\\n second"'
first
second
Container testbox exited with status 0.

The commands are not joined by a shell. Shell operators such as &&, pipes and redirections have no special meaning unless you explicitly start a shell as the command. That is a useful safety boundary, but it also means that a command copied from a shell script may not behave as expected. If you need shell syntax, make the shell and its script arguments explicit, and review them before booting.

5. Diagnose quoting and command failures

Start with a command that has an absolute path and no user input. If systemd.run="/usr/bin/id" works but a longer command does not, simplify it until the failing argument is visible. Check the two parsing layers separately: your shell parses the outer command, then the generator parses the quoted kernel-command-line value.

A common mistake is to omit the inner double quotes:

$ sudo systemd-nspawn -D /var/lib/machines/testbox -b 'systemd.run=/usr/bin/printf %s\\n broken'
systemd-nspawn: ...

The exact diagnostic depends on the container and systemd release, but the value is no longer protected as one command line. Restore the inner quotes and test with printf before trying a state-changing command. Do not interpret a container's generic boot failure as proof that the generator itself is broken.

If the command returns non-zero, inspect the final status from the container manager and the service logs inside the image when it remains running. With the default failure action, the container exits promptly, so capture the outer status:

$ sudo systemd-nspawn -D /var/lib/machines/testbox -b 'systemd.run="/usr/bin/sh -c exit 7"'
$ printf 'container status: %s\n' "$?"
container status: 7

The shell in that example is explicit, and exit 7 is deliberately harmless. Replace it with a real command only after checking its privileges, paths and rollback plan.

6. Move from a test to a real boot only with recovery ready

On a physical or virtual machine, systemd.run= must be placed on the kernel command line for that boot. Editing a bootloader entry can affect the next startup, and a command may run as root before the normal service set is available. Make a temporary, one-boot change where your bootloader supports it, keep an independent console or rescue path open, and remove the option after the test. Do not make a permanent bootloader edit for a command you have not already tested in a disposable container.

A command that writes files, changes accounts, alters networking or stops services is not a harmless smoke test. The generator's default exit actions can also shut down the machine immediately after the command. Use systemd.run_success_action=none and systemd.run_failure_action=none only when continued operation is genuinely intended, and remember that disabling the exit action does not make the command safer.

There is no persistent unit file to remove after a normal use of the generator: the unit is generated from that boot's kernel command line. Undo the test by rebooting without the temporary systemd.run= options, or by removing the options from the bootloader configuration if you made them persistent. Keep the original boot entry until the new one has been verified.

Done means

  • You confirmed the installed systemd version and found systemd-nspawn.
  • You ran a harmless absolute-path command in a disposable container.
  • You used the two quoting layers correctly and know where each is removed.
  • You understand that the generated unit is kernel-command-line.service, a oneshot service.
  • You checked the default success and failure exit actions before using a real boot.
  • You have a rescue path and an undo plan before adding the option to a persistent boot entry.