How to handle standalone certbot certificates

1. The problem I’m having:

I am using custom TLS Certificates with the tls fullchain.pem privkey.pem option.
Until now, I requested my certificates manually using a .csr certificate file, but want to migrate to a automatic tool such as certbot.

The certification of the standalone certificates finished successfully with the command specified below and the certificates live under /etc/letsencrypt/live.

My problem is that the privkey.pem has permissions of 600 and is owned by root:root, which results in caddy not being able to read the private key.

I am not sure what the best solution for this is:
How can I automatically move the certificates to a folder owned by caddy, but only if a new certificate is required (due to the certificate still being valid)

2. Error messages and/or full log output:

Error: loading initial config: loading new config: loading tls app module: provision tls: loading certificates: open /etc/letsencrypt/live/privkey.pem: permission denied

3. Caddy version:

v2.11.4

4. How I installed and ran Caddy:

4a. System environment:

Debian Trixie

4b. Command:

certbot certonly --standalone --non-interactive --agree-tos --eab-kid --eab-hmac-key --server --domain

4c. Service/unit/compose file:

/usr/lib/systemd/system/caddy.service

4d. My complete Caddy config:

tls fullchain.pem privkey.pem

5. What I already tried, and links to relevant resources:

Searched in caddy.community and multiple search tearms in search engine.

Assistance disclosure

No AI used.

The cleanest answer is usually to not use Certbot here. Let Caddy manage the certificate directly if possible. If the reason for Certbot is EAB, Caddy supports ACME EAB as well so you can configure the issuer in Caddy instead of copying /etc/letsencrypt/live files around.

If you must keep Certbot, do not make Caddy read root-only private keys directly. Use a Certbot deploy hook that copies the renewed fullchain.pem and privkey.pem into a Caddy-readable directory e.g. /etc/caddy/certs/example.com/ then set ownership/permissions for the Caddy service user.

Example deploy-hook shape: certbot certonly ... --deploy-hook '/usr/local/bin/deploy-caddy-cert.sh' The hook should copy only after successful renewal, set restrictive permissions then reload Caddy. Avoid broadening permissions on /etc/letsencrypt/live unless you are absolutely comfortable with the security trade-off.

Direct commands:

install -d -o caddy -g caddy -m 0750 /etc/caddy/certs/example.com
install -o caddy -g caddy -m 0640 /etc/letsencrypt/live/example.com/fullchain.pem /etc/caddy/certs/example.com/fullchain.pem
install -o caddy -g caddy -m 0600 /etc/letsencrypt/live/example.com/privkey.pem /etc/caddy/certs/example.com/privkey.pem
systemctl reload caddy

Thank you for your answer.
So the ´Install´ commands install EAB into caddy?

I read about the deploy hook in certbot documentation just now, and I am a little bit confused of what the purpose of the 3 hooks are (deploy, pre, post):
Does the deploy hook only run if I run certbot with ´–deploy-hook´? Or does it run everytime certbot issues a renewal?
The pre and post (renewal hooks) are more clear to me: They run everytime a certificate needs to be renewed: The pre hook just before and the post hook just after the renewal, right?

Best option would probably be to let Caddy manage the cert directly. Caddy supports ACME EAB so you probably do not need Certbot here.

If you must keep Certbot, use a --deploy-hook to copy the renewed cert/key into a Caddy-readable directory then reload Caddy. Do not loosen permissions on /etc/letsencrypt/live.

Example hook:

install -d -o caddy -g caddy -m 0750 /etc/caddy/certs/example.com
install -o caddy -g caddy -m 0640 /etc/letsencrypt/live/example.com/fullchain.pem /etc/caddy/certs/example.com/fullchain.pem
install -o caddy -g caddy -m 0600 /etc/letsencrypt/live/example.com/privkey.pem /etc/caddy/certs/example.com/privkey.pem
systemctl reload caddy

Then configure Caddy to use /etc/caddy/certs/example.com/fullchain.pem and /etc/caddy/certs/example.com/privkey.pem.