diff --git a/manifests/tmpfiles.pp b/manifests/tmpfiles.pp
new file mode 100644
index 0000000000000000000000000000000000000000..c5eacb7337dfe1fa079379188691d7a9b2c71563
--- /dev/null
+++ b/manifests/tmpfiles.pp
@@ -0,0 +1,109 @@
+# Copyright © 2024        Thomas Bellman, Linköping, Sweden
+# Licensed under the GNU LGPL v3+; see the README file for more information.
+
+
+/*
+ * Manage a systemd-tmpfiles(8) config file.
+ *
+ * You want to read the tmpfiles.d(5) manual page to understand what most
+ * of the parameters actually do in the end.
+ *
+ * systemd-tmpfiles is automatically run as part of managing the tmpfiles.d
+ * config file, so e.g. any directory the config file says should be created,
+ * will be.
+ *
+ * Note that there is basically no verification that the parameter values
+ * are syntactically correct, so it is possible to create config files
+ * that are not valid.
+ */
+define systemd::tmpfiles(
+	# Path(s) to manage.  Can be a single path, or a (possibly nested)
+	# list of paths.  If more than one path is given, the same type,
+	# permissions, and maxage is applied to them all.
+	#
+	# If specified, then at least $type must also be specified.
+	#
+	$paths = [],
+
+	# The entry type for the paths in $paths.  All path entries will
+	# share the same type.
+	#
+	$type = undef,
+
+	# The mode, uid, gid, age, and argument fields, respectively, of the
+	# tmpfiles.d config entries for $paths.  All entries will share the
+	# same settings.
+	#
+	$mode = undef,
+	$owner = undef,
+	$group = undef,
+	$maxage = undef,
+	$argument = undef,
+
+	# Alternative to specifying paths to manage using parameters above.
+	# One or more lines that will be written as-is to the tmpfiles.d
+	# file.
+	# While it says "lines", this can actually be any string, if you
+	# e.g. a template generating several lines as a single string.
+	#
+	$content = [],
+
+	# Comments to prepend to the generated tmpfiles.d config file.
+	#
+	$comment = [],
+
+	# One of 'present' or 'absent'.
+	#
+	$ensure = 'present',
+)
+{
+    $resource_ref = "Systemd::Tmpfiles[${title}]"
+
+    case $ensure
+    {
+	'present': {
+	    if (($paths != []) and ((! $type) or ($type == ""))) {
+		fail("${resource_ref}: paths parameter set, but not type")
+	    }
+	    if (($type) and ($paths == [])) {
+		fail("${resource_ref}: type parameter set, but no paths")
+	    }
+	    if (($paths == []) and ($content == '' or $content == [])) {
+		fail("${resource_ref}: both paths and content are empty")
+	    }
+	    contain systemd::tmpfiles::trigger
+
+	    $cfgfile_content = template('systemd/tmpfiles.conf.erb')
+	    file {
+		"/etc/tmpfiles.d/${name}.conf":
+		    ensure => file,
+		    content => $cfgfile_content,
+		    owner => 'root', group => 'root', mode => '0644',
+		    notify => Exec['systemd::tmpfiles::trigger'];
+	    }
+	}
+
+	'absent': {
+	    file {
+		"/etc/tmpfiles.d/${name}.conf":
+		    ensure => absent;
+	    }
+	}
+
+	default: {
+	    fail("${resource_ref}: Bad ensure parameter, ${ensure}")
+	}
+    }
+}
+
+
+class systemd::tmpfiles::trigger
+{
+    exec {
+	'systemd::tmpfiles::trigger':
+	    command => shellquote(
+		'systemd-tmpfiles', '--create', '--remove', '--clean'),
+	    path => [ '/bin', '/usr/bin', '/sbin', '/usr/sbin', ],
+	    refreshonly => true;
+    }
+}
diff --git a/templates/tmpfiles.conf.erb b/templates/tmpfiles.conf.erb
new file mode 100644
index 0000000000000000000000000000000000000000..9d2a8959a320d402c376f7d71034fa1d0c1b50bd
--- /dev/null
+++ b/templates/tmpfiles.conf.erb
@@ -0,0 +1,25 @@
+<% # Copyright © 2024        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")
+    comment_lines.each do |line| -%>
+<%=	'# ' + line %>
+<%  end -%>
+<%  if not comment_lines.empty? -%>
+<%	%>
+<%  end -%>
+<%  [@paths].flatten.each do |path|
+	fields = [ path ]
+	fields << (@mode || '-')
+	fields << (@owner || '-')
+	fields << (@group || '-')
+	fields << (@maxage || '-')
+	fields << (@argument || '')
+-%>
+<%=	sprintf('%-2s %s', @type, fields.join("\t")).rstrip() %>
+<%  end -%>
+<%  [@content].flatten.each do |chunk| -%>
+<%=	chunk %>
+<%  end -%>
+<%  %>
+# Managed by Puppet:  <%= @resource_ref %>