diff --git a/manifests/unit_options.pp b/manifests/unit_options.pp
new file mode 100644
index 0000000000000000000000000000000000000000..e933e0c6744a7f55ad59d20272c6ee00a58aa516
--- /dev/null
+++ b/manifests/unit_options.pp
@@ -0,0 +1,104 @@
+# Copyright © 2021   Thomas Bellman, Linköping, Sweden
+# Licensed under the GNU LGPL v3+; see the README file for more information.
+
+
+/*
+ * Manage systemd options for a unit.
+ *
+ * This actually manages a config file in /etc/systemd/system/<service>.d.
+ * The name parameter must be on the format
+ *
+ *     UNITNAME "." TYPE "/" FILENAME
+ *
+ * where
+ *
+ *  - UNITNAME is the name of the service, e.g. "sshd" or "getty@".
+ *  - TYPE is the unit type, e.g. "service", "socket", et.c; see
+ *    systemd.unit(5).
+ *  - FILENAME is the basename of the file, without the ".conf" suffix,
+ *    where the options are stored.
+ *
+ * Note that the ".d" suffix of the service directory will be added
+ * automatically, as will the ".conf" suffix on the file name.  The
+ * name parameter should thus be e.g. "getty@.service/foo", not
+ * "getty@.service.d/foo.conf".
+ *
+ * Systemd will automatically be told to reload the configuration
+ * files, by notifying the class systemd::daemon_reload, so users
+ * don't need to do that themselves.
+ *
+ * This definition cannot be used to manage "wants" of systemd targets.
+ */
+define systemd::unit_options(
+	# Hash of hash of options.  The keys in the top level hash are
+	# the sections in the config file, e.g. "Unit", "Service", "Socket".
+	# The inner hashes are mappings from option names to values.
+	#
+	# If the value of an option is a (possibly nested) list, one option
+	# line will be generated for each element in the list.  This is to
+	# support multi-value options, e.g. "ExecStart" in a service config,
+	# or "ListenStream" in a socket config.
+	#
+	# If the option name starts with a minus sign ("-"), an extra line
+	# setting that option to the empty string is prepended, in order to
+	# reset that option to the empty list, as systemd would otherwise
+	# just append the new value(s) to the existing list.
+	#
+	$options,
+
+	# List of comment lines to add to the start of the parameter file.
+	# The list may be nested, and individual "lines" are allowed to
+	# contain newlines, turning them into multiple comment lines.
+	#
+	$comment=[],
+
+	# One of 'present' of 'absent'.
+	#
+	$ensure='present',
+)
+{
+    include systemd::daemon_reload
+
+    if ($name =~ /^([^\/.]+)\.([^\/.]+)\/(.+)$/) {
+	$unitname = $1
+	$unittype = $2
+	$filename = $3
+    } else {
+	fail("Systemd::Unit_options[${title}]:",
+	     " Name not on format <service>.<type>/<option_filename>,",
+	     " ``${name}''")
+    }
+
+    $etcdir = '/etc/systemd/system'
+    $unitdir = "${etcdir}/${unitname}.${unittype}.d"
+    $filepath = "${unitdir}/${filename}.conf"
+
+    case $ensure
+    {
+	'present': {
+	    cfgfile::directory_parents {
+		$filepath:
+		    top => $etcdir,
+		    owner => 'root', group => 'root', mode => '0755';
+	    }
+	    file {
+		$filepath:
+		    ensure => file,
+		    content => template('systemd/unit_options.conf.erb'),
+		    owner => 'root', group => 'root', mode => '0444',
+		    notify => Class['systemd::daemon_reload'];
+	    }
+	}
+	'absent': {
+	    file {
+		$filepath:
+		    ensure => absent,
+		    notify => Class['systemd::daemon_reload'];
+	    }
+	}
+	'default': {
+	    fail("Systemd::Unit_options[${title}]:",
+		 " Bad parameter ensure, ``${ensure}''")
+	}
+    }
+}
diff --git a/templates/unit_options.conf.erb b/templates/unit_options.conf.erb
new file mode 100644
index 0000000000000000000000000000000000000000..8cdf01b84d58c9241731233af8e9ea5aae26ac52
--- /dev/null
+++ b/templates/unit_options.conf.erb
@@ -0,0 +1,25 @@
+<%
+   # Copyright © 2021   Thomas Bellman, Linköping, Sweden
+   # Licensed under the GNU LGPL v3+; see the README file for more information.
+-%>
+<% comment_lines = [@comment].flatten.join("\n").split("\n")
+   if comment_lines.length() > 0
+      comment_lines.each do |cline|
+-%>
+# <%= cline %>
+<%    end %>
+<% end -%>
+<% @options.sort.each do |section_name,section_options| -%>
+[<%= section_name %>]
+<%    section_options.sort.each do |optname,value| -%>
+<%       if optname =~ /^-(.*)/
+            optname = $1
+-%>
+<%= optname %>=
+<%       end -%>
+<%       [value].flatten.each do |v| -%>
+<%= optname %>=<%= v %>
+<%       end -%>
+<%    end %>
+<% end -%>
+# Managed by Puppet:  Systemd::Unit_options[<%= @title %>]