|

Adding Additional IPv6 Addresses on Hetzner Cloud Servers (Debian 12/13)

Configuring additional IPv6 addresses on Hetzner Cloud servers running Debian 12/13 requires a careful approach to work harmoniously with cloud-init. This guide is an updated version of my previous tutorial, reflecting lessons learned from production deployments and providing a more reliable method that avoids common pitfalls.

What’s changed since the original guide:

  • New approach using post-up hooks instead of multiple static interface definitions
  • Better compatibility with cloud-init and DHCP
  • Avoids “File exists” errors during network operations
  • More production-safe workflow that doesn’t require restarting networking services

Note: These instructions are specifically for Debian 12/13 systems using /etc/network/interfaces. Ubuntu users should refer to Netplan documentation.

Understanding the Challenge

Hetzner Cloud servers use cloud-init to manage the primary network configuration in /etc/network/interfaces.d/50-cloud-init. Each server receives a /64 IPv6 subnet (e.g., 2001:db8:1234:5678::/64), allowing you to use any address within this range.

The challenge is adding additional IPv6 addresses without:

  • Interfering with cloud-init’s management
  • Causing “File exists” errors during network restarts
  • Losing configuration after reboots

The Recommended Method: Post-Up Hooks

The most reliable approach uses post-up hooks with manual mode, which adds IP addresses after the interface is brought up by cloud-init.

Step 1: Check Your Existing Configuration

First, verify your current setup:

cat /etc/network/interfaces.d/50-cloud-init

You should see something like:

auto eth0
iface eth0 inet dhcp

iface eth0 inet6 static
    address 2001:db8:1234:5678::1/64
    dns-nameservers 2a01:4ff:ff00::add:1 2a01:4ff:ff00::add:2
    gateway fe80::1

Note your subnet prefix – you’ll use this for additional addresses.

Step 2: Add IPv6 Addresses Immediately

For immediate availability without waiting for a reboot, add the addresses directly:

sudo ip -6 addr add 2001:db8:1234:5678::2/128 dev eth0
sudo ip -6 addr add 2001:db8:1234:5678::3/128 dev eth0
sudo ip -6 addr add 2001:db8:1234:5678::4/128 dev eth0

Important: Use /128 prefix for additional addresses, not /64.

Verify they’re active:

ip -6 addr show dev eth0
ping6 -c 3 2001:db8:1234:5678::2

Step 3: Make Configuration Persistent

Create a new configuration file that loads after cloud-init:

sudo nano /etc/network/interfaces.d/99-custom

Add the following content (replace with your actual IPv6 subnet):

# Additional IPv6 addresses
iface eth0 inet6 manual
    post-up ip -6 addr add 2001:db8:1234:5678::2/128 dev $IFACE || true
    post-up ip -6 addr add 2001:db8:1234:5678::3/128 dev $IFACE || true
    post-up ip -6 addr add 2001:db8:1234:5678::4/128 dev $IFACE || true
    pre-down ip -6 addr del 2001:db8:1234:5678::2/128 dev $IFACE || true
    pre-down ip -6 addr del 2001:db8:1234:5678::3/128 dev $IFACE || true
    pre-down ip -6 addr del 2001:db8:1234:5678::4/128 dev $IFACE || true

Key points about this configuration:

  • iface eth0 inet6 manual – Prevents interference with cloud-init’s static configuration
  • post-up – Adds addresses after interface comes up
  • pre-down – Removes addresses before interface goes down
  • || true – Prevents errors if address already exists (important for reliability)
  • $IFACE – Automatically expands to the interface name

Step 4: Verify Configuration

Check that your file is syntactically correct:

cat /etc/network/interfaces.d/99-custom

Verify your addresses are active:

ip -6 addr show dev eth0

You should see all your addresses listed with “scope global”.

Step 5: Test Persistence (Optional)

The safest way to test that your configuration survives a reboot is to actually reboot during a maintenance window:

sudo reboot

After reboot, verify:

ip -6 addr show dev eth0

All your additional IPv6 addresses should be present automatically.

Why Not Restart Networking?

You might be tempted to run systemctl restart networking to apply changes, but this is not recommended on production systems because:

  1. It can drop active SSH connections
  2. It may cause “File exists” errors with DHCP routes (cosmetic but alarming)
  3. It’s unnecessary – adding IPs directly works immediately

The workflow of “add now + configure for persistence” is safer and more reliable.

Alternative: Subnet-Wide Accessibility (Advanced)

If you want your server to respond to all IP addresses within your /64 subnet without explicitly configuring each one, add these lines to your 99-custom file:

iface eth0 inet6 manual
    post-up ip -6 route add local 2001:db8:1234:5678::/64 dev eth0 || true
    pre-down ip -6 route del local 2001:db8:1234:5678::/64 dev eth0 || true

This creates a local route for the entire subnet. Use this carefully and only if you understand the implications for your security and firewall rules.

Troubleshooting

Q: My addresses disappear after reboot

  • A: Check that /etc/network/interfaces.d/99-custom exists and has correct syntax
  • Verify the file is readable: ls -la /etc/network/interfaces.d/

Q: I get “RTNETLINK answers: File exists” errors

  • A: This is normal when restarting networking on a live system. The || true in the config prevents this from being a problem. Your network is working fine.

Q: Can I use a different numbering scheme?

  • A: Yes! You can use any address in your /64 subnet. Examples:
    • Sequential: ::2, ::3, ::4
    • Organized: ::100, ::200, ::300
    • Meaningful: ::web, ::mail, ::db
    • Random: ::1a2b:3c4d, ::cafe:babe, ::dead:beef

Q: Should I modify the 50-cloud-init file?

  • A: No! Cloud-init regenerates this file. Always use separate numbered files like 99-custom.

Adding More Addresses Later

To add additional IPv6 addresses in the future:

  1. Add immediately: sudo ip -6 addr add 2001:db8:1234:5678::5/128 dev eth0
  2. Edit /etc/network/interfaces.d/99-custom and add another post-up line
  3. Test during next reboot

Conclusion

This method provides a production-safe way to add multiple IPv6 addresses to Debian 12/13 systems on Hetzner Cloud while respecting cloud-init’s management. The key advantages are:

  • Non-invasive to existing configuration
  • Works reliably with DHCP
  • Survives reboots automatically
  • Easy to maintain and extend
  • No service restarts required

By using post-up hooks instead of multiple static interface definitions, you avoid conflicts with cloud-init and ensure your configuration remains stable across system updates and reboots.

Similar Posts