Skip to content

Instantly share code, notes, and snippets.

@jesserockz
Last active December 4, 2025 20:32
Show Gist options
  • Select an option

  • Save jesserockz/f6e7dc277c8f850dd3d353d41138bb4b to your computer and use it in GitHub Desktop.

Select an option

Save jesserockz/f6e7dc277c8f850dd3d353d41138bb4b to your computer and use it in GitHub Desktop.
Tailscale script for GL-iNet Beryl AX side switch

Tailscale script for GL-iNet Beryl AX side switch

This script allows you to toggle tailscale settings using the physical side switch on the GL-iNet Beryl AX.

To install:

  1. Copy the below file to /etc/gl-switch.d/tailscale.sh and replace the exit_node_ip value with your exit node ip address.

  2. Make sure to set the execute bit on the new file: chmod +x /etc/gl-switch.d/tailscale.sh

  3. Depending on version:

    4.8.0 and above:
    Copy/write the switch-button file below to /etc/config/switch-button

    4.7.4 and below:
    Go to System -> Toggle Button Settings and choose Tailscale (On/Off) in the dropdown and Apply

# /etc/config/switch-button
config main
option func 'tailscale'
#!/bin/sh
# /etc/gl-switch.d/tailscale.sh
action=$1
if [ "$action" = "on" ];then
enabled=true
lan_enabled=false
wan_enabled=true
exit_node_ip="{{ REPLACE WITH YOUR EXIT NODE IP }}"
elif [ "$action" = "off" ];then
enabled=true
lan_enabled=false
wan_enabled=true
exit_node_ip=""
else
echo "Usage: $0 [on|off]"
exit 1
fi
curl -H 'glinet: 1' -s -k http://127.0.0.1/rpc -d "{\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"tailscale\",\"set_config\",{\"enabled\":$enabled,\"lan_enabled\":$lan_enabled,\"wan_enabled\":$wan_enabled,\"exit_node_ip\":\"$exit_node_ip\"}],\"id\":1}"
sleep 5
@rdgerken
Copy link

Thanks for posting this! I modified it to turn Tailscale completely off or on (with an exit node). A few things:

  • For some reason, the GUI hides the WAN/LAN enable setting when custom exit node is enabled. This seems like a bug in the GUI maybe? I notice that if they are enabled/applied, and THEN you configure a custom exit node, they would remain on the last configured state - so I don't know why the GUI is hiding those options. I think a long time ago, when using an exit node, the WAN/LAN settings did not apply on the Tailscale side of things, but to my knowledge, the functionality to expose at least the LAN side optionally when using an exit node was added to the Tailscale feature set
  • Be sure to set execute rights on the tailscale.sh file after copying to the router

@lucfig
Copy link

lucfig commented Mar 6, 2025

Thanks for posting this! I modified it to turn Tailscale completely off or on (with an exit node). A few things:

  • For some reason, the GUI hides the WAN/LAN enable setting when custom exit node is enabled. This seems like a bug in the GUI maybe? I notice that if they are enabled/applied, and THEN you configure a custom exit node, they would remain on the last configured state - so I don't know why the GUI is hiding those options. I think a long time ago, when using an exit node, the WAN/LAN settings did not apply on the Tailscale side of things, but to my knowledge, the functionality to expose at least the LAN side optionally when using an exit node was added to the Tailscale feature set
  • Be sure to set execute rights on the tailscale.sh file after copying to the router

Mind sharing your script?

@MrKaiba
Copy link

MrKaiba commented Mar 18, 2025

I tried using this on the Slate-AXT 1800 but didnt seem to work, should this work across all GLiNET units with the toggle switch or do they have different saving of files for the toggle switch?

@DougS026
Copy link

DougS026 commented May 17, 2025

Mind sharing your script?

Here is what I'm using, and I believe it addresses @lucfig's question. It differs from the original by: a) It turns Tailscale completely off or on (with an exit node) as @rdgerken mentions, and b) This script switches what side is on and off from the factory. Below will turn off Tailscale when the switch is to the left and turn it on when the switch is to the right (the side with the dot). This makes more sense to me.

#!/bin/sh

action=$1

if [ "$action" = "off" ]; then  # Switched the logic so "off" now enables Tailscale
  enabled=true
  lan_enabled=false
  wan_enabled=true
  exit_node_ip="{{ REPLACE WITH YOUR EXIT NODE IP }}"
elif [ "$action" = "on" ]; then  # "On" now fully disables Tailscale
  enabled=false
  lan_enabled=false
  wan_enabled=false
  exit_node_ip=""
else
  echo "Usage: $0 [on|off]"
  exit 1
fi

curl -H 'glinet: 1' -s -k http://127.0.0.1/rpc -d "{\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"tailscale\",\"set_config\",{\"enabled\":$enabled,\"lan_enabled\":$lan_enabled,\"wan_enabled\":$wan_enabled,\"exit_node_ip\":\"$exit_node_ip\"}],\"id\":1}"

sleep 5

As noted, be sure to set execute rights on the tailscale.sh file after copying to the router.

@smolz
Copy link

smolz commented Jul 2, 2025

Any idea if this would work for the Slate 7?

@DougS026
Copy link

DougS026 commented Jul 2, 2025

Sorry, no idea.

@jesserockz
Copy link
Author

I never expected people to actually find and use this 🙈
I more put it here for my own reference for when an upgrade wiped it out..haha

Good to see it has been helpful though

@smolz
Copy link

smolz commented Jul 10, 2025

Used @DougS026 script and it works a treat.

@DougS026
Copy link

@smolz, I assume you mean on the Slate 7? Thanks for letting us know!

@smolz
Copy link

smolz commented Jul 10, 2025

@smolz, I assume you mean on the Slate 7? Thanks for letting us know!

yes

@DougS026
Copy link

Anyone upgrade their Beryl to the latest V4.8.0? I did and they took away the ability to program the side switch. I had to downgrade to the previous version 4.7.4 to get the script working again. Any ideas how to get it working on 4.8.0?

@Mk2rocco
Copy link

Mk2rocco commented Sep 6, 2025

Anyone upgrade their Beryl to the latest V4.8.0? I did and they took away the ability to program the side switch. I had to downgrade to the previous version 4.7.4 to get the script working again. Any ideas how to get it working on 4.8.0?

Interested in this as well. I tried to make it work with the latest firmware and failed.

@jesserockz
Copy link
Author

I have figured it out and updated the instructions and a new file contents.
Basically you cannot choose tailscale from the button GUI anymore as they must have added some validation, but you can manually write that config file and it works just fine.

@DougS026
Copy link

DougS026 commented Oct 2, 2025

Thanks!

@Bl4cksus
Copy link

Bl4cksus commented Oct 7, 2025

Incredible this exists - thank you!
Like @smolz I also have a Slate 7 / GL-BE3600 and used the script by @DougS026 to reverse the switch and toggle tailscale. I also find this button behaviour more naturally.

However, this does not change the behaviour of the display of the device. What is now "on" will show as "off" on the screen. This can be solved by editing
/usr/bin/screen_disp_switch

in the if statement at the bottom, just change "$action" = "on" to "$action" = "off"
Maybe not the cleanest way but it does the trick. You can also change the *) Message to be "Tailscale" instead of "Toggle Button", so it will say Tailscale on the display. With that, my full /usr/bin/screen_disp_switch on 4.8.1 looks like this:

#!/bin/sh

action="$1"
func=$(uci -q get switch-button.@main[0].func)
sub_func=$(uci -q get switch-button.@main[0].sub_func)
case "$func" in
    "repeater")
        msg="Repeater"
        ;;
    "wifi")
        if [ "$sub_func" = "main_wifi" ];then
            msg="Main Wi-Fi"
        elif [ "$sub_func" = "guest_wifi" ];then
            msg="Guest Wi-Fi"
        else
            msg="Toggle Button"
        fi
        ;;
    "vpn")
        msg="VPN"
        ;;
    "tor")
        msg="Tor"
        ;;
    "adguardhome")
        msg="AdGuard Home"
        ;;
    "led")
        msg="LED"
        ;;
    *)
        msg="Tailscale"
        ;;
esac

if [ "$action" = "off" ]; then
    ubus call gl_screen set "{\"method\": \"switch\", \"params\": { \"enable\": true, \"mode\": \"$msg\", \"sub_func\": \"$sub_func\"}}"
else
    ubus call gl_screen set "{\"method\": \"switch\", \"params\": { \"enable\": false, \"mode\": \"$msg\", \"sub_func\": \"$sub_func\"}}"
