diff --git a/manifests/nodes.pp b/manifests/nodes.pp
index 697efffc26acd3d6ae56efd1d65dc46a9491a33e..38d9b8571e29700598c5b366a9bea5c6072236ad 100644
--- a/manifests/nodes.pp
+++ b/manifests/nodes.pp
@@ -6,202 +6,102 @@ node /(chameleon|dragon|iguana|lizard|skink)\.tails\.net/ {
   include role::tails::physical
 }
 
-### Guests: *.lizard -----------------------------------------------------------
-
 node 'apt.lizard' {
-  include tails::profile::mounts
-  include tails::profile::msmtp
-  include tails::profile::reprepro
+  include role::tails::apt
 }
 
 node 'apt-proxy.lizard' {
-  include tails::profile::apt_cacher_ng
-  include tails::profile::mounts
-  include tails::profile::msmtp
+  include role::tails::apt_proxy
 }
 
 node 'bitcoin.lizard' {
-  include tails::profile::bitcoin
-  include tails::profile::mounts
-  include tails::profile::msmtp
+  include role::tails::bitcoin
 }
 
 node 'bittorrent.lizard' {
-  include tails::profile::bittorrent
-  include tails::profile::msmtp
+  include role::tails::bittorrent
 }
 
 node 'dns.lizard' {
-  include tails::profile::dns::primary
-  include tails::profile::msmtp
+  include role::tails::dns::primary
 }
 
 node /^isobuilder\d\.lizard$/ {
-  include tails::profile::jenkins::isobuilder_only
-  include tails::profile::mounts
-  include tails::profile::msmtp
+  include role::tails::jenkins::isobuilder
 }
 
 node 'jenkins.dragon' {
-  include tails::profile::jenkins::artifacts_store
-  include tails::profile::jenkins::master
-  include tails::profile::jenkins::reverse_proxy
-  include tails::profile::msmtp
+  include role::tails::jenkins::orchestrator
 }
 
 node 'mail.lizard' {
-  include tails::profile::autoreplies
-  include tails::profile::mailalias
-  include tails::profile::rspamd
-  include tails::profile::schleuder
+  include role::tails::mail
 }
 
 node 'misc.lizard' {
-  include tails::profile::check_gpg_monitoring
-  include tails::profile::check_mirrors
-  include tails::profile::jenkins::support::ssh
-  include tails::profile::jenkins::support::sftp
-  include tails::profile::mailalias
-  include tails::profile::msmtp
-  include tails::profile::release_misc
-  include tails::profile::rss2email
-  include tails::profile::tailsbot
+  include role::tails::misc
 }
 
 node 'puppet-git.lizard' {
-  include tails::profile::gitolite
-  include tails::profile::mounts
-  include tails::profile::msmtp
+  include role::tails::gitolite
 }
 
 node 'rsync.lizard' {
-  include tails::profile::mirrorbits
-  include tails::profile::mounts
-  include tails::profile::msmtp
-  include tails::profile::rsync
+  include role::tails::rsync
 }
 
 node 'translate.lizard' {
-  include tails::profile::mailalias
-  include tails::profile::msmtp
-  include tails::profile::weblate
-
-  # XXX workaround for sysadmin#17988, remove once node is upgraded to Bookworm
-  tails::profile::git::safe { "${weblate::params::weblate_data_dir}/vcs/tails/index": }
-  tails::profile::git::safe { "${weblate::params::weblate_repos_dir}/integration": }
-  tails::profile::git::safe { "${weblate::params::weblate_repos_dir}/staging": }
-
-  # XXX attempt of workaround for sysadmin#17925, maybe migrate to puppet-weblate
-  cron { 'Unlock weblate translations':
-    command => '/usr/bin/podman exec -t -i weblate weblate unlock_translation tails',
-    user    => 'weblate',
-    minute  => 0,
-    hour    => 1,
-  }
+  include role::tails::translate
 }
 
 node 'whisperback.lizard' {
-  include tails::profile::whisperback
-
-  # XXX Remove once enough time has passed after deployment of the new address
-  # below (see: sysadmin#18080)
-  tails::profile::whisperback::onion { 'tails_whisperback_relay':
-    public_key    => base64('decode', lookup('whisperback_old_public_key', String, 'first', undef)),
-    secret_key    => Sensitive(base64('decode', lookup('whisperback_old_secret_key', String, 'first', undef))),
-    onion_address => lookup('whisperback_old_onion_address', String, 'first', undef),
-  }
-
-  tails::profile::whisperback::onion { 'whisperback-relay':
-    public_key    => base64('decode', lookup('whisperback_public_key', String, 'first', undef)),
-    secret_key    => Sensitive(base64('decode', lookup('whisperback_secret_key', String, 'first', undef))),
-    onion_address => lookup('whisperback_onion_address', String, 'first', undef),
-  }
+  include role::tails::whisperback
 }
 
 node 'www.lizard' {
-  include tails::profile::fundraisingfeedback
-  include tails::profile::http_to_git_annex
-  include tails::profile::mailalias
-  include tails::profile::mirrorbits::reverse_proxy
-  include tails::profile::msmtp
-  include tails::profile::nginx
-  include tails::profile::nginx::exportcert
-  include tails::profile::redmine
-  include tails::profile::redmine::redirector
-  include tails::profile::reprepro::reverse_proxies
-  include tails::profile::weblate::reverse_proxy
-  include tails::profile::weblate::staging_reverse_proxy
-  include tails::profile::website
-}
-
-### Other systems managed by lizard's puppetmaster -----------------------------
+  include role::tails::www
+}
 
 node 'ecours.tails.net' {
-  include tails::profile::dropbear
-  include tails::profile::backupfs
-  include tails::profile::mta
-  include tails::profile::vpn
+  include role::tails::ecours
 }
 
 node 'gecko.tails.net' {
-  include tails::profile::backupfs
-  include tails::profile::dropbear
-  include tails::profile::hedgedoc
-  include tails::profile::msmtp
-  include tails::profile::tailsbot
-  include tails::profile::vpn
+  include role::tails::gecko
 }
 
 node 'teels.tails.net' {
-  include tails::profile::backupfs
-  include tails::profile::dns::secondary
-  include tails::profile::msmtp
-  include tails::profile::vpn
+  include role::tails::dns::secondary
 }
 
 node 'puppet.lizard' {
-  include tails::profile::msmtp
-  include tails::profile::sshkeymaster
-  include monitoring::plugins::puppetmaster
-}
-
-node 'gitlab-runner.iguana' {
-  include tails::profile::gitlab_runner
-  include tails::profile::msmtp
+  include role::tails::puppetserver
 }
 
