Hello and thanks for this module,
Problem/Motivation
Since the Hetzner is popular in Europe, wouldn't it be nice to have Hetzner as a cloud provider?
Best,
Orkut
| Comment | File | Size | Author |
|---|---|---|---|
| #38 | Screenshot 2026-06-05 at 4.01.09 PM.png | 165.99 KB | baldwinlouie |
| #35 | Screenshot 2026-05-22 at 4.48.51 PM.png | 136.45 KB | baldwinlouie |
| #35 | Screenshot 2026-05-22 at 4.32.52 PM.png | 221.32 KB | baldwinlouie |
| #35 | Screenshot 2026-05-22 at 4.31.01 PM.png | 267.28 KB | baldwinlouie |
| #33 | Screenshot 2026-05-13 at 2.19.32 PM.png | 282.37 KB | baldwinlouie |
Issue fork cloud-3576337
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
erdinchasI'm working on it =)
Comment #5
erdinchas@orkut could you please check?
Comment #6
orkutmuratyilmaz@erdinchas, thanks for your contribution:)
Generally, the submodule looks working, but it has some phpcs, phpstan and phpunit errors. Can you solve them too?
Comment #7
erdinchasCould you please check again?
Comment #8
orkutmuratyilmazThank you @erdinchas, it works:)
Comment #9
baldwinlouie commented@erdinchas. Thank you so much for the new module. This looks like hard work to implement.
Before getting this merged, I tested it against a fresh installation of Drupal 11. I'm getting the following message when enabling
Hetzner Cloudfield.storage.cloud_config.field_api_endpoint.ymlis declared in the OpenStack submodule. If users have the OpenStack module enabled, Hetzner Cloud will encounter this installation error.One option is to rename the Hetzner Cloud's field_api_endpoint to something else.
Secondly, can you replace the
cloud/modules/cloud_service_providers/hetzner_cloud/images/hetzner_cloud.pngwith the actual Hetzner logo or image? It is currently an image of the AWS logo.Please give us your thoughts.
Comment #10
erdinchasThanks for the feedback, the issues you've raised must be solved with the latest push. Hetzner Cloud's field_api_endpoint is renamed as you've suggested. Could you please review again?
Comment #11
yas@erdinchas
Thank you for the wonderful patch. I posted my comments. Also, please add the PHPUnit tests based on CRUD. We won't merge the source code wi/o tests.
Thanks
Comment #12
baldwinlouie commented@erdinchas Thank you for the updated patch. Again, this is a lot of work and we really appreciate the patch!
I can install it now and am testing it out. I have some requests for you.
1. Please add the Hetzner Cloud provider menu to the left hand navigation. If you look at
aws_cloud.links.menu.yml, you will see a snippet that looks like this.This will dynamically add the AWS provider to the left hand navigation. See the following two images for reference. Notice the AWS links shows up in to submenus.
2. In the
Add Hetzner Cloudscreen, can you put the fields in#type=>details, similar to AWS, K8s? See screenshot:Please look at the code in
aws_cloud_form_cloud_config_aws_cloud_form_common_alter(), specifically, the functionaws_cloud_cloud_config_fieldsets(), which orders the fields for aws.You will see similar functions in K8s.
k8s_form_cloud_config_k8s_form_common_alter()k8s_cloud_config_fieldsets()3. I'd like to ask that

