IMPORTCTL(1)

✨ NAME ✨

importctl β€” download, import or export disk images πŸ’–

(it's importctl, from section 1 of the manual β€” yes bestie, the whole page is literally called IMPORTCTL(1), iconic)


✨ SYNOPSIS ✨

importctl [OPTIONS...] {COMMAND} [NAME...]

✨ DESCRIPTION ✨

Okay so importctl is your girl for downloading, importing, and exporting disk images, and it does all of it through systemd-importd.service(8). 🫢

It works on block-level disk images (think DDIs), file-system-level images (tarballs, yes), and OCI images too. And it supports disk images in one of these four classes:

When images get downloaded or imported, they land in these directories, depending on what you pass to --class=:

  • Class "machine" β†’ /var/lib/machines/
  • Class "portable" β†’ /var/lib/portables/
  • Class "sysext" β†’ /var/lib/extensions/
  • Class "confext" β†’ /var/lib/confexts/

✨ COMMANDS ✨

The commands below are the ones you can use:

πŸ’… pull-tar URL [NAME]

Downloads a .tar image from the URL you gave it, and makes it available under the local name you specified, inside the image directory for whichever --class= you selected. The URL must be of type "http://" or "https://", and it must point at a .tar, .tar.gz, .tar.xz or .tar.bz2 archive file. If you leave out the local image name, it gets derived automatically from the last component of the URL, with the suffix stripped off. πŸŽ€

Before the image is made available it gets verified β€” unless you passed --verify=no. Verification happens one of two ways: either via a file named after the image with the suffix .sha256 plus a detached .sha256.asc or .sha256.gpg signature, or via separate SHA256SUMS and SHA256SUMS.asc or SHA256SUMS.gpg files. Those signature files need to be made available on the same web server, under the same URL as the .tar file β€” no sneaky side-loading. With --verify=checksum, only the SHA256 checksum for the file is verified, based on the .sha256-suffixed file or the SHA256SUMS file. With --verify=signature, the sha checksum file gets verified first with the detached GPG signature of .sha256 or SHA256SUMS. The public key for this verification step needs to live in /usr/lib/systemd/import-pubring.pgp or /etc/systemd/import-pubring.pgp. πŸ”‘

If -keep-download=yes is specified, the image gets downloaded and stored in a read-only subvolume/directory inside the image directory, named after the URL you specified and its HTTP etag (see HTTP ETag[1] for more info, iykyk). A writable snapshot is then taken from that subvolume and named after the local name you specified. This is the whole point: creating multiple instances of the same URL stays efficient, because you don't have to download multiple times. And if you want only the read-only image and don't want the writable snapshot at all, just specify - as the local name. πŸ¦‹

Heads up: pressing Control+c while this command is running will not abort the download. Use cancel-transfer, described further down.

Added in version 256. ⭐

🌸 pull-raw URL [NAME]

Downloads a .raw disk image from the URL you gave it, and makes it available under your specified local name in the image directory for the selected --class=. The URL must be of type "http://" or "https://". The image has to be either a qcow2 or raw disk image, optionally compressed as .gz, .xz, or .bz2. If you skip the local name, it's automatically derived from the last component of the URL, suffix removed. Same energy as above. ✨

Image verification is identical for raw and tar images (see the bit above, bestie).

If the downloaded image is in qcow2 format, it gets converted into a raw image file before it's made available.

If -keep-download=yes is specified, the image gets downloaded and stored in a read-only file in the image directory, named after the specified URL and its HTTP etag. A writable copy is then made from that file, named after the specified local name. Again: this keeps creating multiple instances of the same URL efficient, since multiple downloads are not necessary. To create only the read-only image and avoid making the writable copy, specify - as the local name. πŸ’œ

Same warning, no cap: pressing Control+c during this command will not abort the download. Use cancel-transfer.

Added in version 256. ⭐

🧚 pull-oci REF [NAME]

Downloads the OCI container image you specified, and makes it available under the specified local name in the image directory for the selected --class=. The first argument must be an OCI container reference, like "library/nginx". If the local name is omitted, it's automatically derived from the last component of the URL, with its suffix removed.

When you're downloading images of this type, no image verification is done beyond the usual authentication of the HTTPS certificates. That's it, that's the whole check. πŸ™ˆ

You already know: pressing Control+c during this command will not abort the download. Use cancel-transfer.

Added in version 260. ⭐

πŸ“ import-tar FILE [NAME], import-raw FILE [NAME]

Imports a TAR or RAW image and places it under the specified name in the image directory for the image class you selected via --class=.

  • When import-tar is used, the file you pass as the first argument should be a tar(1) archive, possibly compressed with xz(1), gzip(1), zstd(1), or bzip2(1). It then gets unpacked into its own subvolume/directory.
  • When import-raw is used, the file should be a qcow2 or raw disk image, possibly compressed with xz, gzip, zstd or bzip2.

If you don't specify the second argument (the resulting image name), it's automatically derived from the file name. And if you pass the filename as -, the image is read from standard input β€” in that case the second argument is mandatory, no exceptions. πŸ’€

No cryptographic validation is done when importing the images. None. Zero. Nada.

Much like image downloads, ongoing imports may be listed with list and aborted with cancel-transfer. πŸ‘‘

Added in version 256. ⭐

🌈 import-fs DIRECTORY [NAME]

Imports an image stored in a local directory into the image directory for the image class selected via --class=, and operates similarly to import-tar or import-raw β€” except the first argument is the source directory. If supported, this command will create a btrfs(8) snapshot or subvolume for the new image. Slay. 🌱

Added in version 256. ⭐

πŸŽ€ export-tar NAME [FILE], export-raw NAME [FILE]

Exports a TAR or RAW image and stores it in the specified file. The first parameter should be an image name. The second parameter should be the file path the TAR or RAW image is written to.

How the compression goes down:

  • path ends in .gz β†’ compressed with gzip(1)
  • path ends in .xz β†’ compressed with xz(1)
  • path ends in .zst β†’ compressed with zstd(1)
  • path ends in .bz2 β†’ compressed with bzip2(1)
  • path ends in none of those β†’ file is left uncompressed

If the second argument is missing, the image is written to standard output. Compression may also be explicitly selected with the --format= switch β€” which is especially useful if you left the second parameter unspecified. 😌

Much like image downloads and imports, ongoing exports may be listed with list and aborted with cancel-transfer.

Real talk: currently, only directory and subvolume images may be exported as TAR images, and only raw disk images as RAW images.

Added in version 256. ⭐

πŸ¦‹ list-transfer

Shows a list of image downloads, imports and exports that are currently in progress. Perfect for when you're like "wait, what's still running??" πŸ‘€

Added in version 256. ⭐

πŸ’– cancel-transfer ID...

Aborts a download, import or export of the image with the specified ID. To list ongoing transfers and their IDs, use list.

Added in version 256. ⭐

πŸ‘‘ list-images

Shows a list of already downloaded/imported images. Your little collection, displayed with love. πŸ’…

Added in version 256. ⭐

✨ OPTIONS ✨

okay bestie, here's the whole menu of flags β€” read it, love it, don't skip any of it πŸ’…

  • --read-only β€” when you use this with pull-raw, pull-tar, import-raw, import-tar or import-fs, the image you get is read-only. no edits, no notes, just vibes.

Added in version 256.

  • --verify= β€” when you're downloading an image, this says whether the image gets verified before it's actually made available to you. takes one of "no", "checksum" and "signature". if it's "no", no verification happens at all (living dangerously, honestly πŸ’€). if it's "checksum", the download gets checked for integrity after the transfer is done, but no signatures get verified. if it's "signature", then the checksum is verified and the image's signature is checked against a local keyring of trustable vendors. it is strongly recommended to set this to "signature" if the server and protocol support it. defaults to "signature". πŸ‘‘

Added in version 256.

  • --force β€” when you're downloading an image and a local copy already exists under the local name you specified, this deletes it first and replaces it with the newly downloaded image. out with the old, in with the new, no cap 🫢

Added in version 256.

  • --format= β€” when used with the export-tar or export-raw commands, this sets the compression format for the resulting file. takes one of "uncompressed", "xz", "gzip", "zst", "bzip2". by default the format is figured out automatically from the output image file name you passed. ✨

Added in version 256.

  • -q, --quiet β€” suppresses the extra informational output while it's running. less chatter, same slay.

Added in version 256.

  • -H, --host= β€” run the operation remotely. give it a hostname, or a username and hostname separated by "@", to connect to. the hostname can optionally be suffixed by a port ssh is listening on, separated by ":", and then a container name, separated by "/", which connects you directly to a specific container on that host. this uses SSH to talk to the remote machine manager instance. container names can be enumerated with machinectl -H HOST. and put IPv6 addresses in brackets, bestie 🧚
  • -M, --machine= β€” connect to systemd-importd.service(8) running in a local container, so the operation you asked for happens inside that container. πŸŽ€

Added in version 256.

  • --class=, -m, -P, -S, -C β€” picks the image class for the downloaded images, which mostly just picks the directory it downloads into. --class= takes "machine", "portable", "sysext" or "confext" as its argument. the short options -m, -P, -S, -C are shortcuts for --class=machine, --class=portable, --class=sysext, --class=confext respectively.

quick heads up: --keep-download= defaults to true for --class=machine and false otherwise, see below πŸ‘‡

Added in version 256.

  • --keep-download=, -N β€” takes a boolean argument. when used with pull-raw or pull-tar, it selects whether to download directly into the local image name you specified, or whether to download into a read-only copy first and then make a writable copy after the download is done. defaults to true for --class=machine, and false otherwise.

the -N switch is a shortcut for --keep-download=no. πŸ¦‹

Added in version 256.

  • --json=MODE β€” shows output formatted as JSON. expects one of "short" (the shortest possible output, no redundant whitespace or line breaks), "pretty" (a pretty version of the same, with indentation and line breaks) or "off" (turns JSON output off β€” and this is the default).
  • -j β€” same as --json=pretty if you're running on a terminal, and --json=short otherwise. it reads the room, literally 😭
  • --no-pager β€” don't pipe the output into a pager.
  • --no-legend β€” don't print the legend, meaning the column headers and the footer with hints.
  • --no-ask-password β€” don't query you for authentication for privileged operations.
  • -h, --help β€” print a short help text and exit. the classic.
  • --version β€” print a short version string and exit.

🌈 EXAMPLES 🌈

Example 1. Download an Ubuntu TAR image and open a shell in it

# importctl pull-tar -mN https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64-root.tar.xz
# systemd-nspawn -M jammy-server-cloudimg-amd64-root

this downloads and verifies the .tar image you specified, and then uses systemd-nspawn(1) to open a shell inside it. obsessed πŸ’–

Example 2. Download an Ubuntu RAW image, set a root password in it, start it as a service

# importctl pull-raw -mN \
      https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64-disk-kvm.img \
      jammy
# systemd-firstboot --image=/var/lib/machines/jammy.raw --prompt-root-password --force
# machinectl start jammy
# machinectl login jammy

this downloads the .raw image you specified and makes it available under the local name "jammy". then a root password gets set with systemd-firstboot(1). after that the machine is started as a system service. and with the last command, a login prompt into the container is requested. full main character energy πŸ‘‘

Example 3. Exports a container image as tar file

# importctl export-tar -m fedora myfedora.tar.xz

exports the container "fedora" as an xz-compressed tar file named myfedora.tar.xz into the current directory. cute and compressed 🌸


⭐ EXIT STATUS ⭐

on success, 0 is returned. otherwise you get a non-zero failure code. that's it, that's the whole story πŸ’œ

πŸ’– ENVIRONMENT πŸ’–

okay bestie, this is where it gets good. these are the env vars that shape how the tool talks to you β€” and yes, every single one matters ✨

πŸ¦‹ $SYSTEMD_LOG_LEVEL

the max log level of messages that actually get emitted β€” anything with a higher log level (i.e. less important, like, the chatter) gets suppressed, periodt.

takes a comma-separated list of values. each value is either one of these, listed in order of decreasing importance: emerg, alert, crit, err, warning, notice, info, debug β€” or just an integer in the range 0...7. see syslog(3) for the whole lore.

each value may optionally be prefixed with one of console, syslog, kmsg or journal, followed by a colon, to set the max log level for that specific log target. so like:

SYSTEMD_LOG_LEVEL=debug,console:info

means "log at debug level everywhere, except the console which stays at info". cute, right?

⚠️ heads up: the global maximum log level takes priority over any per-target max log levels. no cap, that's just how it is.

🌸 $SYSTEMD_LOG_COLOR

a boolean. if true, messages written to the tty get colored according to priority β€” aesthetic AND functional πŸ’…

this setting is only useful when messages are written directly to the terminal, because journalctl(1) and other tools that display logs will color messages based on the log level on their own. they got it covered, fr.

πŸŽ€ $SYSTEMD_LOG_TIME

a boolean. if true, console log messages get prefixed with a timestamp. ⏰

again, only useful when messages are written directly to the terminal or a file, because journalctl(1) and friends attach timestamps based on the entry metadata on their own. they're not gonna let you down.

πŸ¦‹ $SYSTEMD_LOG_LOCATION

a boolean. if true, messages get prefixed with a filename and line number in the source code where the message originates. absolute main character energy for debugging πŸ”₯

note that the log location is often attached as metadata to journal entries anyway β€” but ngl, including it directly in the message text can still be super convenient when you're debugging programs.

⭐ $SYSTEMD_LOG_TID

a boolean. if true, messages get prefixed with the current numerical thread ID (TID).

note that this information is attached as metadata to journal entries anyway. still, including it directly in the message text can be convenient when debugging programs. lowkey underrated.

πŸ’œ $SYSTEMD_LOG_TARGET

the destination for log messages, bestie. pick one:

  • console β€” log to the attached tty
  • console-prefixed β€” log to the attached tty, but with prefixes encoding the log level and "facility", see syslog(3)
  • kmsg β€” log to the kernel circular log buffer
  • journal β€” log to the journal
  • journal-or-kmsg β€” log to the journal if available, and to kmsg otherwise
  • auto β€” determine the appropriate log target automatically, the default
  • null β€” disable log output entirely (silence, iconic)

πŸ“ $SYSTEMD_LOG_RATELIMIT_KMSG

whether to ratelimit kmsg or not. takes a boolean. defaults to "true". if disabled, systemd will not ratelimit messages written to kmsg β€” so go off, but like, responsibly.

🫢 $SYSTEMD_PAGER, $PAGER

the pager to use when --no-pager is not given. $SYSTEMD_PAGER is used if set; otherwise $PAGER is used. if neither $SYSTEMD_PAGER nor $PAGER are set, a set of well-known pager implementations gets tried in turn β€” including less(1) and more(1) β€” until one is found. if no pager implementation is discovered, no pager is invoked. it's giving "we'll figure it out".

setting those environment variables to an empty string or the value "cat" is equivalent to passing --no-pager.

🚨 note: if $SYSTEMD_PAGERSECURE is not set, $SYSTEMD_PAGER and $PAGER can only be used to disable the pager (with "cat" or ""), and are otherwise ignored. yes, this is a whole thing, keep reading.

🧚 $SYSTEMD_LESS

override the options passed to less β€” by default it's "FRSXMK".

users might want to change two options in particular, and honestly? same:

K

this option instructs the pager to exit immediately when Ctrl+C is pressed. to allow less to handle Ctrl+C itself and switch back to the pager command prompt, unset this option.

if the value of $SYSTEMD_LESS does not include "K", and the pager that is invoked is less, Ctrl+C will be ignored by the executable, and needs to be handled by the pager. so like, know what you're signing up for πŸ’€

X

this option instructs the pager to not send termcap initialization and deinitialization strings to the terminal. it's set by default so command output stays visible in the terminal even after the pager exits β€” which slays. nevertheless, it prevents some pager functionality from working, in particular paged output cannot be scrolled with the mouse. tradeoffs, bestie πŸ₯²

πŸ“Œ note: setting the regular $LESS environment variable has no effect for less invocations by systemd tools.

see less(1) for more discussion.

🌈 $SYSTEMD_LESSCHARSET

override the charset passed to less β€” by default "utf-8", if the invoking terminal is determined to be UTF-8 compatible.

πŸ“Œ note: setting the regular $LESSCHARSET environment variable has no effect for less invocations by systemd tools.

πŸ‘‘ $SYSTEMD_PAGERSECURE

okay this one is the big one, sit down. β˜•

common pager commands like less(1), in addition to "paging" β€” i.e. scrolling through the output β€” support opening of or writing to other files and running arbitrary shell commands. when commands are invoked with elevated privileges, for example under sudo(8) or pkexec(1), the pager becomes a security boundary. care must be taken that only programs with strictly limited functionality are used as pagers, and that unintended interactive features like opening or creation of new files or starting of subprocesses are not allowed. "secure mode" for the pager may be enabled as described below, if the pager supports that (most pagers are not written in a way that takes this into consideration 😭).

it is recommended to either explicitly enable "secure mode" or completely disable the pager using --no-pager or PAGER=cat when allowing untrusted users to execute commands with elevated privileges.

this option takes a boolean argument. when set to true, the "secure mode" of the pager is enabled. in "secure mode", LESSSECURE=1 will be set when invoking the pager, which instructs the pager to disable commands that open or create new files or start new subprocesses. currently only less(1) is known to understand this variable and implement "secure mode".

when set to false, no limitation is placed on the pager. setting SYSTEMD_PAGERSECURE=0 or not removing it from the inherited environment may allow the user to invoke arbitrary commands. πŸ”₯ (deranged behaviour, but okay)

when $SYSTEMD_PAGERSECURE is not set, systemd tools attempt to automatically figure out if "secure mode" should be enabled and whether the pager supports it. "secure mode" is enabled if the effective UID is not the same as the owner of the login session, see geteuid(2) and sd_pid_get_owner_uid(3), or when running under sudo(8) or similar tools ($SUDO_UID is set [2]). in those cases, SYSTEMD_PAGERSECURE=1 will be set and pagers which are not known to implement "secure mode" will not be used at all. note that this autodetection only covers the most common mechanisms to elevate privileges and is intended as convenience. it is recommended to explicitly set $SYSTEMD_PAGERSECURE or disable the pager. don't leave it to vibes.

🚨 note: if the $SYSTEMD_PAGER or $PAGER variables are to be honoured, other than to disable the pager, $SYSTEMD_PAGERSECURE must be set too.

πŸ’… $SYSTEMD_COLORS

takes a boolean argument, or a special value. by default (unset), systemd and related utilities will use colors in their output if possible. if $COLORTERM is set to "truecolor" or "24bit", 24-bit colors will be enabled, 256 colors otherwise β€” unless $NO_COLOR or $TERM indicates colors are disabled.

  • true β€” same as unset, except that $NO_COLOR is ignored.
  • false β€” the output will be monochrome. sad beige output for sad beige times.
  • "16", "256", "24bit" β€” always use the base 16 ANSI colors, 256 colors, or 24 bit color, respectively. no questions asked.
  • "auto-16", "auto-256", "auto-24bit" β€” use the given quantity of colours, subject to $TERM, and what the console is connected to.

🌟 $SYSTEMD_URLIFY

the value must be a boolean. controls whether clickable links should be generated in the output for terminal emulators supporting this. this can be specified to override the decision that systemd makes based on $TERM and other conditions. clickable links are genuinely such a slay 🫢


πŸ”— SEE ALSO πŸ”—

systemd(1), systemd-importd.service(8), systemd-nspawn(1), systemd-vmspawn(1), machinectl(1), portablectl(1), systemd-sysext(8), systemd-confext(8), tar(1), xz(1), gzip(1), zstd(1), bzip2(1)


πŸ“ NOTES πŸ“

  1. HTTP ETag β€” https://en.wikipedia.org/wiki/HTTP_ETag
  2. It is recommended for other tools to set and check $SUDO_UID as appropriate, treating it as a common interface.

and that's the whole page, bestie!! πŸŽ‰ you just read the entirety of importctl(1) from systemd 261.3 β€” go forth and import responsibly, keep your pager secure, and remember: the vibes are immaculate but the flags are exact πŸ’–βœ¨πŸŽ€πŸ¦‹πŸŒΈπŸ’…πŸ«Ά

πŸ“¦ where this page comes from