Pebble
A miniature version of Boulder, Pebble is a small RFC 8555 ACME test server not suited for a production certificate authority.
Install / Use
/learn @letsencrypt/PebbleREADME
Pebble
A miniature version of Boulder, Pebble is a small ACME test server not suited for use as a production CA.
!!! WARNING !!!

Pebble is NOT INTENDED FOR PRODUCTION USE. Pebble is for testing only.
By design Pebble will drop all of its state between invocations and will randomize keys/certificates used for issuance.
Goals
Pebble has several top level goals:
- Provide a simplified ACME testing front end
- Provide a test-bed for new and compatibility breaking ACME features
- Encourage ACME client best-practices
- Aggressively build in guardrails against non-testing usage
Pebble aims to address the need for ACME clients to have an easier to use, self-contained version of Boulder to test their clients against while developing ACME v2 support. Boulder is multi-process, requires heavy dependencies (MariaDB, gRPC, etc), and is operationally complex to integrate with other projects.
Where possible Pebble aims to be a test-bed for new ACME protocol features that can be used to inform later Boulder support. Pebble provides a way for Boulder developers to test compatibility breaking changes more aggressively than is appropriate for Boulder.
In places where the ACME specification allows customization/CA choice Pebble aims to make choices different from Boulder. For instance, Pebble changes the path structures for its resources and directory endpoints to differ from Boulder. The goal is to emphasize client specification compatibility and to avoid "over-fitting" on Boulder and the Let's Encrypt production service.
Lastly, Pebble will enforce it's test-only usage by aggressively building in
guardrails that make using it in a production setting impossible or very
inconvenient. Pebble will not support non-volatile storage or persistence
between executions. Pebble will also randomize keys/certificates used for
issuance. Where possible Pebble will make decisions that force clients to
implement ACME correctly (e.g. randomizing /directory endpoint URLs to ensure
clients are not hardcoding URLs.)
Limitations
Pebble is missing some ACME features (PRs are welcome!). It does not presently support pre-authorization or revoking a certificate issued by a different ACME account by proving authorization of all of the certificate's domains.
Pebble does not perform all of the same input validation as Boulder. Some domain names that would be rejected by Boulder/Let's Encrypt may work with Pebble.
Pebble does not enforce any rate limits. It is not presently an appropriate tool for testing that your client handles Boulder/Let's Encrypt rate limits correctly.
Install
- Set up Go
- Add
~/go/binto your $PATH, or setGOBINto a directory that is in your $PATH already, so thatpebblewill be in your $PATH for easy execution.- One way to do this is to add
export PATH=$PATH:$HOME/go/binto your~/.profile
- One way to do this is to add
- git clone https://github.com/letsencrypt/pebble/
- cd pebble
- go install ./cmd/pebble
Usage
Binary
Assuming pebble is easily accessible in your $PATH:
pebble -config ./test/config/pebble-config.json
(otherwise replace pebble with ~/go/bin/pebble or $GOBIN/pebble)
Afterwards you can access the Pebble server's ACME directory
at https://localhost:14000/dir.
Docker
Pebble includes a docker-compose file that
will create a pebble instance that uses a pebble-challtestsrv instance for
DNS resolution with the correct ports mapped to the host system.
To download and start the containers run:
docker-compose up
Afterwards you can access the ACME API from your host machine at
https://localhost:14000/dir, pebble's management interface
at https://localhost:15000 and the pebble-challtestsrv's management
interface at http://localhost:8055.
To get started you may want to update the pebble-challtestsrv DNS data
with a new default IPv4 address to use to respond to A queries from pebble:
curl --request POST --data '{"ip":"172.20.0.1"}' http://localhost:8055/set-default-ipv4
See the pebble-challtestsrv README for more information.
If you are running a one-off container for either pebble or
pebble-challtestsrv, you will need to manually map ports.
docker run -p 14000:14000 -p 15000:15000 ghcr.io/letsencrypt/pebble:latest
docker run -p 5001:5001 -p 5002:5002 -p 5003:5003 -p 8053:8053 -p 8055:8055 -p 8443:8443 ghcr.io/letsencrypt/pebble-challtestsrv:latest
Prebuilt Docker Images
Pebble releases are published as Docker images to the Github Container Registry
With a docker-compose file:
services:
pebble:
image: ghcr.io/letsencrypt/pebble:latest
command: -config /test/my-pebble-config.json
ports:
- 14000:14000 # ACME port
- 15000:15000 # Management port
environment:
- PEBBLE_VA_NOSLEEP=1
volumes:
- ./my-pebble-config.json:/test/my-pebble-config.json
With a Docker command:
docker run -p 14000:14000 -p 15000:15000 -e "PEBBLE_VA_NOSLEEP=1" ghcr.io/letsencrypt/pebble
# or
docker run -p 14000:14000 -p 15000:15000 -e "PEBBLE_VA_NOSLEEP=1" --mount src=$(pwd)/my-pebble-config.json,target=/test/my-pebble-config.json,type=bind ghcr.io/letsencrypt/pebble -config /test/my-pebble-config.json
Default validation ports
To make it easier to test ACME clients and run challenge response servers without root privileges Pebble defaults to validating ACME challenges using unprivileged high ports:
- Default HTTP-01 Port: 5002
- Default TLS-ALPN-01 Port: 5001
These ports can be changed by editing the "httpPort" and "tlsPort" values of
the Pebble -config file provided to pebble.
Strict Mode
Pebble's goal to aggressively support new protocol features and backwards compatibility breaking changes is slightly at odds with its goal to provide a simple, light-weight ACME test server for clients to use in integration tests. On the one hand we want to introduce breaking changes quickly and use Pebble as a test-bed for this. On the other we want to make sure we don't break client integration tests using Pebble too often.
As a balance to meet these two needs Pebble supports a -strict flag. By
running Pebble with -strict false changes known to break client compatibility
are disabled.
Presently we default -strict to false but this will change in the future.
If you are using Pebble for integration tests and favour reliability over
learning about breaking changes ASAP please explicitly run Pebble with -strict false.
DNS Server
By default Pebble uses the system DNS resolver, this may mean that caching causes
problems with DNS-01 validation. It may also mean that no DNSSEC validation is
performed.
You should configure your system's recursive DNS resolver according to your
needs or use the -dnsserver flag to define an address to a DNS server.
pebble -dnsserver 10.10.10.10:5053
pebble -dnsserver 8.8.8.8:53
pebble -dnsserver :5053
You may find it useful to set pebble's -dnsserver to the address you used as
the -dnsserver argument when starting up a pebble-challtestsrv instance. This
will let you easily serve DNS data for Pebble. See the included
docker-compose.yml and the pebble-challtestsrv
README
for more information.
Testing at full speed
By default Pebble will sleep a random number of seconds (from 0 to 15) between individual challenge validation attempts. This ensures clients don't make assumptions about when the challenge is solved from the CA side by observing a single request for a challenge response. Instead clients must poll the challenge to observe the state since the CA may send many validation requests.
To test issuance "at full speed" with no artificial sleeps set the environment
variable PEBBLE_VA_NOSLEEP to 1. E.g.
PEBBLE_VA_NOSLEEP=1 pebble -config ./test/config/pebble-config.json
The maximal number of seconds to sleep can be configured by defining
PEBBLE_VA_SLEEPTIME. It must be set to a positive integer.
Skipping Validation
If you want to avoid the hassle of having to stand up a challenge response server for real HTTP-01, DNS-01 or TLS-ALPN-01 validation requests Pebble supports a mode that always treats challenge validation requests as successful. By default this mode is disabled and challenge validation is performed.
To have all challenge POST requests succeed without performing any validation run:
PEBBLE_VA_ALWAYS_VALID=1 pebble
Invalid Anti-Replay Nonce Errors
The urn:ietf:params:acme:error:badNonce error type is meant to be retry-able.
When receiving this error a client should make a subsequent request to the
/new-nonce endpoint (or use the nonce from the error response) to retry the
failed request, rather than quitting outright.
Experience from Boulder indicates that many ACME clients do not gracefully retry on invalid nonce errors. To help ensure future ACME clients are able to gracefully handle these errors by default **Pebble r
