Run and Inspect Containers Safely with machinectl

machinectl is systemd's control room for every container and VM it manages, from listing images to a clean shutdown. You will discover local systemd-managed machines, inspect an image, start a container as a service, open a shell in it, copy a file, and shut it down cleanly. The command controls the systemd machine manager, so it can work with containers and virtual machines registered by different managers. This guide uses the machinectl 255 manpage installed with systemd-container version 255.4-1ubuntu8.17.

Allow 15 to 30 minutes. You need a shell, an installed systemd-container package, and either an existing image or permission to create one. The inspection commands are read-only. Starting, copying, binding, importing, editing and removing images change state and may require elevated privileges or policy authorisation.

1. Check the local machine manager

Start by checking the installed version and listing what is already registered:

$ machinectl --version
$ machinectl --no-pager list
$ machinectl --no-pager list-images

On the reference host, the first command reports systemd 255 (255.4-1ubuntu8.17), followed by the build features. The other two commands report No machines. and No images. because this host has neither. Your output will differ if a container or image is present.

machinectl list shows running machines, not images waiting to be started. Use list-images for the latter. Both hide names beginning with a dot by default. Add --all when investigating downloaded base images or other hidden entries:

$ machinectl --no-pager list --all
$ machinectl --no-pager list-images --all

Checkpoint: You should now know whether you are operating an existing container, an image that can be started, or an empty machine store. Do not continue with a placeholder name until an actual image appears in the second listing.

2. Inspect an image before starting it

Set a shell variable to the exact image name shown by list-images. Replace the example with that name:

IMAGE=example-container
machinectl --no-pager image-status "$IMAGE"
machinectl --no-pager show-image "$IMAGE"

image-status is formatted for people. show-image exposes properties and is the better choice for scripts. Select properties with --property=, and add --value when you need values without the property names:

$ machinectl --no-pager show-image --property=Name "$IMAGE"
$ machinectl --no-pager show-image --property=Name --value "$IMAGE"

Empty properties are suppressed unless you use --all. Keep --no-pager in commands that feed logs or automation. Without it, systemd may send longer output through the configured pager.

3. Start the image as a service

Start a container by its image name:

$ sudo machinectl start "$IMAGE"
$ machinectl --no-pager list

start uses systemd-nspawn and looks in /var/lib/machines/, with additional search paths documented by the manpage. It starts [email protected] for the requested name. This is a service-style start, not an interactive console. If you need the container's full console while launching it, use systemd-nspawn directly.

Use status for human-readable state and recent log data:

$ machinectl --no-pager status "$IMAGE"
$ machinectl --no-pager status --lines=25 "$IMAGE"

The default status output includes up to 10 recent journal lines. The lines are supplied by the machine manager and may include console output; they are not necessarily the container's complete internal journal. Use show when a script needs properties rather than formatted status:

$ machinectl --no-pager show --property=Name,State,Leader "$IMAGE"

Checkpoint: The machine should appear in machinectl list, and status should identify the same name. If it does not, stop and read the service or manager error before retrying.

4. Choose login or shell deliberately

For a normal login prompt, use:

$ machinectl login "$IMAGE"

This needs a container running systemd as its init system and requests a getty. Exit the login session using the usual shell logout command or Ctrl-D.

For one interactive command or a shell without a login prompt, use shell:

$ machinectl shell "$IMAGE" /bin/sh
$ machinectl shell "$IMAGE" /bin/sh -c 'printf "machine=%s\n" "$HOSTNAME"'

The command also accepts a user prefix such as operator@, or the --uid= option. If no executable is supplied, it chooses the user's default shell and otherwise /bin/sh. The important scripting trap: machinectl shell does not propagate the invoked process's exit status. Use systemd-run --machine=... --wait when automation must receive that status.

Do not confuse an omitted machine name with a harmless no-op. machinectl shell and login without a name operate on the local host. Always include the container name in a copy-and-paste command intended for a remote or managed machine.

5. Copy a file, then verify it

Copy data into a running container with copy-to. This example creates a temporary source file on the host first:

printf '%s\n' 'managed by machinectl' > /tmp/machinectl-check.txt
sudo machinectl copy-to "$IMAGE" /tmp/machinectl-check.txt /tmp/machinectl-check.txt
machinectl shell "$IMAGE" /bin/sh -c 'cat /tmp/machinectl-check.txt'
rm -- /tmp/machinectl-check.txt

If the destination path is omitted, it matches the source path. When user and group namespaces differ, copied files and directories become owned by root inside the container, regardless of their host ownership. Use copy-from for the reverse direction and inspect the destination before overwriting it.

bind is different: it exposes a host path through a mount rather than copying its contents. It is only supported for systemd-nspawn containers and has restrictions when private user namespacing is enabled. Treat a bind mount as live access to host data. Use --read-only where a writeable view is unnecessary, and use --mkdir only when creating the destination is intended.

6. Stop the machine and undo changes

Prefer a clean shutdown:

$ sudo machinectl poweroff "$IMAGE"
$ machinectl --no-pager list

Warning: These are service-disrupting actions: verify the name before pressing Enter.

To undo the file-copy example, remove the destination from inside the container only after checking it is the file you created:

$ machinectl shell "$IMAGE" /bin/sh -c 'test -f /tmp/machinectl-check.txt && rm -- /tmp/machinectl-check.txt'

Destructive action: Do not use machinectl clean --all as routine tidy-up. It removes every image in the machine store, while plain clean removes hidden images. First inspect machinectl list-images --all, back up anything required, and use remove for a named image only when you have confirmed it is no longer needed. Recovery depends on an external copy.

Done means