Automatic HTTPS possible rate limit exceeded with Let's Encrypt

1. Caddy version (caddy version):

caddy:2.4.2-alpine

2. How I run Caddy:

{$SERVICE_DOMAIN} {
    reverse_proxy service:3000
}

{$APP_DOMAIN} {
    root * /var/www/html/app
    encode gzip zstd
    try_files {path} {path}/ /index.html
    file_server
}

a. System environment:

Docker image 2.4.2-alpine

b. Command:

docker compose up

c. Service/unit/compose file:

version: '3.7'


services:
    app:
        container_name: app
        image: ${APP_IMAGE}
        volumes:
            - static-content:/home/node/app/dist/app

    service:
        container_name: service
        image: ${SERVICE_IMAGE}
        environment:
            - DB_HOST=database
            - NODE_ENV=${NODE_ENV}
            - DB_PASSWORD=${DB_PASSWORD}
            - JWT_SECRET=${JWT_SECRET}
        networks:
            - backend
        depends_on:
            - database
        restart: unless-stopped

    database:
        container_name: database
        image: postgres:13.1-alpine
        environment:
            - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
        volumes:
            - pgdata:/var/lib/postgresql/data
        networks:
            - backend
        restart: unless-stopped

    webserver:
        container_name: webserver
        image: caddy:2.4.2-alpine
        environment:
            - APP_DOMAIN=${APP_DOMAIN}
            - SERVICE_DOMAIN=${SERVICE_DOMAIN}
        ports:
            - "80:80"
            - "443:443"
            - "3000:3000"
        volumes:
            - ./Caddyfile:/etc/caddy/Caddyfile
            - static-content:/var/www/html/app
            - caddy_data:/data
            - caddy_config:/config
        networks:
            - backend

        restart: unless-stopped

networks:
    backend:
        name: backend

volumes:
    static-content:
    caddy_data:
    caddy_config:
    pgdata:
        name: pgdata

d. My complete Caddyfile or JSON config:

{$SERVICE_DOMAIN} {
    reverse_proxy service:3000
}

{$APP_DOMAIN} {
    root * /var/www/html/app
    encode gzip zstd
    try_files {path} {path}/ /index.html
    file_server
}

3. The problem I’m having:

My question: will restarting the Caddy Docker container dozens of times per day (CI/CD) cause constant renewals for HTTPS certificates?

I have read the documentation Automatic HTTPS — Caddy Documentation as well as the samples on Docker Hub. Currently, I have the front end application building its static content into a shared Docker volume between the app container and the Caddy container. The front end code is being updated all the time. In order to re-attach the latest app volume to the Caddy container I would need to stop the Caddy container, refresh the volume and restart the Caddy container. Does this initiate an certificate renewal each time? I had assumed that it would check the disk (/data or /config) to determine whether a renewal is required but I don’t see this documented anywhere.

Alternatively, is there a better approach to this (fairly common) scenario?

4. Error messages and/or full log output:

None yet

5. What I already tried:

I am currently using HTTP only in my Caddyfile by specifying :3000 and :80 for the service and web application, respectively

6. Links to relevant resources:

No, because you have a caddy_data:/data volume which will persist your certs/keys.

Certificates issued by Let’s Encrypt have a 90 day lifetime, and Caddy will attempt to renew them after 2/3 of their life, so after 60 days.

You shouldn’t need to restart Caddy so frequently though. You can tell Caddy to reload its config in case it changes, which is graceful, instead of doing a full restart.

Although if your Docker environment variables do change, then you do need to restart it for that to take.

Thanks for your response!

Is there documentation outlining the steps taken prior to attempting a certificate renewal?

There’s nothing to do, Caddy entirely automates the process. Just make sure your ports 80 and 443 are still publicly accessible, and DNS records are kept up to date, and Caddy does the rest.

Yeah, sorry I meant the general algorithm that takes place within Caddy before it decides to renew the certificate.

Could you be more specific about what you’re trying to understand?

Read through this page in the docs:

1 Like

I also go into depth on automatic https here in my Expert Caddy chapter, for sponsors: Chapters - Expert Caddy

Yep, I completely read Automatic HTTPS — Caddy Documentation

I will read the Expert Caddy material as well.

Essentially: I would like to use HTTPS within my staging environment and this environment will experience a lot of churn through CI/CD. Currently relying on HTTP only.

With each deployment of the latest front-end code:

  1. Remove front-end and Caddy containers
  2. Remove static-content Docker volume
  3. docker compose up both front-end and Caddy containers
  4. Caddy is now serving the latest front-end content

Repeatedly bringing up the Caddy container like this is where I am concerned.

Preemptively, I wanted to verify that I wouldn’t exceed the rate limit with Let’s Encrypt with this type of activity. I migrated to Caddy from NGINX where I was deploying as described above but, as you know, it does not automate the renewal of certificates. I was managing the cert renewal with a single request nightly through cron. So, in that case I had no worries that I would suddenly get banned from renewals.

Well, I would just emphasize what the docs say about the data directory:

The data directory must not be treated as a cache. Its contents are not ephemeral or merely for the sake of performance. Caddy stores TLS certificates, private keys, OCSP staples, and other necessary information to the data directory. It should not be purged without understanding the implications.

It is crucial that this directory is persistent and writeable by Caddy.

You can do what you want with the containers, but if you delete Caddy’s storage, of course it will have to get new certificates again, and yes, depending on rate limits enforced by CAs, that could earn you strikes if you do it too often.

If using Let’s Encrypt, you could use its staging endpoint exclusively; but these won’t be trusted by browsers.

Or just persist the storage. That’s what I’d recommend.

Thanks for the heads up.
From the following volumes, the only one being shredded upon deployment is ‘static-content’, the rest will remain for weeks/months.

            - ./Caddyfile:/etc/caddy/Caddyfile
            - static-content:/var/www/html/app
            - caddy_data:/data
            - caddy_config:/config

Thank you both for taking the time to respond. You’ve answered my question.

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.