Smaller binaries for embedded devices
Tailscale provides downloads for a variety of operating systems and architectures on our downloads page. However, there are cases where you may want to build an “extra-small” Tailscale binary—one that takes up a minimal amount of disk space. One common use case is building for an embedded device like an OpenWrt router.
Before you begin this guide, you’ll need to have a Go development environment already set up and working.
First, you can build a version of Tailscale that embeds both the client (the
tailscale binary) and the daemon (the
tailscaled binary) into a single
binary. This is similar to how tools like
busybox work, where the behavior
of the binary depends on how it’s called (specifically, the value of
argv). Essentially: if the file on-disk is named
tailscale, then the
binary will behave like the
tailscale binary, and if it’s named
then it’ll behave like
In the Tailscale repository:
$ go build -o tailscale.combined -tags ts_include_cli ./cmd/tailscaled $ du -hs tailscale.combined 23M tailscale.combined
By creating symlinks to the combined binary, you can see how it can run as both the daemon and the CLI:
$ ln -s tailscale.combined tailscale $ ln -s tailscale.combined tailscaled $ ./tailscale --help USAGE tailscale [flags] <subcommand> [command flags] For help on subcommands, add --help after: "tailscale status --help". # ... omitted ... $ ./tailscaled --help Usage of ./tailscaled: -bird-socket string path of the bird unix socket -cleanup clean up system state and exit # ... omitted ...
In addition to combining both the Tailscale client and daemon into the same
binary, you can also pass link flags to omit things from the built
binary. We’re going to pass the
-w flags, which omit a variety of
debug information from the binary.
$ go build -o tailscale.combined -tags ts_include_cli -ldflags="-s -w" ./cmd/tailscaled $ du -hs tailscale.combined 18M tailscale.combined
In cases where disk space is a premium, one of the more effective options is to use compression. There’s a class of tools called “packers”, which compress a binary on-disk and restore the original uncompressed version of the binary when it is run.
Using a packer can be dangerous! These tools fundamentally change how a binary interacts with the operating system in ways that can reduce that binary’s security. It’s beyond the scope of this guide to go into the full details, but as a specific example, most packers require that W^X protection be turned off or an executable temporary directory in order to function.
In the case where it’s necessary, though, the resulting space savings can be impressive. Perhaps the most well-known packer is UPX, and it can be used to compress the Tailscale combined binary that we built above.
$ go build -o tailscale.combined -tags ts_include_cli -ldflags="-s -w" ./cmd/tailscaled $ du -hs tailscale.combined 18M tailscale.combined $ upx --lzma --best ./tailscale.combined $ du -hs tailscale.combined 4.5M tailscale.combined
After UPX compression, we’ve reduced the size of the (combined) Tailscale binary from the original size of 23MiB down to only 4.5MiB, about 20% of the original size!