Forge Home


Centralized SSL certificate management using Let's Encrypt™ and


1,186 latest version

3.8 quality score

We run a couple of automated
scans to help you access a
module's quality. Each module is
given a score based on how well
the author has formatted their
code and documentation and
modules are also checked for
malware using VirusTotal.

Please note, the information below
is for guidance only and neither of
these methods should be considered
an endorsement by Puppet.

Version information

  • 3.0.0 (latest)
  • 2.3.0
  • 2.2.0
  • 2.1.0
  • 2.0.0
  • 1.0.5
  • 1.0.4
  • 1.0.3
  • 1.0.2
  • 1.0.1
  • 1.0.0
released Apr 16th 2017
This version is compatible with:
  • Puppet Enterprise 4.x
  • Puppet >= 4.0.0 < 5.0.0
  • Ubuntu, Debian, RedHat, CentOS

Start using this module

  • r10k or Code Manager
  • Bolt
  • Manual installation
  • Direct download

Add this module to your Puppetfile:

mod 'fraenki-acme', '1.0.0'
Learn more about managing modules with a Puppetfile

Add this module to your Bolt project:

bolt module add fraenki-acme
Learn more about using this module with an existing project

Manually install this module globally with Puppet module tool:

puppet module install fraenki-acme --version 1.0.0

Direct download is not typically how you would use a Puppet module to manage your infrastructure, but you may want to download the module in order to inspect the code.



fraenki/acme — version 1.0.0 Apr 16th 2017

Table of Contents

  1. Overview
  2. Requirements
  3. Module Description
  4. Setup
  5. Usage
  6. Examples
  7. Reference
  8. Limitations
  9. Development
  10. Fork
  11. License


Centralized SSL certificate management using Let's Encrypt™. Keep your private keys safe on the host they belong to and let your Puppet Server sign the CSRs and distribute the certificates.


Furthermore you're advised to use hiera-eyaml to protect sensitive information (such as DNS API secrets).

Module Description

This module creates private keys and CSRs, transfers the CSR to your Puppet Server where it is signed using the popular and lightweight Neilpang/

Signed certificates are shipped back to the appropriate host.

You just need to specify the required challenge configuration on your Puppet Server. All DNS-01 hooks that are supported by will work immediately.


Configure your Puppet Server

The whole idea is centralized certificate management, thus you have to add some configure on your Puppet Server.

