I have been hearing a lot about Tailscale and it’s ease of use in easily creating a secure mesh VPN for your systems. I was already a fan of Wireguard which was a plus to Tailscale for using it but I was a bit worried being tied to a company for a VPN again. Users of the Hamachi VPN will know my pain after losing free access to it that after LogMeIn bought them . However, there is an open source Tailscale VPN coordinator called HeadScale which allows people to control their own setup VPN outside of Tailscale’s control.
The final push for me to shift to using Tailscale was the concept of ACLs. ACLs in Tailscale allow you control network access between users, devices or groups. For example, If you want to make sure only some devices can access a Plex server on port 32400, you can restrict that. If you wanted make sure that your file server is accessible to some servers to allow them to get backed up over ssh, you can control that. The changes are centrally controlled and rolled out the network quickly and pretty easily.
The steps to get stated with Tailscale ACLs are as follows:
– Planning the HeadScale network
– Setting up users and groups for your devices
– Creating the ACLs rules to control network access
– Applying, Troubleshooting and Testing rules
Before you start, make sure to setup the Headscale server with this guide.
Planning the Headscale network
I need the following for my Headscale network:
– I have an admin system (mylaptop) that I want to be able to access other servers with no restrictions.
– There are DMZ systems (intdmz, extdmz) that I want to allow to send backup data to my nasbox over ssh but nothing else.
– I have a mobile device (iphone) that I want to restrict be able to use only certain apps like Jellyfin that I will host at home on my mediabox.
– I have a systems on a different network that I want to allow only direct syncthing access to so I can get a direct connection to without using a relay.
User and Group Creation
We can now create users and groups in the ACL file. Each system/device is going to get a username to ID them and then those usernames will then be put in groups to assign ACL rules to them. You can assign users or IPs directly if needed.
You can assign tags to devices as well but I will not cover that because I feel groups offer a better option to control ACLs for your systems.
To create a user, run the following command on the Headscale server:
sudo headscale users create username_to_create
Log on to Tailscale on the device that you want to add as per the Headscale getting started guide. On the Headscale server, check to see that the devices are online with the command:
sudo headscale nodes list
The device should show up as online.

After the users have been created, we need to make the groups that I will store in the ACLs file json here:
/etc/headscale/acls.json
You can use any text editor to make this file but I recommend using vscode to edit the ACL file as it is easier to edit and find errors later on. We will add the ACL file path to the Headscale config file later on to apply it.
Here you can see my groups at the top of the ACL file :
“groups”: {
“group:admin”: [“mylaptop”],
“group:dmz”: [“intdmz”,”extdmz”],
“group:mobiledevices”: [“iphone”],
“group:dirtyboxes”: [“seedbox1”],
“group:home”: [“jellyfin”,”nasbox”]
},
Creating ACLs
The next section in the acls file is where I am going to set my rules. The acls section starts with:
“acls”: [
and is closed with ] with all the ACL rules are entered between.
Each ACL rule needs an action, a source and a destination with port.
I am going to add a optional comment at the top of each ACL rule to help document them. For this tutorial, the only action I will worry about is the allow action. By default, once ACLs are applied in Tailscale anything that is not defined is denied. There are other actions but I’m not sure if they apply to Headscale and are not needed for the basics.
Below are examples the ACL rules we need but I recommend adding them one by one to the ACL file and jumping to the troubleshooting section for each one to make sure it is OK. Make backups of your known good ACL files to save yourself some pain.
// admin system to have access to all groups and all ports on those systems
{
“action”: “accept”,
“src”: [“group:admin”],
“dst”: [
“group:dmz:*”, “group:admin:*”,
“group:dirtyboxes”,
“group:home:*”, “group:mobiledevices:*”
]
},
// Allow all dmz systems to backup to nasbox over ssh
{
“action”: “accept”,
“src”: [“group:dmz”],
“dst”: [“nasbox:22”]
},
// Allow mobiledevices to access media server for the Jellyfin website
{
“action”: “accept”,
“src”: [“group:mobiledevices”],
“dst”: [“mediabox:8096”]
},
// Allow access to syncthing on mediabox
{
“action”: “accept”,
“src”: [“group:dirtyboxes”],
“dst”: [“mediabox:22000”]
}
There are more details on ACL syntax can be found here on Tailscale’s site,
These basic rules should get you by for now.
Applying, Troubleshooting and Testing ACL Rules
To apply the ACL rules, we need to add the rules file to the Headscale config file and restart the Headscale service to apply the settings. In the /etc/headscale/config.yaml file add the ACL file path. This may change but for me it was:
acl_policy_path: “/etc/headscale/acls.json”
Next, we need to restart Headscale. I suggest that you ssh directly to the Headscale server (not over Tailscale) just in case it gets broken when you do this. The command below will:
– Restart the Headscale service
– Check to see the status of the HeadScale service
– Check to see if Tailscale devices are checking in:
sudo systemctl restart headscale && sudo systemctl status headscale && sleep 15 && sudo headscale nodes list
Below is an example of an acls file that has because there was an error stating Headscaleand the Headscale nodes are not listed.

We need to see what part of the acl file is not working but this error does not offer much information. We can see more details on what is wrong in the ACL file with the command:
sudo /usr/bin/headscale serve /etc/headscale/config.yaml

We can see the line where the error is so we can fix it. Once the acl file has been corrected, if you run the command again:
sudo systemctl restart headscale && sudo systemctl status headscale && sleep 15 && sudo headscale nodes list

You will see the service is running and nodes are being listed again.
After the application of an ACL rule, Make sure that you have access connect to the resources you specified in the rule. Can you get to that internal website from the device you wanted? Can other devices NOT get to that site? Can you use ssh where you wanted and blocked if you had not given access. The changes will apply right way so if it does not allow what it should, double check your acl file.
I hope this guide helps!
Please check out the Headscale Discord for more detailed assistance. Thanks to the Headscale and Tailscale teams for bringing this great remote access tool into the world.