Hetzner DNS API

Bug Report Template

Hetzer DNS API is deprecated,

Description

The current used Hetzner DNS API is deprecated, and will be decommissioned next month.

https://docs.hetzner.com/networking/dns/faq/beta

Are there any plans to support the long existing Hetzer Cloud API by ApisCP

kind regards

EDIT: there is also a “supported” PHP SDK for the Cloud API

I’m aware of it. A rewrite will be addressed before the end of this month.

I´m very happy to read this…

Want to ask friendly, about a short update.

Any news regarding Hetzner Cloud API Support?

Out through Tuesday for a wedding. Before I departed, I was working on an edge case where an incomplete site deletion could recycle an admin group, which includes structuring a test case to adequately isolate the concern.

I understand this is a problem for you. I am constrained on resources, prioritizing support. What time is left over is allocated toward development.

i have to bag for pardon, but are there any news?

Hetzner migrated the old DNS Zones to the new API (as announced .

Starting last week, no automated Let´s Encrypt Updates via DNS Challenge are possible,
no DNS Updates, as also no new DNS registrations.

It’s failing a couple complex test cases, which I’d like to resolve by Wednesday. If you need the module ASAP, you can install it direct from GitHub:

cd /usr/local/apnscp
git clone https://github.com/apisnetworks/apiscp-dns-hetzner config/custom/dns-hetzner
sudo -u apnscp ./composer dumpautoload
systemctl restart apnscp

A few notes as well:

  • DS records are improperly restricted on zone cuts, where they belong per RFC 3658. I emailed Hetzner about this on Friday, waiting to hear back.
  • Keys are 64 characters long, no longer 32 chars.
  • Zones must be manually migrated to their new interface if not done yet.
1 Like

Hetzner already forced migration to the new API, none of my zones are available on old API.

i have forced updated the SSL Certs. Will have round about 12 days, until the next update.

Hetzner API update is now available on edge.

cpcmd scope:set cp.update-policy edge-major
upcp

update done on edge…

after restart of apnscp.service i got following messages in the start.log

ERROR  : Admin_Module::edit_site(): Opcenter\Account\Edit::installServices(): failed verification on service `dns': Opcenter\Dns\Providers\Hetzner\Module::__construct(): DNS manageme
nt unavailable until migration to new Hetzner API key
         0B. Error_Reporter::merge_buffer([[message:"Opcenter\Account\Edit::installServices(): failed verification on service `dns': Opcenter\Dns\Providers\Hetzner\Module::__construc
t(): DNS management unavailable until migration to new Hetzner API key", severity:16, caller:"Opcenter\Account\Edit::installServices", bt:"         0. Error_Reporter::append_msg("Opc
enter\Dns\Providers\Hetzner\Module::__construct(): DNS management unavailable until migration to new Hetzner API key", 16, null) 

I tried to update the DNS Provider and Key also via Nexus. With the same error:

I have also updated the Default Provider to ‘cpcmd scope:set dns.default-provider hetzner’ and scope:set the dns.default-provider-key

cpcmd misc:cp_version
revision: 645700f45c36fd4f6224e16c4637ab257511d46d
timestamp: 1778618257
ver_maj: 3
ver_min: 2
ver_patch: 48
ver_pre: 58-g645700f45
dirty: false
debug: false

Key is 64 characters long now. It must be generated within Hetzner’s Console.

The old key is no longer valid.

EditDomain --reconfig -c dns,key=DEFAULT --all will reset the API key.

Doesn’t work

INFO : Edited 16 sites in 1.42s (0.09s each) 0 succeeded, 16 failed, 0 skipped.

And yes, i generated a new Key and checked the key is 64 Chars long

3 of the Sites are currently down, after trying the change of the dns provider, and dns key via nexus!
Down means: “Service unavailable”

lgtm.

Cannot reproduce.

[root@rocky-test playbooks]# cpcmd scope:set cp.config dns provider_key 0aOKNbygH0yhPg376BQs9Brh6TAxJjaeXXXXXfuvSFm4uqp9Wko6xdAK9EEtEpAm
INFO   : ApisCP will restart in 1 minute
----------------------------------------
MESSAGE SUMMARY
Reporter level: SUCCESS
INFO: ApisCP will restart in 1 minute
----------------------------------------
1
[root@rocky-test playbooks]# EditDomain  -c dns,provider=hetzner -c dns,key=DEFAULT apnscp.dev
DEBUG  : Firing svclyr.success
DEBUG  : Firing service.success
DEBUG  : Running reconfigure on shield,enabled
DEBUG  : Running reconfigure on ipinfo,ipaddrs
DEBUG  : Running reconfigure on rampart,whitelist
DEBUG  : Running reconfigure on diskquota,enabled
DEBUG  : Running reconfigure on mysql,dbaseadmin
DEBUG  : Running reconfigure on mysql,dbaseprefix
DEBUG  : Running reconfigure on cgroup,enabled
cDEBUG  : Running reconfigure on apache,webserver
DEBUG  : Running reconfigure on php,enabled
DEBUG  : 0.00000 service.success Opcenter\Service\Validators\Aliases\Enabled enabled
DEBUG  : Running reconfigure on pgsql,dbaseadmin
DEBUG  : Running reconfigure on mail,enabled
pDEBUG  : Running hooks for `apnscp.dev' (user: `myadmin')
DEBUG  : 0.00002: Site_Module -> _edit
DEBUG  : Skipping reconfiguration on service config `shield', no known module mapping
DEBUG  : 0.00001: Billing_Module -> _edit
DEBUG  : 0.00084: Dns_Module_Surrogate -> _edit
DEBUG  : 0.00002: Ipinfo_Module_Surrogate -> _edit
DEBUG  : 0.00001: Mysql_Module -> _edit
DEBUG  : 0.00001: Cgroup_Module -> _edit
DEBUG  : 0.00001: Web_Module -> _edit
DEBUG  : 0.00007: Php_Module -> _edit
DEBUG  : 0.00001: Bandwidth_Module -> _edit
DEBUG  : 0.00003: Ssl_Module -> _edit
DEBUG  : 0.00001: Tomcat_Module -> _edit
DEBUG  : 0.00001: User_Module -> _edit
DEBUG  : 0.00011: Aliases_Module_Surrogate -> _edit
DEBUG  : 0.01513: Auth_Module -> _edit
DEBUG  : 0.00001: Ftp_Module -> _edit
DEBUG  : 0.00001: Pgsql_Module -> _edit
DEBUG  : 0.00001: Ssh_Module -> _edit
DEBUG  : 0.00002: Email_Module_Surrogate -> _edit
DEBUG  : 0.00028: Majordomo_Module -> _edit
DEBUG  : 0.00001: Spamfilter_Module -> _edit
DEBUG  : 0.00003: Crontab_Module -> _edit
DEBUG  : Skipping reconfiguration on service config `example', no known module mapping
DEBUG  : 0.00001: Dav_Module -> _edit
DEBUG  : 0.00025: Letsencrypt_Module -> _edit
DEBUG  : 0.00001: Sql_Module -> _edit
DEBUG  : 0.00001: Task_Module -> _edit
DEBUG  : 0.00011: Webapp_Module -> _edit
DEBUG  : Firing vdedit.success
DEBUG  : Firing *.end
DEBUG  : Commiting service `siteinfo'
DEBUG  : Commiting service `shield'
DEBUG  : Commiting service `billing'
DEBUG  : Commiting service `dns'
DEBUG  : Commiting service `ipinfo'
DEBUG  : Commiting service `ipinfo6'
DEBUG  : Commiting service `metrics'
DEBUG  : Commiting service `rampart'
DEBUG  : Commiting service `diskquota'
DEBUG  : Commiting service `reseller'
DEBUG  : Commiting service `files'
DEBUG  : Commiting service `mysql'
DEBUG  : Commiting service `cgroup'
DEBUG  : Commiting service `apache'
DEBUG  : Commiting service `php'
DEBUG  : Commiting service `bandwidth'
DEBUG  : Commiting service `logrotate'
DEBUG  : Commiting service `logs'
DEBUG  : Commiting service `ssl'
DEBUG  : Commiting service `tomcat'
DEBUG  : Commiting service `users'
DEBUG  : Commiting service `aliases'
DEBUG  : Commiting service `auth'
DEBUG  : Commiting service `ftp'
DEBUG  : Commiting service `pgsql'
DEBUG  : Commiting service `ssh'
DEBUG  : Commiting service `vacation'
DEBUG  : Commiting service `mail'
DEBUG  : Commiting service `mlist'
DEBUG  : Commiting service `spamfilter'
DEBUG  : Commiting service `crontab'
DEBUG  : Edited site3, apnscp.dev (SUCCESS) Succeeded

[root@rocky-test playbooks]# cpcmd -d apnscp.dev dns:export apnscp.dev
;;
;; Domain:      apnscp.dev
;; Exported:    Wed, 13 May 2026 12:08:21 -0500
;;
$ORIGIN .
@       3600    IN      SOA     apnscp.dev.     dns.hetzner.com. (
        2026051533      ; serial
        86400   ; refresh
        10800   ; retry
        3600000 ; expire
        3600)   ; minimum

;; NS Records (YOU MUST CHANGE THIS)
apnscp.dev.     3600    IN NS   YOU_MUST_CHANGE_THIS_VALUE
apnscp.dev.     3600    IN NS   YOU_MUST_CHANGE_THIS_VALUE
apnscp.dev.     3600    IN NS   YOU_MUST_CHANGE_THIS_VALUE
apnscp.dev.     14400   IN NS   YOU_MUST_CHANGE_THIS_VALUE
apnscp.dev.     14400   IN NS   YOU_MUST_CHANGE_THIS_VALUE
apnscp.dev.     14400   IN NS   YOU_MUST_CHANGE_THIS_VALUE

;; A Records
apnscp.dev.     14400   IN A    165.23.215.130
ftp.apnscp.dev. 14400   IN A    165.23.215.130
horde.apnscp.dev.       14400   IN A    165.23.215.130
mail.apnscp.dev.        14400   IN A    165.23.215.130
roundcube.apnscp.dev.   14400   IN A    165.23.215.130
www.apnscp.dev. 14400   IN A    165.23.215.130


;; MX Records
apnscp.dev.     14400   IN MX   10 mail.apnscp.dev.


;; TXT Records
apnscp.dev.     14400   IN TXT  v=spf1 a mx ~all
_apnscp_uuid.apnscp.dev.        14400   IN TXT  8047836965287962489235d6c2d4b-e2da-52c9-b5fc-0bf9c7a6d91c
dkim3._domainkey.apnscp.dev.    14400   IN TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmE2xPfvKfJBWnkEf5bXNTdoVTccGAKRG/G0cI1rEX/VVk/7d4kftFM5K9CbbCaqVWOy6oeTxWVvlM+lOVJ8Vcuu3qfu8+Ec5i7g/UtjIJhVlEPzK5TNdPPVGK72ghb592FBEQWYGZqdS1UnnORz4BFAfSwRr4Ut8wR16fD5RwymsvgdsP8mjIcbRb0vN2QS" "HWNWoAAccgdOGb8Nguf3kiBe1q1/2a335oMHErrZkuy3943EQCxogMLS/UiCXZ4tAFHyVcEY6h5PptLZmbQhAI7zHwe40eumWcuBYW2jU1NyVNkFkFR/xGY3LZkd04CVtv9A15rkcoV2fxVes7xUtTQIDAQAB"
_dmarc.apnscp.dev.      14400   IN TXT  v=DMARC1; p=none


[root@rocky-test playbooks]# cpcmd -d apnscp.dev common:get-service-value dns key
0aOKNbygH0yhPg376BQs9Brh6TAxJjaeXXXXXfuvSFm4uqp9Wko6xdAK9EEtEpAm

[root@rocky-test playbooks]# cpcmd -d apnscp.dev common:get-service-value dns key | tr -d '\n' | wc -c
64

i’ve used:

cpcmd scope:set dns.default-provider-key STRONGKEYwith64Chars;)newfromAPI

as mentioned in the Github Repo

Will retry with cpcmd scope:set cp.config

give me 3 min

Still the same error in start.log

I now that Key creation works, because same method was used for DynDNS Config on Opensense, and there are the API Keys functional. So it is not related to the key

Do you need further informations?

ok, i rolled back my snapshot, and will give it within the next days a new try.
Downtime of services was too long.

I further analyzed the Issue…

i used following to make a “simple” DNS Query:

env DEBUG=1 cpcmd -d domain.net dns:get-hosting-nameservers domain.net
ERROR  : Opcenter\Dns\Providers\Hetzner\Module::__construct(): DNS management unavailable until migration to new Hetzner API key
         0. Error_Reporter::add_error("DNS management unavailable until migration to new Hetzner API key", )
            [/usr/local/apnscp/lib/log_wrapper.php:72]
         1. error("DNS management unavailable until migration to new Hetzner API key")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:62]
         2. Opcenter\Dns\Providers\Hetzner\Module->__construct()
            [/usr/local/apnscp/lib/ContextableTrait.php:41]
         3. Opcenter\Dns\Providers\Hetzner\Module::instantiateContexted(Auth_Info_User, [username:"domain_admin", domain:"domain.net", level:2, site_id:3, group_id:1004, user_id:9997, id:"xACuLO74kYAKiU0rZfCuMajL8rWooUus", hotworker:null, language:"en_US.UTF-8", ])
            [/usr/local/apnscp/lib/Module/Provider.php:34]
         4. Module\Provider::get("dns", "hetzner", Auth_Info_User, [username:"domain_admin", domain:"domain.net", level:2, site_id:3, group_id:1004, user_id:9997, id:"xACuLO74kYAKiU0rZfCuMajL8rWooUus", ])
            [/usr/local/apnscp/lib/modules/dns.php:201]
         5. Dns_Module->_proxy()
            [/usr/local/apnscp/lib/Module/Skeleton/Standard.php:105]
         6. Module\Skeleton\Standard::autoloadModule(Auth_Info_User, [username:"domain_admin", domain:"domain.net", level:2, site_id:3, group_id:1004, user_id:9997, id:"xACuLO74kYAKiU0rZfCuMajL8rWooUus", hotworker:null, language:"en_US.UTF-8", ])
            [/usr/local/apnscp/lib/apnscpfunction.php:612]
         7. apnscpFunctionInterceptor->load_module("dns")
            [/usr/local/apnscp/lib/apnscpfunction.php:815]
         8. apnscpFunctionInterceptor->get_instance("dns")
            [/usr/local/apnscp/lib/apnscpfunction.php:744]
         9. apnscpFunctionInterceptor->validate_functions("dns")
            [/usr/local/apnscp/lib/apnscpfunction.php:1068]
        10. apnscpFunctionInterceptor->verify_args("dns_get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/CLI/cmd.php:52]
        11. CLI\__call("dns_get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/CLI/cmd.php:574]
        12. CLI\main()
            [/usr/local/apnscp/bin/cmd:7]
(442eee88-74bc-5c1c-a947-7421deda29f6 GuzzleHttp\Exception\ClientException) EXCEPTION: Client error: `GET https://api.hetzner.cloud/v1/zones/domain.net` resulted in a `401 Unauthorized` response:
{"error":{"code":"unauthorized","details":null,"message":"the token you have provided is invalid"}}

 
[/usr/local/apnscp/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:111]
         0. GuzzleHttp\Exception\RequestException::create(GuzzleHttp\Psr7\Request, GuzzleHttp\Psr7\Response, null, null)
            [/usr/local/apnscp/vendor/guzzlehttp/guzzle/src/Middleware.php:72]
         1. GuzzleHttp\Middleware::GuzzleHttp\{closure}(GuzzleHttp\Psr7\Response)
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:209]
         2. GuzzleHttp\Promise\Promise::callHandler(1, GuzzleHttp\Psr7\Response, null)
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:158]
         3. GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/TaskQueue.php:52]
         4. GuzzleHttp\Promise\TaskQueue->run(true)
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:251]
         5. GuzzleHttp\Promise\Promise->invokeWaitFn()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:227]
         6. GuzzleHttp\Promise\Promise->waitIfPending()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:272]
         7. GuzzleHttp\Promise\Promise->invokeWaitList()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:229]
         8. GuzzleHttp\Promise\Promise->waitIfPending()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:69]
         9. GuzzleHttp\Promise\Promise->wait()
            [/usr/local/apnscp/vendor/guzzlehttp/guzzle/src/Client.php:189]
        10. GuzzleHttp\Client->request("GET", "zones/domain.net", [[User-Agent:"webCP@domain.net 3.2", Accept:"application/json", Authorization:"Bearer fsbqJEO7TRFgJEZ8kMbrg8p3lB0OdZky"], json:null, synchronous:true])
            [/usr/local/apnscp/config/custom/dns-hetzner/Api.php:58]
        11. Opcenter\Dns\Providers\Hetzner\Api->do("GET", "zones/domain.net")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:321]
        12. Opcenter\Dns\Providers\Hetzner\Module->populateZoneMetaCache("domain.net")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:371]
        13. Opcenter\Dns\Providers\Hetzner\Module->getZoneMeta("domain.net", "authoritative_nameservers")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:388]
        14. Opcenter\Dns\Providers\Hetzner\Module->get_hosting_nameservers("domain.net")
            [/usr/local/apnscp/lib/Module/Skeleton/Standard.php:146]
        15. Module\Skeleton\Standard->_invoke("get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/apnscpfunction.php:996]
        16. apnscpFunctionInterceptor->call("dns_get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/CLI/cmd.php:55]
        17. CLI\__call("dns_get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/CLI/cmd.php:574]
        18. CLI\main()
            [/usr/local/apnscp/bin/cmd:7]
         19. GuzzleHttp\Exception\RequestException::create(GuzzleHttp\Psr7\Request, GuzzleHttp\Psr7\Response, null, null)
            [/usr/local/apnscp/vendor/guzzlehttp/guzzle/src/Middleware.php:72]
         20. GuzzleHttp\Middleware::GuzzleHttp\{closure}(GuzzleHttp\Psr7\Response)
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:209]
         21. GuzzleHttp\Promise\Promise::callHandler(1, GuzzleHttp\Psr7\Response, null)
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:158]
         22. GuzzleHttp\Promise\Promise::GuzzleHttp\Promise\{closure}()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/TaskQueue.php:52]
         23. GuzzleHttp\Promise\TaskQueue->run(true)
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:251]
         24. GuzzleHttp\Promise\Promise->invokeWaitFn()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:227]
         25. GuzzleHttp\Promise\Promise->waitIfPending()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:272]
         26. GuzzleHttp\Promise\Promise->invokeWaitList()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:229]
         27. GuzzleHttp\Promise\Promise->waitIfPending()
            [/usr/local/apnscp/vendor/guzzlehttp/promises/src/Promise.php:69]
         28. GuzzleHttp\Promise\Promise->wait()
            [/usr/local/apnscp/vendor/guzzlehttp/guzzle/src/Client.php:189]
        29. GuzzleHttp\Client->request("GET", "zones/domain.net", [[User-Agent:"webCP@domain.net 3.2", Accept:"application/json", Authorization:"Bearer fsbqJEO7TRFgJEZ8kMbrg8p3lB0OdZky"], json:null, synchronous:true])
            [/usr/local/apnscp/config/custom/dns-hetzner/Api.php:58]
        30. Opcenter\Dns\Providers\Hetzner\Api->do("GET", "zones/domain.net")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:321]
        31. Opcenter\Dns\Providers\Hetzner\Module->populateZoneMetaCache("domain.net")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:371]
        32. Opcenter\Dns\Providers\Hetzner\Module->getZoneMeta("domain.net", "authoritative_nameservers")
            [/usr/local/apnscp/config/custom/dns-hetzner/Module.php:388]
        33. Opcenter\Dns\Providers\Hetzner\Module->get_hosting_nameservers("domain.net")
            [/usr/local/apnscp/lib/Module/Skeleton/Standard.php:146]
        34. Module\Skeleton\Standard->_invoke("get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/apnscpfunction.php:996]
        35. apnscpFunctionInterceptor->call("dns_get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/CLI/cmd.php:55]
        36. CLI\__call("dns_get_hosting_nameservers", ["domain.net"])
            [/usr/local/apnscp/lib/CLI/cmd.php:574]
        37. CLI\main()
            [/usr/local/apnscp/bin/cmd:7]

I recognized that this method still uses the old Token from the old Hetzner API

So i also checked the files

/root/apnscp-vars.yml
/root/apnscp-vars-runtime.yml

and set again the dns-default provider key via

env DEBUG=1 cpcmd scope:set dns.default-provider-key 'averiefiedtokenwith64chars'

Run again

cpcmd -d domain.net dns:get-hosting-nameservers domain.net

with the same result

Debug output shows that the new Hetzner API Url is used, but still the old Authorization:"Bearer

BTW: i´m also unsure that this cpcmd Command is functional, because i didn´t see that i queries the zoneID, it sets directly the domainname as “zone” which doesn´t work with the new API

But the Point is: it still doesn´t use the new dns-default-provider-key