Custom/change IP address #504

Closed
opened 2025-12-29 02:19:10 +01:00 by adam · 25 comments
Owner

Originally created by @cg31 on GitHub (May 11, 2023).

Headscale is assigning IPs to machines in registering order as x.x.x.1 ... x.x.x.2 ... x.x.x.N.

Is it possible to add a command to assign/change the IP associated with a machine?

The reason is when using script to do automatic job, we can simply use fixed IPs directly.

Originally created by @cg31 on GitHub (May 11, 2023). Headscale is assigning IPs to machines in registering order as x.x.x.1 ... x.x.x.2 ... x.x.x.N. Is it possible to add a command to assign/change the IP associated with a machine? The reason is when using script to do automatic job, we can simply use fixed IPs directly.
adam added the enhancementstaleneeds design doc labels 2025-12-29 02:19:10 +01:00
adam closed this issue 2025-12-29 02:19:11 +01:00
Author
Owner

@cg31 commented on GitHub (May 11, 2023):

Someone already mentioned it in https://github.com/juanfont/headscale/pull/983#issuecomment-1340170494

@cg31 commented on GitHub (May 11, 2023): Someone already mentioned it in https://github.com/juanfont/headscale/pull/983#issuecomment-1340170494
Author
Owner

@san3Xian commented on GitHub (May 17, 2023):

+1 ,now I can only specify the ip of the machine by modifying the ip_addresses column in the machines table via sqlite3 db.sqlite, which is a poor experience.

@san3Xian commented on GitHub (May 17, 2023): +1 ,now I can only specify the ip of the machine by modifying the ip_addresses column in the machines table via `sqlite3 db.sqlite`, which is a poor experience.
Author
Owner

@rjmalagon commented on GitHub (Jul 3, 2023):

+1 ,now I can only specify the ip of the machine by modifying the ip_addresses column in the machines table via sqlite3 db.sqlite, which is a poor experience.
Thanks for the tip.
It works enough, although not user-friendly.

@rjmalagon commented on GitHub (Jul 3, 2023): > +1 ,now I can only specify the ip of the machine by modifying the ip_addresses column in the machines table via `sqlite3 db.sqlite`, which is a poor experience. Thanks for the tip. It works enough, although not user-friendly.
Author
Owner

@gbraad commented on GitHub (Jul 4, 2023):

On the headscale coordination server

$ sqlite3 /var/lib/headscale/db.sqlite
SELECT id, hostname, ip_addresses FROM machines;
UPDATE machines SET ip_addresses = "100.64.0.3" WHERE id=3;

On the tailscale client

$ tailscale down
$ tailscale up
@gbraad commented on GitHub (Jul 4, 2023): On the headscale coordination server ``` $ sqlite3 /var/lib/headscale/db.sqlite SELECT id, hostname, ip_addresses FROM machines; UPDATE machines SET ip_addresses = "100.64.0.3" WHERE id=3; ``` On the tailscale client ``` $ tailscale down $ tailscale up ```
Author
Owner

@rjmalagon commented on GitHub (Jul 4, 2023):

Im my case, I used a more blunt approach to replace my custom tailnet IPV6 range to the standard one.
SELECT ip_addresses FROM machines;
UPDATE machines SET ip_addresses = replace( ip_addresses, 'fdad:1cd0:8088', 'fd7a:115c:a1e0') ;

@rjmalagon commented on GitHub (Jul 4, 2023): Im my case, I used a more blunt approach to replace my custom tailnet IPV6 range to the standard one. `SELECT ip_addresses FROM machines;` `UPDATE machines SET ip_addresses = replace( ip_addresses, 'fdad:1cd0:8088', 'fd7a:115c:a1e0') ;`
Author
Owner

@kradalby commented on GitHub (Oct 2, 2023):

While this was closed with a standardised message, I do not believe we will accept this feature for the time being, it does require more thought than it might seem on the surface and we are currently not willing to open this can of worms.

People can as mentioned use DB modification if needed, but you should rather base your network of not depending on the assigned IPs.