-node 'gitlab-runner2.dragon' {
-  include tails::profile::gitlab_runner
-  include tails::profile::msmtp
+node /gitlab-runner[0-9]*\.iguana/ {
+  include role::tails::gitlab_runner
 }
 
 node 'proxy-dev.skink' {
-  include tails::profile::msmtp
-  include tails::profile::nginx
+  include role::tails::reverse_proxy
 }
 
 node /^isoworker\d+\.(dragon|iguana)$/ {
-  include tails::profile::jenkins::isoworker
-  include tails::profile::testermta
+  include role::tails::jenkins::isoworker
 }
 
 node 'isoworkers-mail.iguana' {
-  include tails::profile::jenkins::mail
+  include role::tails::jenkins::mail
 }
 
 node 'mta.chameleon' {
-  include tails::profile::autoreplies
-  include tails::profile::schleuder
-  include tails::profile::unbound
+  include role::tails::mta
 }
 
 node 'www2.chameleon' {
-  include tails::profile::msmtp
-  include tails::profile::mtasts
-  include tails::profile::nginx::exportcert
-  include tails::profile::website::mirror
+  include role::tails::www2
 }
 
 node 'testlab.skink' {
-  include tails::profile::msmtp
+  include role::tails::testlab
 }
diff --git a/site/role/manifests/tails/apt.pp b/site/role/manifests/tails/apt.pp
new file mode 100644
index 0000000000000000000000000000000000000000..a94486857a9beb8d3e63a29561eb146907ce2bf1
--- /dev/null
+++ b/site/role/manifests/tails/apt.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for apt
+class role::tails::apt {
+  include tails::profile::mounts
+  include tails::profile::msmtp
+  include tails::profile::reprepro
+}
+
diff --git a/site/role/manifests/tails/apt_proxy.pp b/site/role/manifests/tails/apt_proxy.pp
new file mode 100644
index 0000000000000000000000000000000000000000..5ec206852437645e5007ebb3bcb4b21a6e7e31c6
--- /dev/null
+++ b/site/role/manifests/tails/apt_proxy.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for apt-proxy
+class role::tails::apt_proxy {
+  include tails::profile::apt_cacher_ng
+  include tails::profile::mounts
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/bitcoin.pp b/site/role/manifests/tails/bitcoin.pp
new file mode 100644
index 0000000000000000000000000000000000000000..6d142bb04b5c6c5f67d576d6bb7c25818a047ded
--- /dev/null
+++ b/site/role/manifests/tails/bitcoin.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for bitcoin
+class role::tails::bitcoin {
+  include tails::profile::bitcoin
+  include tails::profile::mounts
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/bittorrent.pp b/site/role/manifests/tails/bittorrent.pp
new file mode 100644
index 0000000000000000000000000000000000000000..de92ee0dc85b0b3762eeac18d50958f03803192c
--- /dev/null
+++ b/site/role/manifests/tails/bittorrent.pp
@@ -0,0 +1,7 @@
+# @summary
+#   A role for bittorrent
+class role::tails::bittorrent {
+  include tails::profile::bittorrent
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/dns/primary.pp b/site/role/manifests/tails/dns/primary.pp
new file mode 100644
index 0000000000000000000000000000000000000000..2290ba2b8d8fc0c1b292ce329f39e61b28955b94
--- /dev/null
+++ b/site/role/manifests/tails/dns/primary.pp
@@ -0,0 +1,7 @@
+# @summary
+#   A role for primary DNS
+class role::tails::dns::primary {
+  include tails::profile::dns::primary
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/dns/secondary.pp b/site/role/manifests/tails/dns/secondary.pp
new file mode 100644
index 0000000000000000000000000000000000000000..a91567a035c2c5da20915272b897310220d68bd5
--- /dev/null
+++ b/site/role/manifests/tails/dns/secondary.pp
@@ -0,0 +1,9 @@
+# @summary
+#   A role for secondary DNS
+class role::tails::dns::secondary {
+  include tails::profile::backupfs
+  include tails::profile::dns::secondary
+  include tails::profile::msmtp
+  include tails::profile::vpn
+}
+
diff --git a/site/role/manifests/tails/ecours.pp b/site/role/manifests/tails/ecours.pp
new file mode 100644
index 0000000000000000000000000000000000000000..5266a08c5879c81959afcbf321b8f69a07e54fbe
--- /dev/null
+++ b/site/role/manifests/tails/ecours.pp
@@ -0,0 +1,9 @@
+# @summary
+#   A role for ecours
+class role::tails::ecours {
+  include tails::profile::dropbear
+  include tails::profile::backupfs
+  include tails::profile::mta
+  include tails::profile::vpn
+}
+
diff --git a/site/role/manifests/tails/gecko.pp b/site/role/manifests/tails/gecko.pp
new file mode 100644
index 0000000000000000000000000000000000000000..8dce3db7dcdd828aba7fa7a066d63c688caad1be
--- /dev/null
+++ b/site/role/manifests/tails/gecko.pp
@@ -0,0 +1,11 @@
+# @summary
+#   A role for gecko
+class role::tails::gecko {
+  include tails::profile::backupfs
+  include tails::profile::dropbear
+  include tails::profile::hedgedoc
+  include tails::profile::msmtp
+  include tails::profile::tailsbot
+  include tails::profile::vpn
+}
+
diff --git a/site/role/manifests/tails/gitlab_runner.pp b/site/role/manifests/tails/gitlab_runner.pp
new file mode 100644
index 0000000000000000000000000000000000000000..a3966e31a55fc383e91b47cdcd649e3476763722
--- /dev/null
+++ b/site/role/manifests/tails/gitlab_runner.pp
@@ -0,0 +1,7 @@
+# @summary
+#   A role for gitlab-runners
+class role::tails::gitlab_runner {
+  include tails::profile::gitlab_runner
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/gitolite.pp b/site/role/manifests/tails/gitolite.pp
new file mode 100644
index 0000000000000000000000000000000000000000..53a24dbb7ab87297305f42b5833192b14d9e9bb4
--- /dev/null
+++ b/site/role/manifests/tails/gitolite.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for gitolite
+class role::tails::gitolite {
+  include tails::profile::gitolite
+  include tails::profile::mounts
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/jenkins/isobuilder.pp b/site/role/manifests/tails/jenkins/isobuilder.pp
new file mode 100644
index 0000000000000000000000000000000000000000..13324d934b9bbdc78b5112ea9280dc35eaf153ca
--- /dev/null
+++ b/site/role/manifests/tails/jenkins/isobuilder.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for jenkins isobuilders
+class role::tails::jenkins::isobuilder {
+  include tails::profile::jenkins::isobuilder_only
+  include tails::profile::mounts
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/jenkins/isoworker.pp b/site/role/manifests/tails/jenkins/isoworker.pp
new file mode 100644
index 0000000000000000000000000000000000000000..3be40b56efc4506a11e9a87186a9f342d24e6e09
--- /dev/null
+++ b/site/role/manifests/tails/jenkins/isoworker.pp
@@ -0,0 +1,7 @@
+# @summary
+#   A role for jenkins isoworkers
+class role::tails::jenkins::isoworker {
+  include tails::profile::jenkins::isoworker
+  include tails::profile::testermta
+}
+
diff --git a/site/role/manifests/tails/jenkins/mail.pp b/site/role/manifests/tails/jenkins/mail.pp
new file mode 100644
index 0000000000000000000000000000000000000000..7d64c92bd45522f481c32baec21df0d1f2d43d9a
--- /dev/null
+++ b/site/role/manifests/tails/jenkins/mail.pp
@@ -0,0 +1,6 @@
+# @summary
+#   A role for jenkins mailing tests
+class role::tails::jenkins::mail {
+  include tails::profile::jenkins::mail
+}
+
diff --git a/site/role/manifests/tails/jenkins/orchestrator.pp b/site/role/manifests/tails/jenkins/orchestrator.pp
new file mode 100644
index 0000000000000000000000000000000000000000..a204df1e1bb8b48d52fcf543495ea297d178e5cd
--- /dev/null
+++ b/site/role/manifests/tails/jenkins/orchestrator.pp
@@ -0,0 +1,9 @@
+# @summary
+#   A role for jenkins orchestrator
+class role::tails::jenkins::orchestrator {
+  include tails::profile::jenkins::artifacts_store
+  include tails::profile::jenkins::master
+  include tails::profile::jenkins::reverse_proxy
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/mail.pp b/site/role/manifests/tails/mail.pp
new file mode 100644
index 0000000000000000000000000000000000000000..08caf6aa80d8e5bbb64ea4bacf73c929bb676141
--- /dev/null
+++ b/site/role/manifests/tails/mail.pp
@@ -0,0 +1,9 @@
+# @summary
+#   A role for mail
+class role::tails::mail {
+  include tails::profile::autoreplies
+  include tails::profile::mailalias
+  include tails::profile::rspamd
+  include tails::profile::schleuder
+}
+
diff --git a/site/role/manifests/tails/misc.pp b/site/role/manifests/tails/misc.pp
new file mode 100644
index 0000000000000000000000000000000000000000..c89162d7cf6fb78fdc9d085829403b9f5b36f1ab
--- /dev/null
+++ b/site/role/manifests/tails/misc.pp
@@ -0,0 +1,14 @@
+# @summary
+#   A role for misc
+class role::tails::misc {
+  include tails::profile::check_gpg_monitoring
+  include tails::profile::check_mirrors
+  include tails::profile::jenkins::support::ssh
+  include tails::profile::jenkins::support::sftp
+  include tails::profile::mailalias
+  include tails::profile::msmtp
+  include tails::profile::release_misc
+  include tails::profile::rss2email
+  include tails::profile::tailsbot
+}
+
diff --git a/site/role/manifests/tails/mta.pp b/site/role/manifests/tails/mta.pp
new file mode 100644
index 0000000000000000000000000000000000000000..b437209c500e424ecc341004ec70d31fe86f8568
--- /dev/null
+++ b/site/role/manifests/tails/mta.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for mta
+class role::tails::mta {
+  include tails::profile::autoreplies
+  include tails::profile::schleuder
+  include tails::profile::unbound
+}
+
diff --git a/site/role/manifests/tails/puppetserver.pp b/site/role/manifests/tails/puppetserver.pp
new file mode 100644
index 0000000000000000000000000000000000000000..c962b2730c0abc9364044a51485770b58422965c
--- /dev/null
+++ b/site/role/manifests/tails/puppetserver.pp
@@ -0,0 +1,8 @@
+# @summary
+#   A role for puppet
+class role::tails::puppetserver {
+  include tails::profile::msmtp
+  include tails::profile::sshkeymaster
+  include monitoring::plugins::puppetmaster
+}
+
diff --git a/site/role/manifests/tails/reverse_proxy.pp b/site/role/manifests/tails/reverse_proxy.pp
new file mode 100644
index 0000000000000000000000000000000000000000..60fb65d7c5b66245bd4d7a37f0124233de8c1b5d
--- /dev/null
+++ b/site/role/manifests/tails/reverse_proxy.pp
@@ -0,0 +1,7 @@
+# @summary
+#   A role for reverse proxies
+class role::tails::reverse_proxy {
+  include tails::profile::msmtp
+  include tails::profile::nginx
+}
+
diff --git a/site/role/manifests/tails/rsync.pp b/site/role/manifests/tails/rsync.pp
new file mode 100644
index 0000000000000000000000000000000000000000..c85259de44c998e0b272d79c6123fa5050e3ce26
--- /dev/null
+++ b/site/role/manifests/tails/rsync.pp
@@ -0,0 +1,9 @@
+# @summary
+#   A role for rsync
+class role::tails::rsync {
+  include tails::profile::mirrorbits
+  include tails::profile::mounts
+  include tails::profile::msmtp
+  include tails::profile::rsync
+}
+
diff --git a/site/role/manifests/tails/testlab.pp b/site/role/manifests/tails/testlab.pp
new file mode 100644
index 0000000000000000000000000000000000000000..0c12a13aac49482b89d3005526e960e4fc160836
--- /dev/null
+++ b/site/role/manifests/tails/testlab.pp
@@ -0,0 +1,6 @@
+# @summary
+#   A role for testlab
+class role::tails::testlab {
+  include tails::profile::msmtp
+}
+
diff --git a/site/role/manifests/tails/translate.pp b/site/role/manifests/tails/translate.pp
new file mode 100644
index 0000000000000000000000000000000000000000..1870bcbdd56c7a9986f944e1af7ea2cbb2974d14
--- /dev/null
+++ b/site/role/manifests/tails/translate.pp
@@ -0,0 +1,21 @@
+# @summary
+#   A role for translate
+class role::tails::translate {
+  include tails::profile::mailalias
+  include tails::profile::msmtp
+  include tails::profile::weblate
+
+  # XXX workaround for sysadmin#17988, remove once node is upgraded to Bookworm
+  tails::profile::git::safe { "${weblate::params::weblate_data_dir}/vcs/tails/index": }
+  tails::profile::git::safe { "${weblate::params::weblate_repos_dir}/integration": }
+  tails::profile::git::safe { "${weblate::params::weblate_repos_dir}/staging": }
+
+  # XXX attempt of workaround for sysadmin#17925, maybe migrate to puppet-weblate
+  cron { 'Unlock weblate translations':
+    command => '/usr/bin/podman exec -t -i weblate weblate unlock_translation tails',
+    user    => 'weblate',
+    minute  => 0,
+    hour    => 1,
+  }
+}
+
diff --git a/site/role/manifests/tails/whisperback.pp b/site/role/manifests/tails/whisperback.pp
new file mode 100644
index 0000000000000000000000000000000000000000..55489ea3c637ef5ffc80269025a60813157358c6
--- /dev/null
+++ b/site/role/manifests/tails/whisperback.pp
@@ -0,0 +1,20 @@
+# @summary
+#   A role for whisperback
+class role::tails::whisperback {
+  include tails::profile::whisperback
+
+  # XXX Remove once enough time has passed after deployment of the new address
+  # below (see: sysadmin#18080)
+  tails::profile::whisperback::onion { 'tails_whisperback_relay':
+    public_key    => base64('decode', lookup('whisperback_old_public_key', String, 'first', undef)),
+    secret_key    => Sensitive(base64('decode', lookup('whisperback_old_secret_key', String, 'first', undef))),
+    onion_address => lookup('whisperback_old_onion_address', String, 'first', undef),
+  }
+
+  tails::profile::whisperback::onion { 'whisperback-relay':
+    public_key    => base64('decode', lookup('whisperback_public_key', String, 'first', undef)),
+    secret_key    => Sensitive(base64('decode', lookup('whisperback_secret_key', String, 'first', undef))),
+    onion_address => lookup('whisperback_onion_address', String, 'first', undef),
+  }
+}
+
diff --git a/site/role/manifests/tails/www.pp b/site/role/manifests/tails/www.pp
new file mode 100644
index 0000000000000000000000000000000000000000..a9ec82444263f5a7ecdb3d3f64f0de2d539c6fd0
--- /dev/null
+++ b/site/role/manifests/tails/www.pp
@@ -0,0 +1,18 @@
+# @summary
+#   A role for www
+class role::tails::www {
+  include tails::profile::fundraisingfeedback
+  include tails::profile::http_to_git_annex
+  include tails::profile::mailalias
+  include tails::profile::mirrorbits::reverse_proxy
+  include tails::profile::msmtp
+  include tails::profile::nginx
+  include tails::profile::nginx::exportcert
+  include tails::profile::redmine
+  include tails::profile::redmine::redirector
+  include tails::profile::reprepro::reverse_proxies
+  include tails::profile::weblate::reverse_proxy
+  include tails::profile::weblate::staging_reverse_proxy
+  include tails::profile::website
+}
+
diff --git a/site/role/manifests/tails/www2.pp b/site/role/manifests/tails/www2.pp
new file mode 100644
index 0000000000000000000000000000000000000000..6470fa45cac66043927a54782bbbb31eb4b05c67
--- /dev/null
+++ b/site/role/manifests/tails/www2.pp
@@ -0,0 +1,9 @@
+# @summary
+#   A role for www2
+class role::tails::www2 {
+  include tails::profile::msmtp
+  include tails::profile::mtasts
+  include tails::profile::nginx::exportcert
+  include tails::profile::website::mirror
+}
+