First configure the Let's Encrypt accounts that are available to issue certificates:

    Class { 'acme':
      accounts => ['', '']

Next add configuration for the challenge types you want to use, we call each configuration a "profile":

    Class { 'acme':
      accounts => ['', '']
      profiles => {
        nsupdate_example => {
          challengetype => 'dns-01',
          hook => 'nsupdate',
          env => {
            NSUPDATE_SERVER => ''
          options => {
            dnssleep => 15,
            nsupdate_id => 'example-key',
            nsupdate_type => 'hmac-md5',
            nsupdate_key => 'abcdefg1234567890',
        route53_example => {
          challengetype => 'dns-01',
          hook => 'aws',
          env => {
            AWS_ACCESS_KEY_ID => 'foobar',
            AWS_SECRET_ACCESS_KEY => 'secret',
          options => {
            dnssleep => 15,

In this example we create two "profiles": One is utilizing the "nsupdate" hook to communicate with a BIND DNS server and the other one uses the "aws" hook to communicate with Amazon Route53.

Note that the hook parameter must exactly match the name of the hook that is used by Neilpang/ Some DNS hooks require special environment variables, simply add them to the env parameter.

All CSRs are collected and signed on your Puppet Server, and the resulting certificates and CA chain files are shipped back to your nodes.


Request a certificate

On the Puppet node where you need the certificate(s):

    class { 'acme':
      certificates => {
        # issue a test certificate => {
          use_profile    => 'route53_example',
          use_account    => '',
          letsencrypt_ca => 'staging',
        # a cert for the current FQDN is nice too
        ${::fqdn} => {
          use_profile    => 'nsupdate_example',
          use_account    => '',
          letsencrypt_ca => 'production',

Note: The use_profile and use_account parameters must match the profiles and accounts that you've previously configured on your Puppet Server. Otherwise the module will refuse to issue the certificate.

The private key and CSR will be generated on your node and the CSR is shipped to your Puppet Server for signing. The certificate is put on your node after some time.

Instead of specifying the domains as parameter to the acme class, it is possible to call the acme::certificate define directly:

    ::acme::certificate { '':
      use_profile    => 'route53_example',
      use_account    => '',
      letsencrypt_ca => 'staging',

SAN certificates

Requesting SAN certificates is easy too. To do so pass a space seperated list of domain names in the certificates hash. The first domain name in each list is used as the base domain for the request. For example:

    class { 'acme':
      certificates => {
        '' => {
          use_profile    => 'route53_example',
          use_account    => '',
          letsencrypt_ca => 'staging',

Or use the define directly:

    ::acme::certificate { '':
      use_profile    => 'route53_example',
      use_account    => '',
      letsencrypt_ca => 'staging',

In both examples "" will be used as base domain for the CSR.

Testing and Debugging

For testing purposes you should use the Let's Encrypt staging CA, otherwise you'll hit rate limits pretty soon. It's possible to set the default CA on your Puppet Server by using the letsencrypt_ca parameter:

    class { 'acme' :
      letsencrypt_ca => 'staging'

Or you can use this parameter directly when configuring certificates on your Puppet nodes, as shown by the previous examples.



Using acme in combination with puppetlabs-apache:

    # request a certificate from acme
    ::acme::certificate { $::fqdn:
      use_profile    => 'nsupdate_example',
      use_account    => '',
      letsencrypt_ca => 'production',
      # restart apache when the certificate is signed (or renewed)
      notify         => Class['::apache::service'],

    # get configuration from acme::params
    include ::acme::params
    $base_dir = $::acme::params::base_dir
    $crt_dir  = $::acme::params::crt_dir
    $key_dir  = $::acme::params::key_dir

    # where acme stores our certificate and key files
    $my_key = "${key_dir}/${::fqdn}/private.key"
    $my_cert = "${crt_dir}/${::fqdn}/cert.pem"
    $my_chain = "${crt_dir}/${::fqdn}/chain.pem"

    # configure apache
    include apache
    apache::vhost { $::fqdn:
      port     => '443',
      docroot  => '/var/www/example',
      ssl      => true,
      ssl_cert => "/etc/ssl/${::fqdn}.crt",
      ssl_ca   => "/etc/ssl/${::fqdn}.ca",
      ssl_key  => "/etc/ssl/${::fqdn}.key",

    # copy certificate files
    file { "/etc/ssl/${::fqdn}.crt":
      ensure    => file,
      owner     => 'root',
      group     => 'root',
      mode      => '0644',
      source    => $my_cert,
      subscribe => Acme::Certificate[$::fqdn],
      notify    => Class['::apache::service'],
    file { "/etc/ssl/${::fqdn}.key":
      ensure    => file,
      owner     => 'root',
      group     => 'root',
      mode      => '0640',
      source    => $my_key,
      subscribe => Acme::Certificate[$::fqdn],
      notify    => Class['::apache::service'],
    file { "/etc/ssl/${::fqdn}.ca":
      ensure    => file,
      owner     => 'root',
      group     => 'root',
      mode      => '0644',
      source    => $my_chain,
      subscribe => Acme::Certificate[$::fqdn],
      notify    => Class['::apache::service'],


Files and directories

Locations of the important certificate files (i.e. ""):

  • /etc/ the certificate
  • /etc/ the CA chain
  • /etc/ Certificate and CA chain in one file
  • /etc/ OCSP Must-Staple extension data
  • /etc/ Private key
  • /etc/ All-in-one: certificate, CA chain, private key

Basic directory layout:

  • /etc/ (Puppet Server) Private keys and other files related to Let's Encrypt accounts
  • /etc/ Certificates, CA chains and OCSP files
  • /etc/ OpenSSL configuration and other files required for the CSR
  • /etc/ Certificate signing requests (CSR)
  • /etc/ (Puppet Server) Working directory for
  • /etc/ Private keys for each certificate
  • /opt/ (Puppet Server) Local copy of (GIT repository)
  • /var/log/ (Puppet Server) log file

Classes and parameters


  • acme
  • acme::params
  • acme::request::handler


  • acme::csr
  • acme::deploy
  • acme::deploy::crt
  • acme::request
  • acme::request::crt


  • acme_git_url: URL to the GIT repository (feel free to use a local mirror)
  • acme_host: Defaults to your Puppet Server; override for testing purposes
  • dh_param_size: DH parameter size, defaults to 2048
  • letsencrypt_ca: The Let's Encrypt CA that should be used, choose either staging or production
  • letsencrypt_proxy: A proxy server that should be used when connecting to the Let's Encrypt CA
  • manage_packages: Set to false to disable package management. Defaults to true


  • acme_csrs
  • acme_csr_*
  • acme_crts


Requires multiple Puppet runs

It takes several puppet runs to issue a certificate. Two Puppet runs are required to prepare the CSR on your Puppet node. Two more Puppet runs on your Puppet Server are required to sign the certificate. Now it's ready to be collected by your Puppet node.

The process is seamless for certificate renewals, but it takes a little time to issue a new certificate.

HTTP-01 challenge type untested

The HTTP-01 challenge type is supported, but it's untested with this module. Some additional parameters may be missing. Feel free to report issues or suggest enhancements.

OS Compatibility

This module was tested on CentOS/RedHat and Ubuntu/Debian. Please open a new issue if your operating system is not supported yet, and provide information about problems or missing features.


Please use the GitHub issues functionality to report any bugs or requests for new features. Feel free to fork and submit pull requests for potential contributions.


This module is a fork of the excellent bzed/bzed-letsencrypt. The fork was necessary in order to support instead of dehydrated.


Based on bzed/bzed-letsencrypt, Copyright 2017 Bernd Zeimetz.

Let's Encrypt is a trademark of the Internet Security Research Group. All rights reserved.

See the LICENSE file for further information.