Skip to content

Allow servers without a public IPv6 address - #38

Merged
dbackeus merged 1 commit into
masterfrom
optional-server-ipv6
Oct 13, 2026
Merged

dbackeus merged 1 commit into
masterfrom
optional-server-ipv6

Conversation

@dbackeus

Copy link
Copy Markdown
Contributor

Hetzner Cloud servers can have their public IPv6 disabled, in which case the API returns "ipv6": null under public_net. "Refresh servers" then failed with a 500:

NoMethodError (undefined method 'fetch' for nil):
app/api/hetzner_cloud.rb:23:in 'block (2 levels) in HetznerCloud.sync_to_activerecord'

Changes

  • Migration making servers.ipv6 nullable
  • Drop validates_presence_of :ipv6 on Server
  • Read the address with dig("public_net", "ipv6", "ip") in HetznerCloud.sync_to_activerecord
  • Cloud servers fixture: worker-2 now has "ipv6": null, and the sync request spec asserts it ends up with ipv6: nil (the spec fails with the original NoMethodError without the fix)

ipv6 isn't read anywhere else in the app, so nothing downstream depends on it being present.

🤖 Generated with Claude Code

Hetzner Cloud servers can have their public IPv6 disabled, in which case
the API returns `"ipv6": null` under `public_net`. The sync crashed with
NoMethodError when chaining `fetch("ip")` on that nil, failing the whole
"Refresh servers" action.

Make `servers.ipv6` nullable, drop its presence validation and use `dig`
when reading it from the Hetzner Cloud payload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread app/api/hetzner_cloud.rb
@@ -20,7 +20,7 @@ def self.sync_to_activerecord
data_center: server_payload.fetch("location").fetch("name"),
hetzner_vswitch_id: nil,
ip: server_payload.fetch("public_net").fetch("ipv4").fetch("ip"),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same crash still exists for the IPv4 side: Hetzner Cloud also lets you create servers with public IPv4 disabled (IPv6-only, which is cheaper). The API then returns "ipv4": null, so .fetch("ipv4").fetch("ip") raises NoMethodError on nil and "Refresh servers" fails for every API key. The fix here only covers one of the two symmetric cases. Fully supporting IPv6-only servers would also need ip to be nullable and SSH/bootstrap to fall back to ipv6. If that is out of scope, consider at least raising a clear error that names the server instead of crashing the whole sync.

@@ -0,0 +1,5 @@
class ChangeServersIpv6Nullable < ActiveRecord::Migration[8.0]
def change
change_column_null :servers, :ipv6, true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: once any server has been synced with ipv6 = NULL, rolling back this migration fails with PG::NotNullViolation, because change_column_null reverses to NOT NULL without a default. That is probably fine, but you could pass a default for rollback (change_column_null :servers, :ipv6, true, "") or make it explicitly irreversible.

@dbackeus
dbackeus merged commit 76f49e1 into master Oct 13, 2026
3 checks passed
@dbackeus
dbackeus deleted the optional-server-ipv6 branch October 13, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant