Trayscale: a GTK4 desktop for the Tailscale daemon on Linux
An unofficial GUI wrapper around the Tailscale CLI client.
At a glance
- What is it?
- Trayscale is an unofficial Go and libadwaita interface to tailscaled that adds the features the official Linux client leaves out, and it works by talking to the local daemon as an operator user. It is a beta project with no macOS support, and the Flatpak and AUR packages are the sane install paths.
- Who is it for?
- Adopt Trayscale if you run Tailscale on Linux and want the exit node, DNS and preference controls the official Linux client does not expose in a graphical form. Do not adopt it if you need a supported client, if you are on macOS and need the tray icon, or if your organisation requires software with an upstream vendor behind it.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A GUI for tailscaled, and what the official client leaves out
Trayscale is described as an unofficial GUI interface for the Tailscale daemon, particularly for use on Linux, and the README states plainly that it provides many features the official Linux client does not. The primary language of the repository is Go, the licence is MIT, and the topic tags are golang, gtk4, hacktoberfest, libadwaita, linux and tailscale.
The word unofficial in that first sentence is the important qualifier, and it is not a criticism of quality so much as a statement of standing. Tailscale is a company with its own clients, and this is a third-party front end over the same daemon. The README carries an explicit disclaimer: the project is in a beta state, there may still be undiscovered bugs or compatibility issues, and it should be used at your own risk.
The interesting design decision is the one the README does not dwell on. Trayscale does not reimplement the Tailscale client. It talks to tailscaled, the daemon that already runs on the machine, and asks it to perform the operations the daemon supports. So the GUI is a view over a privileged local service, not a second implementation of the network stack. That has an important consequence for anyone weighing it against the official client: the features Trayscale exposes are the features the daemon's local API exposes, and a GUI cannot conjure an operation the daemon does not have.
The audience is Linux desktop users who want to manage Tailscale settings graphically rather than from a terminal, and specifically the ones who have found gaps in the official Linux client's interface. The GTK4 and libadwaita tags place it squarely in the GNOME desktop idiom, and the desktop file, icon and GSettings schema at the top of the repository confirm that it is written for that desktop environment rather than for a generic toolkit look.
The operator requirement, and why it is a hard prerequisite
There is one configuration step that Trayscale cannot work around, and the README is direct about it. The daemon must have been configured with the current user as the operator. To do this, run the following from the command line at least once manually:
sudo tailscale set --operator=$USERThis is a security boundary, not a setup inconvenience, and understanding it is what makes the rest of the design make sense. The Tailscale daemon runs with elevated privileges because it has to configure network interfaces and routing. A local GUI that could drive it without restriction would be a privilege escalation path, so the daemon offers a narrower local management API and the operator flag is how a user is granted access to it. By requiring you to opt in explicitly, once, with sudo, the daemon's owner has decided that the user is trusted to manage the network configuration but not to run arbitrary commands as root.
The same requirement applies to the Flatpak, and the README says so explicitly. The operator configuration still applies to the Flatpak version. What changes is the client binary: the tailscale client is bundled into the Flatpak, so it does not need to be in your $PATH.
That distinction is worth understanding because it is the one behavioural difference between the two install routes. In a manual or AUR installation, Trayscale shells out to the tailscale CLI that is on your path, so a broken $PATH produces a broken application. In the Flatpak, the client travels with the sandboxed application and the $PATH of your session is irrelevant to it.
The practical order of operations is therefore: install the client, make sure the daemon runs, run the operator command once as root, and only then start the graphical interface. Skipping the operator step is the most likely reason a new user sees nothing useful.
Installing on Linux: Flathub, AUR, or a Go install
Three routes exist for Linux, and the README presents them in descending order of convenience.
The Flathub package is the primary distribution, with the badge linking to dev.deedles.Trayscale on Flathub. As noted above, the Flatpak bundles the tailscale client, so it needs nothing from your $PATH, and the operator configuration is still required. This is the route to take if you want the desktop integration to work without assembling metadata files yourself.
The AUR route covers Arch Linux and derivatives, with the package named trayscale. For an Arch user this is close to a one-command install.
The manual route needs three dependencies: Go >= 1.24, GTK >= 4.0 and Libadwaita >= 1.9. The binary itself installs with go install:
$ go install deedles.dev/trayscale/cmd/trayscale@latestThe module path in go.mod is deedles.dev/trayscale, and the command lives under cmd/trayscale, so the install path follows the standard Go layout rather than the repository URL.
The manual route has one wrinkle the README spells out, and it involves desktop files. Three pieces of metadata have to be copied into the right places:
* `dev.deedles-trayscale.desktop` -> `$HOME/.local/share/applications/` * `dev.deedles.Trayscale.png` -> `$HOME/.local/share/icons/hicolor/256x256/apps/`
The README warns that without both of these in the correct locations, notifications will likely not function correctly in GNOME. It also notes that if the trayscale binary is not in your $PATH in a way the desktop environment can locate, the .desktop file will not be considered valid, and the fix is to edit the file and point the Exec= line at the binary with an absolute path. The repository contains dev.deedles.Trayscale.desktop, dev.deedles.Trayscale.png, dev.deedles.Trayscale.gschema.xml and dev.deedles.Trayscale.metainfo.xml, so the appdata and GSettings pieces are also part of the install, which is the kind of detail that distinguishes a real desktop application from a script with a window.
The go.mod requires Go 1.27.2, which is newer than the Go >= 1.24 stated in the README's dependency list, and the module is built with a toolchain directive that will pull the right toolchain if your local one is older.
macOS exists as a build target, not as a supported platform
The macOS section of the README is short and clearly demarcated, and it is worth reading precisely because the framing can mislead.
First, macOS support is not maintained or tested by the primary developer of Trayscale. Second, the tray icon will not work on macOS. Those two sentences define the platform's status: the code builds, but the project does not stand behind it.
The install route on macOS is through Nixpkgs' unstable channel, and the README notes that the package will need to be built from source if a binary cache is unavailable, and that the build can take some time. Building locally without Nix means installing the Go compiler plus four Homebrew packages: libadwaita, gtk4, gobject-introspection and harfbuzz. The gobject-introspection dependency is the one that reveals the architecture, since it is what lets the Go code talk to the GObject introspection system that GTK is built on.
The build itself is driven by a script in the repository root:
$ ./dist.sh build
$ ./dist.sh install-macosThat dist.sh script is present in the top-level listing alongside assets.go, cmd/, internal/, po/ and the desktop metadata files, so the macOS packaging story is scripted rather than manual, which is the right way to do it even for a platform that is not a priority.
The dependency picture in go.mod confirms the shape of the project. codeberg.org/puregotk/puregotk and its purego runtime are the pure Go bindings for GTK, so the application calls GTK through Go rather than through cgo, which removes the C toolchain from the equation for cross-compilation and simplifies the macOS build. deedles.dev/tray provides the system tray integration, and fyne.io/systray appears as an indirect dependency, which is the mechanism the tray code is built on. github.com/klauspost/compress and deedles.dev/xiter handle compressed and streaming data, and github.com/leonelquinteros/gotext is the gettext library.
Localization through gettext, and a GTK hang worth knowing about
Trayscale can display its interface in other languages through gettext catalogs under the po/ directory, and the README describes the mechanism with unusual precision, down to a hang.
There are two string sources. Strings constructed in Go use gotext, and labels marked translatable="yes" in GtkBuilder files use the same catalogs through libintl. So the same catalog file serves both the code that builds strings at runtime and the declarative UI files, which is the arrangement that keeps a translation from silently covering half the interface.
The hang is the interesting part. The README says to prefer LANGUAGE=ja, or to install the matching system locale, and that a bare LANG=ja without a generated libc locale is sanitized so that GTK does not hang. This is a real class of bug rather than a hypothetical one. If the application requests a locale that the C library cannot actually provide, a GTK call that waits for a locale can block indefinitely, and the symptom is an application that never draws a window. The fix in the code is to normalise the requested locale so an unusable value does not reach the toolkit, and the documentation tells users to set LANGUAGE rather than LANG for the same reason.
The po/README.md file is referenced as the reference for the catalogs, and the po/ directory is one of the top-level entries. The internationalisation approach is gettext rather than a per-platform resource system, which fits a Linux-first application distributed as a Flatpak and through package managers, and it is a deliberate choice to reuse the desktop's existing translation infrastructure rather than to ship its own.
For a user wanting a different language, the practical path is to set LANGUAGE to the locale code you want and launch the application, or to install the matching system locale properly.
Where Trayscale is the wrong tool
There are three situations where it is the wrong choice, and the README is upfront about the first one.
The beta disclaimer is the first limit. The project states there may still be undiscovered bugs or compatibility issues and that you should use it at your own risk. For a machine you use casually, that is an acceptable trade for a better interface. For a machine that holds your production access to other machines, where the Tailscale connection is a path to a database, a build server, or a customer's network, the risk calculus is different, and the unofficial status compounds it because there is no vendor behind the fix when something breaks.
The second is macOS, where the tray icon does not work and the primary developer does not maintain or test the platform. On macOS, the official Tailscale client is the better answer unless you specifically need the settings Trayscale exposes and can live without the tray.
The third is a case where the official client already does the job. Trayscale's value is the gap it fills on Linux, and if you only need to connect and disconnect, or to see your exit node status, the official client covers that and is supported. Installing a third-party GUI to manage a network daemon is a decision to take deliberately.
There is a subtler limit too, which follows from the architecture rather than the feature list. Because Trayscale drives the daemon through the local management API, its capabilities are bounded by what the daemon exposes to an operator, and the Tailscale version installed on the machine determines that surface. The README does not document a compatibility matrix between Trayscale versions and tailscaled versions, and the go.mod pins tailscale.com at v1.104.1 as a Go module dependency, which is the client library rather than the daemon. In practice, a Trayscale built against one client library and talking to a much newer daemon may hit behaviour the GUI does not model, and the beta warning is where that risk is acknowledged.
Editorial conclusion
Adopt Trayscale if you run Tailscale on Linux and want the exit node, DNS and preference controls the official Linux client does not expose in a graphical form. Do not adopt it if you need a supported client, if you are on macOS and need the tray icon, or if your organisation requires software with an upstream vendor behind it. Verify first that you have run sudo tailscale set --operator=$USER so the daemon accepts local management calls, that the Tailscale CLI is on your path when installing outside the Flatpak, and that the beta warning in the README is acceptable for the machines you intend to install it on.
Frequently asked questions
What does Trayscale need before it will work?
The Tailscale daemon must be configured with your user as the operator, which the README says to do once by running `sudo tailscale set --operator=$USER`. This applies to the Flatpak version as well.
How do I install Trayscale on Linux?
Trayscale is on Flathub as dev.deedles.Trayscale, and it is in the AUR for Arch Linux and derivatives as trayscale. A manual install needs Go, GTK 4 and libadwaita, then `go install deedles.dev/trayscale/cmd/trayscale@latest`.
Does Trayscale work on macOS?
The README states macOS support is not maintained or tested by the primary developer, and that the tray icon will not work on macOS. It can be installed through Nixpkgs' unstable channel or built locally with ./dist.sh build and ./dist.sh install-macos.
Is Trayscale an official Tailscale application?
No. The README calls it an unofficial GUI interface for the Tailscale daemon, and it carries a disclaimer that the project is in a beta state with possible undiscovered bugs or compatibility issues.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/deedlefake-trayscale)