diff --git a/man/machine-info.xml b/man/machine-info.xml
index 1e75037f730..e259ec5cd45 100644
--- a/man/machine-info.xml
+++ b/man/machine-info.xml
@@ -179,7 +179,10 @@
ConditionMachineTag= and AssertMachineTag= unit settings, see
systemd.unit5
for details. They may be queried and changed with the tags command of
- hostnamectl1.
+ hostnamectl1.
+ Tags may also be declared in machine-tags.d/ drop-in directories, see
+ machine-tags.d5,
+ and applied with hostnamectl tags --apply.
@@ -252,6 +255,7 @@ TAGS=room=kitchen:company.location=berlin:company.role=personal
hostname5
machine-id5
hostnamectl1
+ machine-tags.d5
systemd-hostnamed.service8
diff --git a/man/machine-tags.d.xml b/man/machine-tags.d.xml
new file mode 100644
index 00000000000..2996f2d5a7b
--- /dev/null
+++ b/man/machine-tags.d.xml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+ machine-tags.d
+ systemd
+
+
+
+ machine-tags.d
+ 5
+
+
+
+ machine-tags.d
+ Configure machine tags
+
+
+
+
+ /etc/machine-tags.d/*.tags
+ /run/machine-tags.d/*.tags
+ /usr/local/lib/machine-tags.d/*.tags
+ /usr/lib/machine-tags.d/*.tags
+
+
+
+
+ Description
+
+ The files in the above directories declare machine tags to be added or removed from the list of
+ tags in the TAGS= field of
+ machine-info5.
+ See that page for the details of how tags are defined and used.
+
+ Those files are read and applied by
+ systemd-hostnamed.service8
+ when requested via hostnamectl tags --apply, see
+ hostnamectl1, or
+ directly via the io.systemd.Hostname.ApplyTags() call. The resulting tag list is
+ stored in /etc/machine-info, thus applying the same configuration repeatedly has no
+ further effect.
+
+ This allows OS vendors, image builders and system extensions (see
+ systemd-sysext8) to
+ declare tags in a drop-in directory hierarchy, instead of setting them imperatively.
+
+
+
+
+
+ Configuration Format
+
+ Each file contains a list of entries separated by whitespace (spaces, tabs, or newlines). Empty
+ lines and comments (lines starting with # or ;) are ignored.
+
+
+ An entry without prefix (or, equivalently, prefixed with +) adds a tag. An
+ entry prefixed with - removes matching tags. The pattern may be a literal tag value,
+ or a shell-style glob, see
+ glob7.
+ For example, -vendor.install.* removes all tags starting with
+ vendor.install., -company.role=* removes any value assigned to the
+ company.role key, and -* removes all tags. Removing a tag that is
+ not set has no effect.
+
+ Entries are applied in the order they appear, across all files in the order described below. A tag
+ may hence be removed by a glob and added back by a later entry. When a parameterized tag
+ key=value is added and a
+ different value is already assigned to the same key, the previous assignment is replaced.
+
+ Each tag must be a valid machine tag, see
+ machine-info5 for
+ the syntax. Invalid entries are ignored with a warning, and the remaining entries are still applied.
+
+
+
+
+ Configuration Directories and Precedence
+
+ Configuration files are read from directories in /etc/,
+ /run/, /usr/local/lib/, and /usr/lib/, in
+ order of precedence, as listed in the SYNOPSIS section above. Files must have the
+ .tags extension. Files in /etc/ override files with the same name
+ in /run/, /usr/local/lib/, and
+ /usr/lib/. Files in /run/ override files with the same name
+ under /usr/.
+
+ All configuration files are sorted by their filename in lexicographic order, regardless of which of
+ the directories they reside in, following the usual semantics of systemd configuration files. Empty
+ files and symlinks to /dev/null mask files in directories with lower priority.
+
+
+ Note that masking or removing a file only affects future applications of the configuration. Tags
+ that were applied earlier remain configured until they are removed explicitly, for example with a
+ - entry in another file, or with hostnamectl tags.
+
+
+
+ Examples
+
+ /usr/lib/machine-tags.d/20-vendor.tags example:
+
+ # Tags declared by the OS vendor
+vendor.role=webserver
+vendor.env=production
+vendor.region=eu
+
+
+
+ /etc/machine-tags.d/70-local.tags example:
+
+ # Drop the vendor's environment and region tags, but keep the role, and add a local tag
+-vendor.env=* -vendor.region=*
+local.rack=7
+
+
+ After hostnamectl tags --apply, the configured tags include
+ local.rack=7 and vendor.role=webserver, in addition to any tags
+ that were configured before.
+
+
+
+ See Also
+
+ systemd1
+ hostnamectl1
+ machine-info5
+ systemd-hostnamed.service8
+ systemd.unit5
+
+
+
+
diff --git a/man/rules/meson.build b/man/rules/meson.build
index c0869c4b5f3..ede758ec11c 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -50,6 +50,7 @@ manpages = [
['logind.conf', '5', ['logind.conf.d'], 'ENABLE_LOGIND'],
['machine-id', '5', [], ''],
['machine-info', '5', [], ''],
+ ['machine-tags.d', '5', [], 'ENABLE_HOSTNAMED'],
['machinectl', '1', [], 'ENABLE_MACHINED'],
['modules-load.d', '5', [], 'HAVE_KMOD'],
['networkctl', '1', [], 'ENABLE_NETWORKD'],
diff --git a/meson.build b/meson.build
index d3e24dc868e..e450a98e1bd 100644
--- a/meson.build
+++ b/meson.build
@@ -161,6 +161,7 @@ rpmmacrosdir = get_option('rpmmacrosdir')
if rpmmacrosdir != 'no'
rpmmacrosdir = prefixdir / rpmmacrosdir
endif
+machinetagsdir = prefixdir / 'lib/machine-tags.d'
modprobedir = prefixdir / 'lib/modprobe.d'
# Our own paths
@@ -276,6 +277,7 @@ conf.set_quoted('INCLUDE_DIR', includedir)
conf.set_quoted('LIBDIR', libdir)
conf.set_quoted('LIBEXECDIR', libexecdir)
conf.set_quoted('KERNEL_INSTALL_DIR', kernelinstalldir)
+conf.set_quoted('MACHINE_TAGS_DIR', machinetagsdir)
conf.set_quoted('MODPROBE_DIR', modprobedir)
conf.set_quoted('MODULESLOAD_DIR', modulesloaddir)
conf.set_quoted('PKGSYSCONFDIR', pkgsysconfdir)
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
index 862d57dcb6c..3fac1657a42 100644
--- a/src/core/systemd.pc.in
+++ b/src/core/systemd.pc.in
@@ -84,6 +84,8 @@ sysctldir=${sysctl_dir}
binfmt_dir=${prefix}/lib/binfmt.d
binfmtdir=${binfmt_dir}
+machine_tags_dir=${prefix}/lib/machine-tags.d
+
modules_load_dir=${prefix}/lib/modules-load.d
modulesloaddir=${modules_load_dir}
diff --git a/src/hostname/meson.build b/src/hostname/meson.build
index 8a8321f184c..9520df35e83 100644
--- a/src/hostname/meson.build
+++ b/src/hostname/meson.build
@@ -22,3 +22,8 @@ install_data('org.freedesktop.hostname1.service',
install_dir : dbussystemservicedir)
install_data('org.freedesktop.hostname1.policy',
install_dir : polkitpolicydir)
+
+install_emptydir(machinetagsdir)
+if install_sysconfdir
+ install_emptydir(sysconfdir / 'machine-tags.d')
+endif
diff --git a/src/rpm/macros.systemd.in b/src/rpm/macros.systemd.in
index b3d87b3d3a8..96eb7680926 100644
--- a/src/rpm/macros.systemd.in
+++ b/src/rpm/macros.systemd.in
@@ -21,6 +21,7 @@
%_user_tmpfilesdir {{USER_TMPFILES_DIR}}
%_environmentdir {{ENVIRONMENT_DIR}}
%_modulesloaddir {{MODULESLOAD_DIR}}
+%_machine_tags_dir {{MACHINE_TAGS_DIR}}
%_modprobedir {{MODPROBE_DIR}}
%_systemdgeneratordir {{SYSTEM_GENERATOR_DIR}}
%_systemdusergeneratordir {{USER_GENERATOR_DIR}}