@kradalby commented on GitHub (Oct 2, 2023): While this was closed with a standardised message, I do not believe we will accept this feature for the time being, it does require more thought than it might seem on the surface and we are currently not willing to open this can of worms. People can as mentioned use DB modification if needed, but you should rather base your network of not depending on the assigned IPs.
Author
Owner

@github-actions[bot] commented on GitHub (Dec 31, 2023):

This issue is stale because it has been open for 90 days with no activity.

@github-actions[bot] commented on GitHub (Dec 31, 2023): This issue is stale because it has been open for 90 days with no activity.
Author
Owner

@github-actions[bot] commented on GitHub (Jan 7, 2024):

This issue was closed because it has been inactive for 14 days since being marked as stale.

@github-actions[bot] commented on GitHub (Jan 7, 2024): This issue was closed because it has been inactive for 14 days since being marked as stale.
Author
Owner

@aapeliv commented on GitHub (Sep 15, 2024):

For anyone trying to backfill IPv6 addresses still on v0.22.3, this works:

update machines set ip_addresses = '100.64.0.1,fd7a:115c:a1e0::1' where ip_addresses = '100.64.0.1';
@aapeliv commented on GitHub (Sep 15, 2024): For anyone trying to backfill IPv6 addresses still on v0.22.3, this works: ```sql update machines set ip_addresses = '100.64.0.1,fd7a:115c:a1e0::1' where ip_addresses = '100.64.0.1'; ```
Author
Owner

@surepy commented on GitHub (Oct 31, 2024):

newer versions changed the table schema so it's now

update nodes set ipv4 = 'desired-ipv4' where ipv4 = 'current-ipv4';

for ipv4

update nodes set ipv6 = 'desired-ipv6' where (...);
@surepy commented on GitHub (Oct 31, 2024): newer versions changed the table schema so it's now ```sql update nodes set ipv4 = 'desired-ipv4' where ipv4 = 'current-ipv4'; ``` for ipv4 ```sql update nodes set ipv6 = 'desired-ipv6' where (...); ```
Author
Owner

@ArcticLampyrid commented on GitHub (Nov 3, 2024):

you should rather base your network of not depending on the assigned IPs.

DNS is not always reliable. In my opinion, manually providing a custom IP for infrastructure will be beneficial for maintenance work.

@ArcticLampyrid commented on GitHub (Nov 3, 2024): > you should rather base your network of not depending on the assigned IPs. DNS is not always reliable. In my opinion, manually providing a custom IP for infrastructure will be beneficial for maintenance work.
Author
Owner

@Geofferey commented on GitHub (Dec 3, 2024):

I know the issue is closed, however if you still Google, you'll probably land here.

Here's a quick script to assist those with changing IPs of nodes in headscale v0.23.0.

If you're like me and not SNATing or OCD I hope you'll appreciate this.

https://gist.github.com/Geofferey/e3c49d195a01229e61b878f4530bd052

wget https://gist.githubusercontent.com/Geofferey/e3c49d195a01229e61b878f4530bd052/raw/d0711e21ba645798aa438228725f3a884686ff3a/hschip.sh

sudo cp hschip.sh /usr/bin/hschip

sudo chmod +x /usr/bin/hschip

sudo hschip nodname 100.64.0.2

You'll need sudo apt-get install sqlite3 ofc

The script has several sanity checks to determine if you're on headscale version v0.23.0, the node exist, IP is valid + tailscale CIDR: 100.64.0.0/10 and that the IP is not actively being used by another node... Sorry, no v6 support 😭😭😭

I couldn't have engineers screwing with the DB by hand and there may be a requirement to ensure the IP is controlled since we are full routing across non tailscale nets. We are not using v6 support in our env so I wasn't gonna go the extra mile.

