Pebble is a small ACME (Automatic Certificate Management Environment) test server not suited to be used as a production CA. One of the benefits of the ACME protocol is being able to create certificates using a wildcard, e.g. *

In my example I’ve set up a HTTPS connection for my development domain mydomain.test using Pebble and Certbot. This domain is just an alias for the host IP: just add <HOST IP> mydomain.test into /etc/hosts. You may use if you don’t run pebble from container.

Let’s start. Clone Pebble repository, and then you may build pebble executable (it’s written in Go language), or run Docker containers, like I did, using docker-compose:

> git clone
> cd pebble
> docker compose up

Now you have your own ACME server running.

If you run Pebble on docker containers, you need to tell ACME server where your fake domain is hosted. To do that run curl --request POST --data '{"ip":""}' http://localhost:8055/set-default-ipv4 with your correct HOST IP address (and make sure, that Pebble can access that IP address.)

Next, install certbot and run this command to generate needed leaf certificates:

> certbot certonly --standalone -d mydomain.test --server https://localhost:14000/dir --email --agree-tos --no-verify-ssl --http-01-port=5002

Used params: * certonly - Obtain or renew a certificate, but do not install it --standalone means, that you don’t need a server like Apache or Nginx for domain verification. Certbot runs it’s own local server. --http-01-port=5002 tells to run this server on port 5002. When you start Pebble, you can see in the logs record challtestsrv_1 | pebble-challtestsrv - 2020/11/17 07:04:45 Creating HTTP-01 challenge server on :5002, you need to use this port number for --http-01-port. * -d mydomain.test domain * --server https://localhost:14000/dir it is a path for the ACME server directory. Pebble from docker-compose runs ACME server on port 14000 and exposes that port to host. Certbot makes a request to that port. * --email --agree-tos - set your email and agree with the terms of service, this is optional. * --no-verify-ssl - since we make a https request to the server, we should use a root certificate from test/certs/pebble.minica.pem, but I decided to skip ssl verification.

Congratulations, now you have signed certificates on /etc/letsencrypt/live/mydomain.test/!

Root and Intermediate Certificates

Ok, now you have leaf certificates. But for an https connection, you also need a public Root Certificate. You may know which certificate exactly you need:

> cd /etc/letsencrypt/live/mydomain.test/
> openssl x509 -in fullchain.pem -noout -issuer
issuer=CN = Pebble Intermediate CA 0342fc

We need an intermediate certificate, which is taken from Pebble:

curl -s -o intermediate.crt https://localhost:15000/intermediates/0

Similarly, the issuer for this certificate is issuer=CN = Pebble Root CA 0e8b65, which can be downloaded from https://localhost:15000/roots/0. This certificate is self-signed.

It’s highly recommended by Pebble to not install these certificates system-wide, but only on clients, e.g. browsers.

Nginx or Apache setup and certificates verification

You may use this config to serve local development server through Nginx by https:

server {
    listen       443 ssl;
    server_name  www.mydomain.test mydomain.test;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_certificate "/etc/letsencrypt/live/mydomain.test/fullchain.pem";
    ssl_certificate_key "/etc/letsencrypt/live/mydomain.test/privkey.pem";
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_prefer_server_ciphers on;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_set_header Host $host;

Analogically, SSL params for Apache’s vhost may be set up as:

SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mydomain.test/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mydomain.test/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/mydomain.test/chain.pem

You may verify certificates by next command:

> openssl verify -CAfile root.crt /etc/letsencrypt/live/mydomain.test/cert.pem 
/etc/letsencrypt/live/mydomain.test/cert.pem: OK

Now you may test the HTTPS setup by curl -iL https://mydomain.test, using root and intermediate certificates. If all fine, and you see a response from the server, it means that you have successfully set up the HTTPS for your HTTP server. If it works for curl with https, then when you open the URL in the browser, you’ll see a message about untrusted CA. It’s ok, browsers don’t trust self-signed certificates. To get rid of this warning, it’s enough to import only the intermediate certificate to your browser. E.g. for Chrome go to Settings > Advanced Settings > Manage Certificates > Import. For Firefox it is Options > Encryption > View Certificates > Your Certificates > Open.

Arthur Sultanbekov

Arthur Sultanbekov


Joined January 2017. A geophysician in past, but decided that web is more interesting for him. Previous work was at small Russian telephony company. Learning python/Django and Javascript languages, web development and Linux administration.