Skip to content

Instantly share code, notes, and snippets.

@dakhnod
Last active December 7, 2025 02:09
Show Gist options
  • Select an option

  • Save dakhnod/776e086467acb24475a6d0a981bf3c7a to your computer and use it in GitHub Desktop.

Select an option

Save dakhnod/776e086467acb24475a6d0a981bf3c7a to your computer and use it in GitHub Desktop.
Automatic certificate renewal using certbot, letsencrypt and hetzner API
#!/bin/bash
set -e
API_KEY='YOUR_HETZNER_API_TOKEN'
ids=`curl \
-s \
--header "Auth-API-Token: ${API_KEY}" \
"https://dns.hetzner.com/api/v1/records" \
| \
jq \
-r \
'.records[] | select(.name | test("^_acme-challenge")) | .id'`
for id in $ids; do
echo "deleting id $id"
curl \
-s \
-X DELETE \
--header "Auth-API-Token: ${API_KEY}" \
"https://dns.hetzner.com/api/v1/records/${id}"
done
#!/bin/bash
systemctl reload apache2
echo "restarted apache2"

Automatic certificate registration and renewal using letsencrypt and Hetzner API

These are my scripts to automatically renew my certificates using letsencrypts and the hetzner API.

The TXT records through the hetzner API are needed in order to obtain wildcard certificates.

Just install these file (renew-certificate can be located anywhere, just adjust it's path in the file itself.)

Make sure you have installed certbot and jq.

Then, obtain an API token from hetzner, fill in your API key and domains into renew-certificates and run the renew-certificates file.

This should obtain the certificates for the first time, verifying all your domains in the process.

Afterwards, you can just run certbot renew periodically (for example through a cronjob), which should renew your certificates automatically, if needed.

You will be able to find your certificates in /etc/letsencrypt/live/*. You could configure your web server to use the certs from that folder.

Don't forget to restart your web server upon certificate renewal through a hook, for example through /etc/letsencrypt/renewal-hooks/deploy/00-restart-webserver

Here is where the files belong:

renew-certificates: anywhere, should stay there and be executable

dns-clean: /etc/letsencrypt/renewal-hooks/pre/00-dns-clean

dns-clean: symlink to /etc/letsencrypt/renewal-hooks/post/00-dns-clean -> /etc/letsencrypt/renewal-hooks/pre/00-dns-clean

00-restart-webserver: /etc/letsencrypt/renewal-hooks/deploy/00-restart-webserver

#!/bin/bash
set -e
# echo 'use "certbot renew" you dignus'
# exit
API_TOKEN="6iB2vdyEBso96DQ22E2jlOCyPXJr1GXQ"
if [ "${CERTBOT_DOMAIN}" ]; then
if [[ "${CERTBOT_DOMAIN}" == *nullco.de ]]; then
zone_id="vEnADjYJ3pa68EJndiW3Gh"
elif [[ "${CERTBOT_DOMAIN}" == *overneaf.de ]]; then
zone_id="b6wCzvbXcDkAYjovYh2USL"
elif [[ "${CERTBOT_DOMAIN}" == *deciderino.de ]]; then
zone_id="t674J3LaZQa2SueTRKzG38"
elif [[ "${CERTBOT_DOMAIN}" == *dakhno.de ]]; then
zone_id="mUm5aTRwtN7EPk4wdYduB7"
else
echo "unknown domain $CERTBOT_DOMAIN"
fi
payload="{
\"zone_id\": \"${zone_id}\",
\"name\": \"_acme-challenge.${CERTBOT_DOMAIN}.\",
\"type\": \"TXT\",
\"value\": \"${CERTBOT_VALIDATION}\"
}"
curl -s \
--header "Auth-API-Token: ${API_TOKEN}" \
--data "$payload" \
"https://dns.hetzner.com/api/v1/records"
for i in `seq 20`; do
echo "checking for DNS record, attempt $i/20"
sleep 5
dig TXT "_acme-challenge.${CERTBOT_DOMAIN}" @helium.ns.hetzner.de. | grep "${CERTBOT_VALIDATION}" && break
done
exit
fi
certbot certonly \
-d "nullco.de" \
-d "*.nullco.de" \
-d "*.home.nullco.de" \
-d "*.daniel.nullco.de" \
-d "*.nodered.nullco.de" \
-d "*.tha.nullco.de" \
-d "*.jonas.nullco.de" \
-d "overneaf.de" \
-d "*.overneaf.de" \
-d "deciderino.de" \
-d "*.deciderino.de" \
-d "dakhno.de" \
-d "*.dakhno.de" \
--manual-public-ip-logging-ok \
--manual \
--preferred-challenge dns \
--manual-auth-hook ~/bin/certbot-renew
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment