Here’s how I used acme.sh to setup Let’s Encrypt for my Pi-holes.

Install:

sudo apt-get install socat lighttpd-mod-openssl
curl https://get.acme.sh | bash -s email=ssl@josh.fail

Log out and back in so acme.sh is available.

Next, configure lighttpd, in /etc/lighttpd/external.conf:

server.modules += ( "mod_openssl" )

$HTTP["host"] == "dns1.josh.fail" {
  # Ensure the Pi-hole Block Page knows that this is not a blocked domain
  setenv.add-environment = ("fqdn" => "true")

  # Enable the SSL engine with a LE cert, only for this specific host
  $SERVER["socket"] == ":443" {
    ssl.engine = "enable"
    ssl.pemfile = "/etc/letsencrypt/dns1.josh.fail/hostkey.pem"
    ssl.ca-file =  "/etc/letsencrypt/dns1.josh.fail/fullchain.crt"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.use-sslv2 = "disable"
    ssl.use-sslv3 = "disable"
  }

  # Redirect HTTP to HTTPS
  $HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
      url.redirect = (".*" => "https://%0$0")
    }
  }
}

$HTTP["host"] == "dns1.local" {
  $HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
      url.redirect = (".*" => "https://dns1.josh.fail$0")
    }
  }
}

Create a place to store the certificates:

sudo mkdir -p /etc/letsencrypt/dns1.josh.fail
sudo chown pi:pi /etc/letsencrypt/dns1.josh.fail

Now, issue the cert. I am using Digital Ocean and DNS challenges, so it looked something like:

export DO_API_KEY=asdf
acme.sh --issue --dns dns_dgon --server letsencrypt -d dns1.josh.fail

Install the cert and ensure lighttpd is restarted when a new cert is issued:

acme.sh --install-cert -d dns1.josh.fail \
  --key-file /etc/letsencrypt/dns1.josh.fail/host.key \
  --fullchain-file /etc/letsencrypt/dns1.josh.fail/fullchain.crt \
  --cert-file /etc/letsencrypt/dns1.josh.fail/host.crt \
  --reloadcmd "cat /etc/letsencrypt/dns1.josh.fail/host.crt /etc/letsencrypt/dns1.josh.fail/host.key > /etc/letsencrypt/dns1.josh.fail/hostkey.pem; sudo service lighttpd restart"

Done!