When working with systemd instantiated services in Puppet, many administrators encounter difficulties with the enablement process. The traditional approach:
service { "getty@ttyUSB0.service":
provider => systemd,
ensure => running,
enable => true,
}
fails because Puppet's default systemd provider doesn't properly handle template unit instantiation. The error occurs because systemd requires special handling for template-based services.
The manual solution you discovered:
ln -s /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@ttyUSB0.service
works because it follows systemd's convention for enabling instantiated services. Systemd expects template services to be enabled by creating symlinks in the appropriate .wants directory with the full instantiated unit name.
Here's the correct way to implement this in Puppet using the systemd::unit_file
resource from the puppet-systemd module:
# First ensure the module is available
include systemd
# Create the instantiated service file
systemd::unit_file { 'getty@ttyUSB0.service':
ensure => 'present',
source => "puppet:///modules/profile/getty@ttyUSB0.service",
enable => true,
active => true,
}
Alternatively, if you're not using the systemd module, you can implement it with native Puppet resources:
file { '/etc/systemd/system/getty.target.wants/getty@ttyUSB0.service':
ensure => 'link',
target => '/lib/systemd/system/getty@.service',
notify => Exec['systemctl daemon-reload'],
}
exec { 'systemctl daemon-reload':
refreshonly => true,
path => '/bin:/usr/bin:/sbin:/usr/sbin',
}
For production environments, consider these enhancements:
# Dynamic instantiation for multiple TTYs
['ttyUSB0', 'ttyUSB1', 'ttyACM0'].each |$tty| {
file { "/etc/systemd/system/getty.target.wants/getty@${tty}.service":
ensure => 'link',
target => '/lib/systemd/system/getty@.service',
}
->
service { "getty@${tty}.service":
ensure => 'running',
provider => 'systemd',
require => File["/etc/systemd/system/getty.target.wants/getty@${tty}.service"],
}
}
After implementation, verify with:
systemctl is-enabled getty@ttyUSB0.service
systemctl is-active getty@ttyUSB0.service
Common issues to check:
- Ensure systemd has reloaded its configuration
- Verify the symlink points to the correct target
- Check journalctl for service-specific errors
When working with systemd instantiated services (template units) in Puppet, many administrators encounter a common pitfall. The standard Puppet service resource doesn't fully understand systemd's instantiation syntax. Here's a typical broken configuration:
service { "getty@ttyUSB0.service":
provider => systemd,
ensure => running,
enable => true,
}
This fails because Puppet attempts to enable the literal string "getty@ttyUSB0.service" rather than understanding it as an instance of getty@.service.
The manual approach you mentioned works because it follows systemd's native instantiation pattern:
ln -s /lib/systemd/system/getty@.service /etc/systemd/system/getty.target.wants/getty@ttyUSB0.service
Systemd expects template services to be enabled this way - by creating symlinks that reference the template but use the full instance name in the symlink path.
For Puppet 6.0+ with systemd provider improvements:
systemd::unit_file { 'getty@ttyUSB0.service':
ensure => 'present',
enable => true,
active => true,
content => epp('profile/getty@.service.epp', { 'tty' => 'ttyUSB0' }),
}
For older Puppet versions, use the exec resource as a workaround:
exec { 'enable-getty-instance':
command => '/bin/systemctl enable getty@ttyUSB0.service',
unless => '/bin/systemctl is-enabled getty@ttyUSB0.service',
}
For production environments, consider creating a defined type:
define profile::getty_instance (
String $tty,
Boolean $enable = true,
) {
file { "/etc/systemd/system/getty.target.wants/getty@${tty}.service":
ensure => 'link',
target => '/lib/systemd/system/getty@.service',
}
service { "getty@${tty}.service":
ensure => running,
enable => $enable,
provider => 'systemd',
require => File["/etc/systemd/system/getty.target.wants/getty@${tty}.service"],
subscribe => File['/lib/systemd/system/getty@.service'],
}
}
After implementation, verify with:
puppet resource service "getty@ttyUSB0.service"
systemctl list-unit-files | grep getty@
journalctl -u "getty@ttyUSB0.service"
Common issues to check:
- Template file permissions in /lib/systemd/system/
- systemd daemon-reload requirements
- TTY device existence before service start