#type=>detailsbe used for the Hetzner entities' add/edit forms. See screenshot for an example with AWS.You can look at the Add/Edit forms in
aws_cloud/src/Form/Ec2/. All the forms are nested in'#type' => 'details',I will sign up for an account and do some additional testing.
Comment #13
erdinchasThank you both for your to the point fedbacks, appreciated!
I am just back from KubeCon and will work on your recent comments as soon as I can.
Comment #14
erdinchas@yas I have completed the revisions you've mentioned; those are ready to be reviewed.
@baldwinlouie now I'm starting to work on your suggestions :)
Comment #15
erdinchas@baldwinlouie, can you review?
Thanks to both of you for your guidance, looking forward to hear about what comes next; cheers!
Comment #16
erdinchasI just received a warning about pipeline failure, is the pipeline up to date or should I update the code?
Have a nice weekend!
Comment #17
baldwinlouie commented@erdinchas, Thank you for the updated patch. I will review it.
Don't worry about the pipeline failure. This is happening with our other Merge Requests as well.
Comment #18
baldwinlouie commented@erdinchas, I'm beginning to test the MR. The first issue I ran into is the following error:
When this happens, the module will return an error and the entire site will display a blank screen.
I've looked at the patch and traced it back to the following function.
$form['cloud_context']['#access'] = FALSE;When this is done, the `cloud_context` is not set. As a result, it is not set in the database:
As you know from building the Hetzner module, everything in the `cloud` suite is driven by the `cloud_context` value. All urls, plugin, look ups..etc...use that value.
I'm sure you're basing the
$form['cloud_context']['#access'] = FALSE;by looking at the other `provider modules` in the Cloud suite.Yes, we set it to FALSE, but we also use a custom `submit` function, and in it, we use a service to auto-generate the `cloud_context`.
For example, take a look at
k8s_form_cloud_config_k8s_add_form_submitWe set `cloud_context` to FALSE, but we also have a custom submit function to handle auto-generating the cloud_context value
Code in the K8s module that handles generating the cloud_context
Other examples include the
vmware_form_cloud_config_vmware_add_form_alterandvmware_form_cloud_config_vmware_add_form_submitfunctions.Can I ask you to update the the hetzner_cloud module to follow this convention?
Btw, is there a trial key for Hetzner cloud? I'd like to give the module a try too.
Comment #19
yas@erdinchas
Thank you for the updated patch. As for the pipeline failure, I was trying to fix composer failure at #3586634: Update a .gitlab-ci.yml file (5). Also, I fixed the following issues:
However, please note that the PHPUnit tests in the pipeline are still having errors.
Thanks
Comment #20
erdinchasI've tried my best to fix the mentioned issues, struggled a bit, but I think they're solved as far as I see.
Thanks again for your feedbacks.
Comment #21
orkutmuratyilmaz@erdinchas, sorry, but you need to pull the latest release of 8.x-dev first. then you should push your changes into this MR. I'm updating the issue's status.
Comment #22
yas@orkutmuratyilmaz
Thank you for the follow-up.
@erdinchas More specifically, please try the following:
Comment #23
erdinchasI finished merging the latest commits on 8.x and pushed it as suggested; however phpunit tests are failing due to PHP version issues.
Could you please check again?
Comment #24
yas@erdinchas
Thank you for the update. Since I've fixed the Drupal CI failure w/ the following issues, so could you please update your MR? Thanks!
Comment #25
erdinchasComment #26
baldwinlouie commented@erdinchas
Thank you for the update. I can now add a Hetzner cloud service provider without any errors.
Can I ask you to implement HetznerCloudLocalTasks.php? Please look at
K8sLocalTasks.phpandAwsCloudLocalTasks.phpfor reference.When implemented, all the Hetzner view's pages will have links to the other Hetzner entities. See following screenshot as an example.
Comment #27
erdinchasThank you for your feedback. The Hetzner Cloud entities include Servers, Volumes, Networks, Firewalls, Floating IPs, SSH Keys, Images, and Load Balancers. I have added them, ensuring the implementation style remains consistent with the existing AWS entities.
Comment #28
baldwinlouie commented@erdinchas Thank you so much for the update. The links now appear and I can click between the different Hetzner entities.
I did find a bug. Steps to reproduce.
1. Navigate to any of the Hetzner Cloud entity listing page
2. Click on "Local balancers"
3. Click "Add Load Balancer"
When doing so, I get the following exception:
Can you please take a look?
Comment #29
baldwinlouie commented@erdinchas I get a similar error when trying to add an ssh key. It seems to be happening when I try to add any of the Hetzner entities.
1. Navigate to any of the Hetzner SSH keys listing page: /clouds/hetzner_cloud/hetzner/ssh_key
2. Click "Add SSH Key"
3. Fill in the SSH key "Name" field
4. Click Save
I get the following error:
Comment #30
baldwinlouie commented@erdinchas Please take a look at one of the *CreateForm.php for AWS. The issue is because for cloud_context is not being passed to the
save()function.The {cloud_context} can be accessed as a method parameter for
buildForm(). For example, inElasticIpCreateForm.phpIf I do the same for
HetznerCloudNetworkCreateForm.phpI am able to pick up the
$cloud_context. See screenshot of my PHPStorm debug session.Comment #31
baldwinlouie commented@erdinchas I'm sorry to be asking you so many questions. I have another one.
I am looking at all the HetznerCloud*EditForms. It looks you are just making the edits in Drupal, and not making an API call to Hetzner to update the remote entities? For example, in
HetznerCloudNetworkEditForm, thesave()function looks like this:Can you confirm if the remote entity is updated?
Comment #32
erdinchasI am so glad about your questions and thanks for the detailed entries. They helped me to see a foundational problem that I've missed when making $cloud_context edits.
The exceptions on Add Load Balancer and Add SSH Key were both coming from the same place; our Create/Edit forms weren't accepting $cloud_context as a buildForm() parameter, so the entity hit save() with an empty cloud context and CloudConfigPluginManager::setCloudContext() blew up. I aligned all eight Hetzner Create/Edit forms (buildForm(..., $cloud_context = '')), set cloud_context on new entities before save.
On the last comment — yes, edits were only writing to Drupal locally and never reaching Hetzner. I have actually tested them with already existing entites, added and deleted them somehow, saw the actions took place on Hetzner console and tought it was working. Now I added updateServer/Volume/Network/Firewall/FloatingIp/SshKey/Image/LoadBalancer to HetznerCloudServiceInterface (wrapping the SDK's ->update() on each model) and wired every *EditForm::save() to push the change remotely first, aborting the local save if the API call fails.
For the Server form specifically, I followed the AWS convention: name is editable, server_type is editable but disabled unless the server is off (calling resizeServer() with an optional disk-upgrade checkbox), and image stays read-only since changing it is a rebuild action, not an update — same as how AWS treats AMI swaps.
Btw as I am unsure if you received my direct message about trial Hetzner API, I want to address it here as well. You can find it below;
"I was going to tell you that Hetzner does not make out an invoice under 5$ bill and there is no fee for account creation, so you can register and test it out freely for a while. Just do not forget to kill the servers after testing for not to get invoiced."
Comment #33
baldwinlouie commented@erdinchas Thank you so much for providing the Hetzner invoicing insight. I've created an account to do testing. It has been going great.
Thank you for fixing the Load Balancer and SSH Key issues. I'm doing more testing and am finding some additional issues.
1. Creating a volume is throwing a PHP exception. Steps to reproduce.
- Go to `Add Hetzner Cloud volume`
- Fill out the form. I filled out the following: Name="Test", Size="10", Location="fsn1", Filesystem Format="ext4"
- After clicking "Save", I get the following error:
2. Creating an SSH key does not create it on the Hetzner side. Steps to reproduce:
- Go to `Hetzner Cloud SSH Keys`
- Click `Add SSH Key`
- Fill out the Name field
- Click Save.
- Check the Hetzner console. I notice the SSH key is not created.
- I checked
HetznerCloudSshKeyCreateFormand the parentsave()method. It doesn't seem to be support creating a new SSH key.3. Creating Floating IP does not create it on the Hetzner side.
Steps to reproduce are the same as #2 above.
4. Creating a Load Balancer keeps producing a
Name field is required.validation error. Steps to reproduce:- Go to `Hetzner Cloud Load Balancers`
- Click `Add Load Balancer`
- Fill out the form. I made sure to fill out the `Name` at the top and bottom of the form.
- I get the following screenshot:
5. Creating a Server brings back the following error (Please advise if this is user error)
Steps to reproduce:

- Fill out the `Add Hetzner server` form
- Server Name: Test
- Location: Either `Auto select` or fsn1
- Server Type: cpx11
- Image: Debian 11
- Check my default SSH key
- After clicking save. I get this screenshot. I also notice the form does not repopulate with my previous entries.
Can you please take a look at these?
Thank you again in advance.
Comment #34
erdinchasHi @baldwinlouie,
Thanks a lot for the thorough review and for taking the time to test against a real Hetzner account — really appreciated. I've pushed fixes for all five issues you pointed:
1. Volume creation — the service was passing arguments to the SDK in the wrong order (size, name, …) when the SDK expects (name, size, server, location, …). Location also had to be resolved to a Location object rather than passed as a string.
2. SSH keys — the create form was missing a public_key field entirely, and save() only persisted locally. Added the field and rewired save() to call createSshKey() on the API, then re-sync.
3. Floating IPs — same root cause as SSH keys (form missing type, home_location, description; save() never hitting the API), plus the service's createFloatingIp() had its arguments in the wrong order.
4. Load Balancer name validation — the name base field had setDisplayOptions('form', …), so Drupal auto-rendered a widget that collided with the custom name field. Also discovered the SDK's LoadBalancers class doesn't expose a create() method at all, so the service now POSTs to /load_balancers directly via the HTTP client. While testing the fix I hit a follow-up "Cannot unset string offsets" fatal — that's also resolved by removing the name component from the entity form display in buildForm().
5. Server creation — made location required (the "Auto select" path was letting Hetzner pick a location that didn't support the chosen server type), added validation when the submitted server_type isn't in the catalog, and switched the error path to setRebuild(TRUE) so user input is preserved on API errors. Applied the same retention pattern to the Volume form.
Could you give it another go when you get a chance?
Comment #35
baldwinlouie commented@erdinchas Thank you for the updated patch. We are getting close!
Volume reate/delete - Works now!
SSH Keys - I'm getting the following error when trying to add SSH Keys
Floating IPs create/delete - Works now!
Load Balancer create/delete - Works now! There is a UI issue I was hoping you can adjust. The Revision log message can be hidden. It isn't something that needs to be displayed

Servers create/delete - Works now!. However, there was a combo that was still giving me
"unsupported location for server type"error. Please check this one out?- fsn1
- cpx11
- Debian 11
I have some low priority UI issues. Please see if these are things you can take a look at too.
On some of the add/edit forms (Server, Volumes and Network), the
Authored Byis displayed at the bottom outside of a fieldset. In our AWS implementation, we put any additional fields in a collapsed fieldset called "Others". I was wondering if you might want to do the same for your forms. It will help give it a uniform look with the other cloud service providers. See screenshot.Comment #36
baldwinlouie commentedComment #37
erdinchas@baldwinlouie
I've pushed fixes for the things you flagged:
1. SSH Key add error — the form was reading the name from the (unpopulated) entity instead of form state. The custom name/public_key fields now have to be binding correctly via #parents, so creation should be going through.
2. Load Balancer "Revision log message" — hidden on all Hetzner create/edit forms via a shared hideRevisionFields() helper (Hetzner entities aren't actually revisioned, so this widget was just leaking in from the parent base fields).
3. fsn1 + cpx11 + Debian 11 "unsupported location" — realized that it was because Hetzner keeps deprecated server types (like cpx11) and deprecated images (like Debian 11) in the catalog but rejects them on new orders with that misleading error. Both the server-type dropdown and the image dropdown now filter out anything with a deprecated/deprecation flag, and validateForm() mirrors the checks so stale form state can't slip through.
4. "Authored By" outside fieldsets — every Hetzner create/edit form now follows the AWS pattern and renders the meta fields inside a collapsed "Others" fieldset (Server, Volume, Network, Load Balancer, Floating IP, Firewall, SSH Key, Image).
Hopefully ready for another go whenever you have time
Comment #38
baldwinlouie commented@erdinchas Thank you for the updated patch. Sorry, this has taken so long.
I've tested the fixes. The only one I am still having issues with is #3.
I tried fsn1 + cpx11 + Debian 11. The message now says:
Can you please take a look at this?
Comment #39
erdinchasI just created an issue on Hetzner-php-sdk repo.
I am debugging with a workaround, and will need a re-work when the sdk has a fix for it.
I'll push it when it seems to be working.
Comment #40
erdinchasHi,
My MR fails the pipeline because of the exact reason of this issue.
https://www.drupal.org/project/cloud/issues/3594984
Unfortunately, I am unable to create a healthy MR and so won't change the status to 'Needs Review"
Wanted to bring it to your attention.
Cheers.
Comment #41
yas@erdinchas
Thank you for letting us know about it. I've fixed it at #3594984: Fix incorrect route provider return types from ?\Serializable to ?Route so I hope that you can proceed with creating your MR.
HTH
Comment #42
erdinchasThe new lkdevelopment/hetzner-cloud-php-sdk v3.1.0 release maps the deprecated/deprecation fields on server types (issue #151 / PR #152 upstream), so I've bumped the constraint to ^3.1 and reverted our raw-JSON workaround — getServerTypes() is back to the normal SDK call now.
Comment #43
yas@erdinchas
I'm truly sorry for keeping you waiting for so long after you took the time to update the MR. Some personal circumstances delayed my review.
I reviewed it and found a bug. I will update your MR and follow up in my next comment.
Comment #44
yas@erdinchas
I've updated your MR. Could you please review the changes?
Comment #45
orkutmuratyilmazThis MR needs a rebase too:) @erdinchas
Comment #47
ai_yas commented@orkutmuratyilmaz @erdinchas
MR !2348 has been rebased onto the latest
8.x, and the merged-results pipeline passed. Could you please review it?Comment #48
orkutmuratyilmazI've just completed the reviewing.
I'll have some more feature requests about Hetzner, but this issue looks like completed. It does basic CRUD operations for Hetzner services very well:)
@erdinchas, @yas, @baldwinlouie and @ai_yas, thanks for everything you've done:) This process taught me a lot.
Have a wonderful September:)
Comment #51
yasI’ll merge the MR into
8.xand9.x; and close this issue as Fixed.Comment #53
yasComment #55
yas