@Geofferey commented on GitHub (Dec 3, 2024): I know the issue is closed, however if you still Google, you'll probably land here. Here's a quick script to assist those with changing IPs of nodes in headscale `v0.23.0`. If you're like me and not SNATing or OCD I hope you'll appreciate this. https://gist.github.com/Geofferey/e3c49d195a01229e61b878f4530bd052 `wget https://gist.githubusercontent.com/Geofferey/e3c49d195a01229e61b878f4530bd052/raw/d0711e21ba645798aa438228725f3a884686ff3a/hschip.sh` `sudo cp hschip.sh /usr/bin/hschip` `sudo chmod +x /usr/bin/hschip` `sudo hschip nodname 100.64.0.2` You'll need `sudo apt-get install sqlite3` ofc The script has several sanity checks to determine if you're on `headscale version` `v0.23.0`, the node exist, IP is valid + tailscale CIDR: `100.64.0.0/10` and that the IP is not actively being used by another node... Sorry, no v6 support 😭😭😭 I couldn't have engineers screwing with the DB by hand and there may be a requirement to ensure the IP is controlled since we are full routing across non tailscale nets. We are not using v6 support in our env so I wasn't gonna go the extra mile.
Author
Owner

@TheWebMachine commented on GitHub (Dec 9, 2024):

Here's a quick script to assist those with changing IPs of nodes in headscale v0.23.0.

Perfect timing! I just setup headscale for the first time tonight and wanted to move my exit node to .1!

This worked perfectly. Thank you!

@TheWebMachine commented on GitHub (Dec 9, 2024): > Here's a quick script to assist those with changing IPs of nodes in headscale `v0.23.0`. Perfect timing! I just setup headscale for the first time tonight and wanted to move my exit node to .1! This worked perfectly. Thank you!
Author
Owner

@ngaro commented on GitHub (Jan 9, 2025):

Seeing that all PR's with code to implement this feature contain comments really similar to the "I do not believe we will accept this feature for the time being, it does require more thought than it might seem on the surface and we are currently not willing to open this can of worms." I assume that changing IP's will cause unexpected/buggy behavior that we are missing...

For now the only things I can think off are these things:
(Note that this is just what I expect can happen, I didn't examine the inner working of Headscale and Tailscale enough to be sure)

  • Headscale will need to restart and during that time new connections will not be possible between systems that didn't change IP's and didn't communicate for a while (I assume the Tailscale client caches info it receives).
    This shouldn't be a large issue and should just feel like laggy behavior for a few seconds
  • The system with the changed IP will not be accessible until it's tailscale client is restarted (or maybe until it's cache expires if it also caches info about itself)
  • Other systems cannot connect to the system with the new IP until they restart their tailscale client (or until their cache expires)

Is this behavior that you noticed and are there also other problems ?

@ngaro commented on GitHub (Jan 9, 2025): Seeing that all PR's with code to implement this feature contain comments really similar to the _"I do not believe we will accept this feature for the time being, it does require more thought than it might seem on the surface and we are currently not willing to open this can of worms."_ I assume that changing IP's will cause unexpected/buggy behavior that we are missing... For now the only things I can think off are these things:<br>_(Note that this is just what I expect can happen, I didn't examine the inner working of Headscale and Tailscale enough to be sure)_ - Headscale will need to restart and during that time new connections will not be possible between systems that didn't change IP's and didn't communicate for a while _(I assume the Tailscale client caches info it receives)_.<br> This shouldn't be a large issue and should just feel like laggy behavior for a few seconds - The system with the changed IP will not be accessible until it's tailscale client is restarted _(or maybe until it's cache expires if it also caches info about itself)_ - Other systems cannot connect to the system with the new IP until they restart their tailscale client (_or until their cache expires)_ Is this behavior that you noticed and are there also other problems ?
Author
Owner

@Oni commented on GitHub (Jan 24, 2025):

(Sorry for joining the discussion late)

People can as mentioned use DB modification if needed, but you should rather base your network of not depending on the assigned IPs.

You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course):

dns:
  # List of DNS servers to expose to clients.
  nameservers:
    global:
      - <tailscale machine here>

What about an approach similar to DHCP servers? You basically reserve ips to certain nodes.

dhcp:
  foo.node.myhs.net:
    ipv4:
      - <ip here in headscale range>

After headscale reboots, when a node tries to connect, the server checks if it has a reserved ip. If not, it assigns a non-reserved ip.

This approach would cut down the complexity of dealing with ip changes, but would still provide the advantage of controlling ips of nodes.

@Oni commented on GitHub (Jan 24, 2025): (Sorry for joining the discussion late) > People can as mentioned use DB modification if needed, but you should rather base your network of not depending on the assigned IPs. You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course): ``` dns: # List of DNS servers to expose to clients. nameservers: global: - <tailscale machine here> ``` What about an approach similar to DHCP servers? You basically reserve ips to certain nodes. ``` dhcp: foo.node.myhs.net: ipv4: - <ip here in headscale range> ``` After headscale reboots, when a node tries to connect, the server checks if it has a reserved ip. If not, it assigns a non-reserved ip. This approach would cut down the complexity of dealing with ip changes, but would still provide the advantage of controlling ips of nodes.
Author
Owner

@jamesandariese commented on GitHub (Mar 12, 2025):

(Sorry for joining the discussion late)

People can as mentioned use DB modification if needed, but you should rather base your network of not depending on the assigned IPs.

You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course):

dns:
  # List of DNS servers to expose to clients.
  nameservers:
    global:
      - <tailscale machine here>

This is exactly where I want it. I've considered using a subnet route for this instead and that's viable but more complex and requires accepting subnet routes to access DNS which I'd rather avoid if possible.

in addition to DNS, I also have a split horizon setup for my http ingress and since I can't use CNAMEs for that without a third DNS server, I have similar options.

to avoid excessive complexity, I'm going to be updating the env var for the DNS IP and likely using the external json file DNS support for the ingress.

What about an approach similar to DHCP servers? You basically reserve ips to certain nodes.

dhcp:
  foo.node.myhs.net:
    ipv4:
      - <ip here in headscale range>

After headscale reboots, when a node tries to connect, the server checks if it has a reserved ip. If not, it assigns a non-reserved ip.

This approach would cut down the complexity of dealing with ip changes, but would still provide the advantage of controlling ips of nodes.

This would solve my problems today and seems likely to provide a seamless path forward. you have all 0 of my votes :D

@jamesandariese commented on GitHub (Mar 12, 2025): > (Sorry for joining the discussion late) > > > People can as mentioned use DB modification if needed, but you should rather base your network of not depending on the assigned IPs. > > You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course): > > ``` > dns: > # List of DNS servers to expose to clients. > nameservers: > global: > - <tailscale machine here> > ``` This is exactly where I want it. I've considered using a subnet route for this instead and that's viable but more complex and requires accepting subnet routes to access DNS which I'd rather avoid if possible. in addition to DNS, I also have a split horizon setup for my http ingress and since I can't use CNAMEs for that without a third DNS server, I have similar options. to avoid excessive complexity, I'm going to be updating the env var for the DNS IP and likely using the external json file DNS support for the ingress. > > What about an approach similar to DHCP servers? You basically reserve ips to certain nodes. > > ``` > dhcp: > foo.node.myhs.net: > ipv4: > - <ip here in headscale range> > ``` > > After headscale reboots, when a node tries to connect, the server checks if it has a reserved ip. If not, it assigns a non-reserved ip. > > This approach would cut down the complexity of dealing with ip changes, but would still provide the advantage of controlling ips of nodes. This would solve my problems today and seems likely to provide a seamless path forward. you have all 0 of my votes :D
Author
Owner

@kradalby commented on GitHub (Mar 13, 2025):

You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course):

The IPs are fixed and will never change, you just can choose them. I think this is a non-issue.

@kradalby commented on GitHub (Mar 13, 2025): > You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course): The IPs are fixed and will never change, you just can choose them. I think this is a non-issue.
Author
Owner

@Oni commented on GitHub (Mar 13, 2025):

You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course):

The IPs are fixed and will never change, you just can choose them. I think this is a non-issue.

That's a great info to hear: I've never noticed this!

@Oni commented on GitHub (Mar 13, 2025): > > You need fixed IPS if the DNS server is hosted inside the tailnet, headscale requires a fixed ip in (of course): > > The IPs are fixed and will never change, you just can choose them. I think this is a non-issue. That's a great info to hear: I've never noticed this!
Author
Owner

@almereyda commented on GitHub (Mar 15, 2025):

I think this would read

- The IPs are fixed and will never change, you just can choose them. I think this is a non-issue.
+ The IPs are fixed and will never change, you just can't choose them. I think this is a non-issue.