fi

@ghzgod
Copy link

ghzgod commented Oct 22, 2025

Modified the script so that it updates the exit node IP based on the hostname of the exit node (so its dynamic). Additionally, I'm not sure why other scripts enable the WAN boolean, but I have that disabled and everything still works.

#!/bin/sh
# /etc/gl-switch.d/tailscale.sh

EXIT_NODE_NAME="MY-HOME-ROUTER-NODE-NAME"

action=$1

if [ "$action" = "off" ];then
  enabled=true
  lan_enabled=false
  wan_enabled=false
  exit_node_ip=$(tailscale status | awk "/$EXIT_NODE_NAME / && /exit node/ {print \$1}")
elif [ "$action" = "on" ];then
  enabled=true
  lan_enabled=false
  wan_enabled=false
  exit_node_ip=""
else
  echo "Usage: $0 [on|off]"
  exit 1
fi

curl -H  'glinet: 1' -s -k http://127.0.0.1/rpc -d "{\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"tailscale\",\"set_config\",{\"enabled\":$enabled,\"lan_enabled\":$lan_enabled,\"wan_enabled\":$wan_enabled,\"exit_node_ip\":\"$exit_node_ip\"}],\"id\":1}"

sleep 5

@ghzgod
Copy link

ghzgod commented Oct 22, 2025

New update.

If you want a kill switch while your exit node is enabled (and the exit node drops connectivity) then try this at your risk. I haven't had a chance to test thoroughly but it just adds a rule and removes that rule to block WAN connectivity if exit node drops and button is toggled in the on state. If button is turned off...(to the left) then it disables killswitch, and exit node, but keeps tailscale up.

Feedback is welcome/appreciated.

#!/bin/sh
# /etc/gl-switch.d/tailscale.sh

EXIT_NODE_NAME="MY-HOME-ROUTER-NODE-NAME"
KILL_SWITCH_ENABLED=true

action=$1

enable_kill_switch() {
  nft list chain inet fw4 forward_lan | grep -q "iifname \"br-lan\" oifname \"eth0\" counter.*drop"
  if [ $? -ne 0 ]; then
    nft insert rule inet fw4 forward_lan iifname br-lan oifname eth0 counter drop comment "tailscale-kill-switch"
  fi
}

disable_kill_switch() {
  nft -a list chain inet fw4 forward_lan | grep "tailscale-kill-switch" | awk '{print $NF}' | while read handle; do
    nft delete rule inet fw4 forward_lan handle $handle
  done
}

if [ "$action" = "off" ];then
  enabled=true
  lan_enabled=false
  wan_enabled=false
  exit_node_ip=$(tailscale status | awk "/$EXIT_NODE_NAME / {print \$1; exit}")
  
  curl -H  'glinet: 1' -s -k http://127.0.0.1/rpc -d "{\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"tailscale\",\"set_config\",{\"enabled\":$enabled,\"lan_enabled\":$lan_enabled,\"wan_enabled\":$wan_enabled,\"exit_node_ip\":\"$exit_node_ip\"}],\"id\":1}"
  
  sleep 6
  
  if [ "$KILL_SWITCH_ENABLED" = "true" ] && [ -n "$exit_node_ip" ]; then
    enable_kill_switch
  fi
elif [ "$action" = "on" ];then
  enabled=true
  lan_enabled=false
  wan_enabled=false
  exit_node_ip=""
  
  disable_kill_switch
  
  curl -H  'glinet: 1' -s -k http://127.0.0.1/rpc -d "{\"jsonrpc\":\"2.0\",\"method\":\"call\",\"params\":[\"\",\"tailscale\",\"set_config\",{\"enabled\":$enabled,\"lan_enabled\":$lan_enabled,\"wan_enabled\":$wan_enabled,\"exit_node_ip\":\"$exit_node_ip\"}],\"id\":1}"
else
  echo "Usage: $0 [on|off]"
  exit 1
fi

@DougS026
Copy link

Has anyone tried using the side switch to toggle between 2 different exit nodes? I gave it a quick go with the older firmware version but didn't get it fully working before having to turn attention to a different project.

@ghzgod do you think it would be possible to modify your kill switch to instead automatically redirect to a backup exit node if the primary drops?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment