Cross-compiling ZeroTier for Ubiquiti with GitHub Actions

There was interest to share the arm64 binaries for ZeroTier on Ubiquiti devices. I took the opportunity to try AI and generate the GitHub Actions workflow.

This is a follow up to Cross-compiling ZeroTier for arm64 Ubiquiti Devices. There was some interest in the comments to generate the Ubiquiti binaries and distribute; but I didn’t want to share the binaries without the source. Since I had all the steps outlined from the previous blog post and just needed it to get it in the GitHub Actions format, I decided to take the opportunity to try out AI to help automate the process. I settled with using ChatGPT, which was using the GPT-5 model.

The final output is below, and hopefully self explanatory. It’s also on GitHub, but on a temporary branch. Please feel free to amend and use in your own fork:

# Injabie3 - 2025-10-23
# Ubiquiti build script for ZeroTier
#
# This GitHub Action is based on the steps from the following blog post:
# https://blog.injabie3.moe/2024/02/23/cross-compiling-zerotier-for-ubiquiti-devices
#
name: Cross-compile ZeroTier for Ubiquiti (arm64)
on:
  push:
    branches: [dev]
  workflow_dispatch:
jobs:
  build-arm64:
    name: Build and package ZeroTier (arm64)
    runs-on: ubuntu-latest
    env:
      CC: aarch64-linux-gnu-gcc
      CXX: aarch64-linux-gnu-g++
      ZT_UBIQUITI: '1'
    steps:
      - name: πŸ›  Checkout source
        uses: actions/checkout@v3
      - name: πŸ“¦ Install dependencies
        run: |
          sudo dpkg --add-architecture arm64
          # Add Ubuntu Ports repository for ARM64 only
          sudo tee /etc/apt/sources.list.d/arm64.list <<'EOF'
          deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble main restricted universe multiverse
          deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted universe multiverse
          deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-security main restricted universe multiverse
          EOF
          # Update ignoring missing arm64 indices from default mirrors
          sudo apt-get update -o Acquire::Retries=3 || true
          sudo apt-get install -y \
            gcc-aarch64-linux-gnu \
            g++-aarch64-linux-gnu \
            binutils-aarch64-linux-gnu \
            make \
            dpkg-dev fakeroot \
            devscripts build-essential lintian debhelper \
            libstdc++6 \
            libstdc++6-arm64-cross \
            libstdc++6-amd64-cross \
            libc6-arm64-cross \
            libc6-amd64-cross \
            libgcc-s1 \
            libgcc-s1-arm64-cross \
            libgcc-s1-amd64-cross \
            libc6:arm64 \
            libstdc++6:arm64 \
            libgcc-s1:arm64
      - name: πŸ”§ Patch make-linux.mk to disable SSO
        run: |
          sed -i 's/^\(.*ZT_SSO_SUPPORTED=\)1/\10/' make-linux.mk
      - name: 🧱 Build and package (.deb) using Makefile
        run: |
          CC=${CC} CXX=${CXX} ZT_UBIQUITI=${ZT_UBIQUITI} ZT_STATIC=1 make debian
      - name: 🚚 Move generated artifacts into workspace
        run: |
          find .. -maxdepth 1 -name "*arm64.*" -exec mv {} . \;
      - name: 🧩 Recompress .deb to xz
        run: |
          set -e
          DEB_FILE=$(ls ./*.deb | head -n1)
          mkdir repack && cd repack
          # Extract components
          ar x "../$DEB_FILE"
          # Recompress zstd β†’ xz
          if [ -f control.tar.zst ]; then
            zstd -d < control.tar.zst | xz > control.tar.xz
          fi
          if [ -f data.tar.zst ]; then
            zstd -d < data.tar.zst | xz > data.tar.xz
          fi
          # Recreate the package
          ar -m -c -a sdsd "../${DEB_FILE%.deb}-xz.deb" debian-binary control.tar.xz data.tar.xz
          # Cleanup
          cd ..
          rm -rf repack
      - name: 🏷 Set metadata for artifact
        id: vars
        run: |
          echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
          echo "date_tag=$(date +'%Y%m%d')" >> "$GITHUB_OUTPUT"
      - name: πŸ“€ Upload .deb package
        uses: actions/upload-artifact@v4
        with:
          name: zerotier-arm64-${{ steps.vars.outputs.date_tag }}-${{ steps.vars.outputs.short_sha }}-deb
          path: '*-xz.deb'
      - name: πŸ“€ Upload debug artifacts
        uses: actions/upload-artifact@v4
        with:
          name: zerotier-arm64-${{ steps.vars.outputs.date_tag }}-${{ steps.vars.outputs.short_sha }}-debug
          path: '*arm64.*'

I basically gave it the link to my blog post, and it almost got all the way there. Getting the build to actually work took some time, because the environment was slightly different than my actual laptop. I had to figure out the missing dependencies to install in the Actions build environment, and that took a bunch of back and forth to massage within ChatGPT. Honestly, I’m pretty impressed at how AI can be used a tool to execute on things. I may explore this area a bit more since we’re also exploring how we can use AI tools at work to help automate or augment our existing workflows.

A successful build that generated the package and debug artifacts.

There’s still quite a lot to improve on, but it does generate the artifacts: I did get it loaded and running on my Ubiquiti Dream Router, and it’s working fine.

Anyways, that’s all I got this time around.

Until next time!
~Lui

Injabie3
Injabie3

Just some guy on the Internet that writes code for fun and for a living, and also collects anime figures.

Articles: 288

3 Comments

  1. Incase it helps anyone the Debian arm64 build for bullseye installs and works perfectly for me on my Unifi Dream Machine on latest updates. No need to compile.
    https://download.zerotier.com/dist/debian/bullseye/zerotier-one_1.16.0-2_arm64.deb
    With the help of Gemini I made a script to run at boot to install zerotier if needed, move it’s config folder to persistent storage and start zerotier (it should hopefully survive upgrades).
    ***Please note I am not an experienced coder, just a willing tinkerer so please check the code and use at your own risk***
    ***I would really appreciate any feedback on improving my logic***
    Script located here: https://pastebin.com/Zm6KU2yV
    To update zerotier with this script you need to change the variable PACKAGE_URL to the new build and run the script with the -u or –update argument
    The script is to be located at: “/data/on_boot.d/10-zerotier.sh” which uses boostchickens on-boot-scripts from here:
    https://github.com/unifi-utilities/unifios-utilities/tree/main/on-boot-script

  2. Have been following your progress on getting zerotier running on unifi devices.
    Incase it helps anyone the Debian arm64 build for bullseye installs and works perfectly for me on my Unifi Dream Machine on latest updates. No need to compile.
    https://download.zerotier.com/dist/debian/bullseye/zerotier-one_1.16.0-2_arm64.deb
    With the help of Gemini I made a script to run at boot to install zerotier if needed, move it’s config folder to persistent storage and start zerotier (it should hopefully survive upgrades).
    ***Please note I am not an experienced coder, just a willing tinkerer so please check the code and use at your own risk***
    ***I would really appreciate any feedback on improving my logic***
    Script located here: https://pastebin.com/Zm6KU2yV
    To update zerotier with this script you need to change the variable PACKAGE_URL to the new build and run the script with the -u or –update argument
    The script is to be located at: “/data/on_boot.d/10-zerotier.sh” which uses boostchickens on-boot-scripts from here:
    https://github.com/unifi-utilities/unifios-utilities/tree/main/on-boot-script

    • Thanks for sharing your experience, yeah I noticed the Debian arm64 builds last time after I decided to tinker as well!

Feel free to leave a reply