Get started - it's free!
Log in
© 2025

GitOps for Tailscale with Bitbucket

Access control policies define what users or devices are permitted to access in your Tailscale network (known as a tailnet). An alternative to managing the access control policy changes in the Access Controls page of the admin console is to use GitOps to manage the access control policy changes. This topic provides details on how to use Bitbucket Pipelines to automatically apply and test access control policy changes to your tailnet.

GitOps for Tailscale is available for all plans.

Prerequisites

In addition to already having your own Tailscale network, you need:

  • A Bitbucket account.
  • Working knowledge of Bitbucket procedures including committing changes, creating pull requests, and merging pull requests.
  • A private Bitbucket repository that will contain your tailnet policy file.

Make sure this repo is private, as tailnet policy files contain personally identifiable information (PII), such as users email addresses.

  • A Tailscale API access token for your tailnet. You can create an API access token in the Keys page of the admin console.

Set up your GitOps configuration

  1. In your Bitbucket repo, create a file named policy.hujson. Copy your tailnet policy file contents from the Access Controls page of the admin console into this file.

If you want to change the tailnet policy file name to something else, you need to modify the --policy-file argument used by the gitops-pusher binary in the Bitbucket pipeline discussed below.

To prevent others admins in your organization from accidentally changing your tailnet policy file, add a comment as the first line in the policy file so the Access Controls page can display a warning:

// This tailnet's ACLs are maintained in <url>

Ensure this comment is the first line of the policy file, otherwise the Access Controls page will not display the warning.

  1. Commit the tailnet policy file and push it to Bitbucket. For example, if you are using the command line:

    git add .
    git commit -sm "policy: import from admin console"
    git push -u origin main
    
  2. Within your repository's Repository variables settings, create the following Bitbucket secured variables:

    • TS_API_KEY: Use your Tailscale API access token as the value.
    • TS_TAILNET: Use your organization as the value. For example, example.com or myemail@example.com. You can find your organization in the Settings page of the admin console.

    These secrets provide the configuration for the pipeline.

  3. Make a new Bitbucket Pipelines pipeline that uses the gitops-pusher binary.

    Create a file named bitbucket-pipelines.yml in the Source folder and paste in the following:

    image: golang
    
    pipelines:
      default:
         - step:
             name: Test ACL
             script:
               - go install tailscale.com/cmd/gitops-pusher@latest
               - gitops-pusher --policy-file ./policy.hujson test
      branches:
        main:
          - step:
              name: Deploy ACL
              script:
                - go install tailscale.com/cmd/gitops-pusher@latest
                - gitops-pusher --policy-file ./policy.hujson apply
    

    For information about Bitbucket Pipelines options and properties, see Bitbucket Pipelines configuration reference.

  4. Commit and push bitbucket-pipelines.yml.

The main steps in the pipeline are installing and building the gitops-pusher binary and then running the binary. On pull requests, the Test ACL step will send your tailnet policy file to Tailscale to determine whether the access control policy is valid and whether all tests pass. Tailscale provides your pipeline with the result of those checks, which you can view in the Bitbucket Pipelines page.

On push (merge) operations that target the main branch, the Deploy ACL step will send your tailnet policy file to Tailscale to determine whether the tailnet policy file is valid and whether all tests pass. If this portion succeeds, Tailscale then applies your tailnet policy file update to your tailnet. Tailscale provides your pipeline with the result of this step, which you can view in the Bitbucket Pipelines page.

With this setup, you have created a continuous integration (CI) that automatically tests and pushes your tailnet policy file changes to Tailscale!

Push changes to your tailnet policy file

Now that your configuration has been set up, any time your want to update your tailnet policy file, modify the tailnet policy file in your repo and use the typical Bitbucket authoring/review/merge flow.

Prevent others from accidentally modifying your tailnet policy file

To prevent other admins in your organization from accidentally changing your tailnet policy file, you can use either the Tailscale admin console (preferred) or a code comment in your tailnet policy file (deprecated).

If your tailnet uses both the admin console and a code comment, the admin console setting takes precedence.

For either way, any admin with permissions to edit the tailnet policy file can still edit it directly by selecting Edit anyway. For example, in case of an emergency.

A screenshot of the warning shown when trying to edit a tailnet policy file which is using GitOps.

The next time you use the GitOps flow, it overwrites the tailnet policy file changes made in the admin console.

Use the admin console to prevent accidental modification

You must be an Owner, Admin, or Network admin of a tailnet to update tailnet policy file settings.

  1. Open the Policy file management page in the Tailscale admin console.
  2. If it is not already enabled, select Lock edit to prevent editing of the file.
  3. For External reference, enter the URL of your repository.
  4. Select Save.

Use a code comment to prevent accidental modification

We deprecated this technique. Use the admin console instead to prevent accidental modification.

Add the following comment as the first line in the policy file with the URL of your repository so the Access Controls page can display a warning:

// This tailnet's ACLs are maintained in <url>
A screenshot of the warning shown in the tailnet policy file.

You can link to any Git repository or URL. You can use your existing source management system and do not need to use the Sync Tailscale ACLs BitBucket CI to display this warning.

Revert the most recent change to your tailnet policy file

If you need to revert the most recent change, use the Bitbucket Pull requests page to find and then revert the merged pull request.

Remove your GitOps configuration

  1. Remove the following line from the top of tailnet policy file. You can make this edit through your current GitOps configuration, or by manually editing your tailnet policy file.

    // This tailnet's ACLs are maintained in <url>
    
  2. Revoke any associated Tailscale API access tokens for this configuration.

  3. Archive or delete the associated BitBucket repository.

Additional information

  • Any manual tailnet policy file changes in the admin console won't be reflected in your Bitbucket version of the tailnet policy file. The next time you use the Bitbucket pipeline, any changes made in the Tailscale admin console will be overwritten.

  • Tailscale API access tokens expire and currently there is no mechanism to have them automatically renewed. To handle the expiration, create a new API access token and update the Bitbucket TS_API_KEY secret to use the new value. Tailscale API access tokens after 90 days (or less if the key has a shorter expiry) but updating the Bitbucket secret monthly is a good practice.

    When you no longer need to use a Tailscale API access token, make sure you revoke it in the Keys page of the Tailscale admin console.

  • Tailscale tailnet policy files are in HuJSON, a JSON format with trailing commas and comments. If you don't want to write your tailnet policy files in HuJSON directly, you can use a tool that lets you generate JSON in the same schema as the HuJSON format.

Last updated Jun 3, 2025