if I'm not mistaken, guided by the last sentence of the comment. The closed status of the issue adds another data point to this interpretation.

@almereyda commented on GitHub (Mar 15, 2025): I think this would read ```diff - The IPs are fixed and will never change, you just can choose them. I think this is a non-issue. + The IPs are fixed and will never change, you just can't choose them. I think this is a non-issue. ``` if I'm not mistaken, guided by the last sentence of the comment. The closed status of the issue adds another data point to this interpretation.
Author
Owner

@MulverineX commented on GitHub (May 3, 2025):

@Geofferey could you please update your script for headscale v25? I just confirmed that it works, I just had to change the /var/lib/headscale path to my headscale config path

@MulverineX commented on GitHub (May 3, 2025): ~~@Geofferey could you please update your script for headscale v25?~~ I just confirmed that it works, I just had to change the `/var/lib/headscale` path to my headscale config path
Author
Owner

@mtmn commented on GitHub (Oct 12, 2025):

Apologies for necrobumping, but schema has changed and on 0.26.1 the approach from @gbraad needs to be reformatted a bit.

sqlite3 /var/lib/headscale/db.sqlite
SELECT id, hostname FROM nodes;
UPDATE nodes 
SET ipv6 = '115c:a1e0::4', 
    ipv4 = '100.32.0.4' 
WHERE id = 4;
@mtmn commented on GitHub (Oct 12, 2025): Apologies for necrobumping, but schema has changed and on `0.26.1` the approach from @gbraad needs to be reformatted a bit. ```bash sqlite3 /var/lib/headscale/db.sqlite ``` ```sql SELECT id, hostname FROM nodes; ``` ```sql UPDATE nodes SET ipv6 = '115c:a1e0::4', ipv4 = '100.32.0.4' WHERE id = 4; ```
Author
Owner

@burzaca commented on GitHub (Oct 28, 2025):

In my case I had to change the id of the peer as well:

sqlite3 /var/lib/headscale/db.sqlite
SELECT id, hostname FROM nodes;
UPDATE nodes 
SET ipv4 = '100.64.0.3' 
WHERE id = 9;
UPDATE nodes SET id='3' WHERE id = 9;

Of course, the id was unused, but when I changed only the ipv4 it would not take effect

@burzaca commented on GitHub (Oct 28, 2025): In my case I had to change the id of the peer as well: ``` sqlite3 /var/lib/headscale/db.sqlite ``` ``` SELECT id, hostname FROM nodes; ``` ``` UPDATE nodes SET ipv4 = '100.64.0.3' WHERE id = 9; ``` ``` UPDATE nodes SET id='3' WHERE id = 9; ``` Of course, the id was unused, but when I changed only the ipv4 it would not take effect
Author
Owner

@Geofferey commented on GitHub (Oct 29, 2025):

@burzaca Does this mean the hschip script that I wrote is no longer compatible with recent schema changes?

@Geofferey commented on GitHub (Oct 29, 2025): @burzaca Does this mean the `hschip` script that I wrote is no longer compatible with recent schema changes?
Author
Owner

@reinob commented on GitHub (Nov 19, 2025):

I just swapped the ipv4 and ipv6 of two nodes, and it worked instantly without having to restart the clients (they pick up the change almsot immediately) and without having to change the id. (I did stop headscale while editing the db and started it again).

The script "hschip" would appear to work, as it already uses the nodes table with { hostname,ipv4,ipv6 }. I didn't use it though.

@reinob commented on GitHub (Nov 19, 2025): I just swapped the ipv4 and ipv6 of two nodes, and it worked instantly without having to restart the clients (they pick up the change almsot immediately) *and* without having to change the id. (I did stop headscale while editing the db and started it again). The script "hschip" would appear to work, as it already uses the nodes table with { hostname,ipv4,ipv6 }. I didn't use it though.
Author
Owner

@TornaxO7 commented on GitHub (Dec 1, 2025):

I'd be interested in this feature because I selfhost a DNS server in my tailscale network.

@TornaxO7 commented on GitHub (Dec 1, 2025): I'd be interested in this feature because I selfhost a DNS server in my tailscale network.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#504