{"slug":"migrate-from-acls-to-grants","title":"Migrate from ACLs to grants","tags":["tailscale","access-control"],"agent_summary":"Last validated: Mar 13, 2026","trigger_phrases":[],"runnable":false,"markdown":"\r\n# Migrate from ACLs to grants\r\n\r\nLast validated: Mar 13, 2026\r\n\r\nThe [tailnet policy file](https://tailscale.com/docs/features/tailnet-policy-file) is the foundation for defining who can access what in your Tailscale network (known as a [tailnet](https://tailscale.com/docs/concepts/tailnet)), controlling [device connections](https://tailscale.com/docs/how-to/connect-to-devices), and managing [logical groupings and assignments](https://tailscale.com/docs/reference/targets-and-selectors). Traditionally, access control lists ( [ACLs](https://tailscale.com/docs/features/access-control/acls)) have been the primary method for defining these permissions at the network layer. However, Tailscale has introduced a more powerful and flexible system called [grants](https://tailscale.com/docs/features/access-control/grants). While ACLs will continue to function indefinitely, the recommended best practice is to prefer grants. This guide walks you through the process of migrating from ACLs to grants. It also explains the benefits, provides conversion examples, and offers best practices to ensure a smooth transition.\r\n\r\nYou can use the [visual policy editor](https://tailscale.com/docs/features/visual-editor) to manage your tailnet policy file. Refer to the [visual editor reference](https://tailscale.com/docs/reference/visual-editor) for guidance on using the visual editor.\r\n\r\n## [Understanding the differences](https://tailscale.com/docs/reference/migrate-acls-grants\\#understanding-the-differences)\r\n\r\nACLs and grants both use a [least privilege](https://tailscale.com/learn/principle-of-least-privilege) and [deny-by-default](https://en.wikipedia.org/wiki/Fail-safe) approach, but grants provide several advantages.\r\n\r\nACLs focus primarily on network layer permissions, using a syntax that combines destinations and ports into a single field. Each ACL entry also requires an explicit `action` field, even though `accept` is the only possible value. This structure can become more complex and difficult to read as your tailnet grows.\r\n\r\nGrants, on the other hand, expand upon ACLs by unifying network and application layer capabilities under a shared syntax. They also separate destinations and ports/protocols into distinct fields for better readability and eliminate the redundant `action` field.\r\n\r\nFor more information, refer to [grants vs. ACLs](https://tailscale.com/docs/reference/grants-vs-acls).\r\n\r\n## [Benefits of migrating to grants](https://tailscale.com/docs/reference/migrate-acls-grants\\#benefits-of-migrating-to-grants)\r\n\r\nMigrating to grants offers several benefits:\r\n\r\n- **Network and application level permissions**: The most significant advantage of grants is their ability to control both network and application layer access. While ACLs can only determine if a connection is allowed at the network level, grants can specify what actions are permitted in applications running at the destination once that connection is established.\r\n- **More intuitive syntax**: Grants provide a more intuitive and consistent syntax. By separating the destination and port specifications, they make your tailnet policy file easier to read, write, and maintain.\r\n- **Routing awareness**: The [`via`](https://tailscale.com/docs/features/access-control/grants/grants-via) field in grants gives the ability to control how traffic flows through subnet routers, exit nodes, or app connectors on its way to the destination. This means you can control not only who can access what, but also how that access is routed.\r\n\r\n## [Structural mapping between ACLs and grants](https://tailscale.com/docs/reference/migrate-acls-grants\\#structural-mapping-between-acls-and-grants)\r\n\r\nThe following table outlines the mapping between the ACLs and grants structure to help visualize the conversion process.\r\n\r\n| ACLs element | Grants element | Notes |\r\n| --- | --- | --- |\r\n| `\"acls\": [...]` | `\"grants\": [...]` | Top-level array changes name |\r\n| `\"action\": \"accept\"` | _Removed_ | Implied in grants format |\r\n| `\"src\": [...]` | `\"src\": [...]` | Source principals remain the same |\r\n| `\"dst\": [...]` | `\"dst\": [...]` and `\"ip\": [...]` | Destination principals and ports separated |\r\n| Port in destination: `\"tag:server:80\"` | Port in `\"ip\"` field: `\"80\"` | Port specification moves to IP field |\r\n| `\"proto\": \"tcp\"` | Part of `\"ip\"` field: `\"tcp:80\"` | Protocol embedded in IP field |\r\n| `\"srcPosture\": [...]` | `\"srcPosture\": [...]` | Same in both formats |\r\n| _Not available_ | `\"app\": {...}` | New field for application capabilities |\r\n| _Not available_ | `\"via\": [...]` | New field for routing control |\r\n\r\nThis structural mapping provides a foundation for the systematic conversion process that follows.\r\n\r\n## [Basic migration patterns](https://tailscale.com/docs/reference/migrate-acls-grants\\#basic-migration-patterns)\r\n\r\nThe process of converting ACLs to grants follows some patterns that you can apply systematically to your tailnet policy file. This section outlines the basic conversion steps with practical examples.\r\n\r\nThe fundamental pattern for converting a standard ACL entry to a grant is to reorganize the components while maintaining the same access permissions.\r\n\r\n1. Remove the `action` field.\r\n2. Combine port specifications from destination with protocols from the `proto` field into the `ip` field.\r\n\r\nIn an ACL, you typically have a structure such as:\r\n\r\n```json\r\n\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server:443\"],\\\r\n    \"proto\": \"tcp\",\\\r\n  }\\\r\n]\r\n```\r\n\r\nWhen converting to a grant, you'll reorganize this into:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server\"],\\\r\n    \"ip\": [\"tcp:443\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\nKey differences: the grant removes the `action` field, as it's implied in grants, and moves the port specification from the `dst` field to a separate `ip` field. This separation makes the policy more readable and easier to maintain, especially when dealing with multiple ports or protocols.\r\n\r\nIf the ACL doesn't specify the protocol, you don't need to specify it in the grant.\r\n\r\nFor ACLs that specify multiple ports:\r\n\r\n```json\r\n\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server:80\", \"tag:web-server:22\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\nBecomes:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server\"],\\\r\n    \"ip\": [\"80\", \"22\"]\\\r\n  }\\\r\n]\r\n```\r\n\r\nHowever, if the original destination (`dst` field) targeted two different [tags](https://tailscale.com/docs/features/tags), you must create two grants⎯one for each tag.\r\n\r\n```json\r\n\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server:80\", \"tag:dev-server:22\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\nBecomes:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server\"],\\\r\n    \"ip\": [\"80\"]\\\r\n  },\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:dev-server\"],\\\r\n    \"ip\": [\"22\"]\\\r\n  }\\\r\n]\r\n```\r\n\r\nIf you were to convert this ACL into a single grant, it would allow port `80` and port `22` for both the `dev-server` and `web-server` tags, which is a different permission outcome than the original ACL.\r\n\r\n### [Convert ACLs to grants using the admin console](https://tailscale.com/docs/reference/migrate-acls-grants\\#convert-acls-to-grants-using-the-admin-console)\r\n\r\nThe admin console includes a **Convert to grants** button next to the JSON editor that automatically converts your entire `acls` section to equivalent `grants` entries.\r\n\r\n1. Go to the [Access controls](https://login.tailscale.com/admin/acls) page of the admin console.\r\n2. Select **JSON editor**.\r\n3. Under **Convert ACLs to grants**, select **Convert to grants**.\r\n4. Review the converted grants in the editor. The conversion removes the `acls` section and replaces it with an equivalent `grants` section.\r\n5. Select **Save** to apply the changes.\r\n\r\nThe **Convert to grants** button is always visible in the JSON editor but is disabled when your tailnet policy file does not contain an `acls` section.\r\n\r\nThe conversion takes effect in the editor immediately with no confirmation dialog. Review the converted grants before saving. Tailscale logs all tailnet policy file changes in the [configuration audit logs](https://tailscale.com/docs/features/logging/audit-logging), which lets you revert changes.\r\n\r\n### [Manually convert ACLs to grants](https://tailscale.com/docs/reference/migrate-acls-grants\\#manually-convert-acls-to-grants)\r\n\r\nThis section outlines a step-by-step process for manually converting your ACLs to grants, with considerations for different scenarios that might add complexity.\r\n\r\nTailscale logs all tailnet policy file changes in the [configuration audit logs](https://tailscale.com/docs/features/logging/audit-logging), which lets you revert changes.\r\n\r\nFirst, identify all ACL entries in your tailnet policy file and categorize them by their function or the resources they protect. This organization will help you convert related entries together and maintain a coherent structure in your grants.\r\n\r\nFor each ACL entry, follow this conversion process:\r\n\r\n1. Create a new grant object with a `src` array containing the values from the ACLs `src` array.\r\n2. Create a `dst` array in the grant object.\r\n3. Create an `ip` array in the grant object.\r\n4. For each entry in the ACLs `dst` array:\r\n1. Split the entry on the colon (`:`) to separate the destination and ports.\r\n2. Add the destination (part before the colon) to the `dst` array.\r\n3. If the ACL rule has a `proto` field, add that protocol to the ports from the `dst` field (for example, `\"tcp:80\", \"tcp:443\"`).\r\n4. If no `proto` field is specified, don't use a protocol prefix.\r\n5. Add the resulting protocol string to the `ip` array.\r\n5. If the ACL rule has a `srcPosture` array, copy it directly to the grant object.\r\n\r\nIf multiple destinations in a single ACL rule have different port requirements, create separate grant rules for each destination to maintain clear and specific permissions.\r\n\r\nFor complex policies, consider a phased approach by converting one section or functional area at a time. This incremental strategy reduces risk and makes it easier to identify and resolve any issues that arise during the migration.\r\n\r\nAfter converting all ACLs to grants, review the entire tailnet policy file for consistency and completeness. Check that all necessary permissions have been preserved and that the resulting grants structure is logical and maintainable.\r\n\r\n## [Migration scenarios](https://tailscale.com/docs/reference/migrate-acls-grants\\#migration-scenarios)\r\n\r\nBeyond the basic patterns, you might encounter more complex ACL configurations that require special attention during migration. This section explores these scenarios and provides guidance on converting them to grants.\r\n\r\n### [Wildcards](https://tailscale.com/docs/reference/migrate-acls-grants\\#wildcards)\r\n\r\nWhen dealing with ACLs that use wildcard ports or protocol (`*`) specifications, the conversion to grants follows specific rules. For example, an ACL that permits access to all ports:\r\n\r\n```json\r\n\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:prod\"],\\\r\n    \"dst\": [\"tag:database:*\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\nConverts to:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:prod\"],\\\r\n    \"dst\": [\"tag:database\"],\\\r\n    \"ip\": [\"*\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\n### [IP address ranges and CIDR notations](https://tailscale.com/docs/reference/migrate-acls-grants\\#ip-address-ranges-and-cidr-notations)\r\n\r\nFor ACLs that use IP address ranges or [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing), the conversion is similar, but you must be careful about how you structure the `dst` and `ip` fields. In grants, the CIDR notation stays in the `dst` field, while protocol and port specifications move to the `ip` field:\r\n\r\n```json\r\n\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:devops\"],\\\r\n    \"dst\": [\"192.0.2.0/24:22\"],\\\r\n    \"proto\": [\"tcp\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\nBecomes:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:devops\"],\\\r\n    \"dst\": [\"192.0.2.0/24\"],\\\r\n    \"ip\": [\"tcp:22\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\n### [Autogroups](https://tailscale.com/docs/reference/migrate-acls-grants\\#autogroups)\r\n\r\nIf you've been using [autogroup selectors](https://tailscale.com/docs/reference/targets-and-selectors#autogroups) such as `autogroup:member` or `autogroup:self` in your ACLs, these convert directly to grants without any special handling:\r\n\r\n```json\r\n\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"autogroup:member\"],\\\r\n    \"dst\": [\"autogroup:self:*\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\nBecomes:\r\n\r\n```json\r\n\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"autogroup:member\"],\\\r\n    \"dst\": [\"autogroup:self\"],\\\r\n    \"ip\": [\"*\"],\\\r\n  }\\\r\n]\r\n```\r\n\r\n## [SSH rules](https://tailscale.com/docs/reference/migrate-acls-grants\\#ssh-rules)\r\n\r\n[Tailscale SSH](https://tailscale.com/docs/features/tailscale-ssh) uses a separate section in the policy file (`ssh`), separate from both ACLs and grants. During your migration, maintain these SSH rules in their original format.\r\n\r\n## [Testing your migration](https://tailscale.com/docs/reference/migrate-acls-grants\\#testing-your-migration)\r\n\r\nYou can write access control [tests](https://tailscale.com/docs/reference/syntax/policy-file#tests) in the tailnet policy file to ensure that your migrated policies grant the expected access.\r\n\r\nYou can also use the **Preview rules** tab in the admin console editor. When you modify your tailnet policy file through the Tailscale admin console, you can preview the changes before applying them to check if the policies grant the expected access.\r\n\r\n## [Best practices for grant management](https://tailscale.com/docs/reference/migrate-acls-grants\\#best-practices-for-grant-management)\r\n\r\nAs you transition to grants and begin managing your policy in this new format, following these best practices will help ensure a smooth and maintainable access control system.\r\n\r\n- Organize your grants logically, grouping related permissions together and using comments to explain the purpose of each section.\r\n- Consider implementing a staged migration approach, particularly for larger organizations. Start by converting your most basic ACLs to grants, then gradually migrate more complex rules, adding application capabilities as the final step. This phased approach reduces risk and makes it easier to identify and resolve any issues that arise during the migration.\r\n- Document your grant structure and the reasoning behind it, especially for application layer capabilities. This documentation will be valuable for onboarding new team members who need to understand or modify the policy.\r\n\r\n## [Troubleshooting common issues](https://tailscale.com/docs/reference/migrate-acls-grants\\#troubleshooting-common-issues)\r\n\r\nEven with careful planning and testing, you might encounter some challenges during your migration.\r\n\r\nIf you notice missing permissions after migration, double-check that you converted all ACL entries to grants correctly. A common mistake is forgetting to include all ports or protocols in the `ip` field or omitting certain destinations when consolidating multiple ACL entries.\r\n\r\nIn complex setups, you might encounter overlapping grants that create unexpected access patterns. Review your grants to ensure that they don't inadvertently provide more access than intended, particularly when dealing with wildcards or ranges in the `ip` field.\r\n\r\nFor more troubleshooting guidance, refer to [Troubleshooting grants](https://tailscale.com/docs/reference/troubleshooting/grants).\r\n\r\n![Project Logo](https://cdn.brandfetch.io/tailscale.com/fallback/lettermark/theme/dark/h/256/w/256/icon?c=1bfwsmEH20zzEfSNTed)\r\n\r\nAsk AI\r\n\r\nreCAPTCHA\r\n\r\nRecaptcha requires verification.\r\n\r\nprotected by **reCAPTCHA**\r\n","html":"<h1>Migrate from ACLs to grants</h1>\n<p>Last validated: Mar 13, 2026</p>\n<p>The <a href=\"https://tailscale.com/docs/features/tailnet-policy-file\">tailnet policy file</a> is the foundation for defining who can access what in your Tailscale network (known as a <a href=\"https://tailscale.com/docs/concepts/tailnet\">tailnet</a>), controlling <a href=\"https://tailscale.com/docs/how-to/connect-to-devices\">device connections</a>, and managing <a href=\"https://tailscale.com/docs/reference/targets-and-selectors\">logical groupings and assignments</a>. Traditionally, access control lists ( <a href=\"https://tailscale.com/docs/features/access-control/acls\">ACLs</a>) have been the primary method for defining these permissions at the network layer. However, Tailscale has introduced a more powerful and flexible system called <a href=\"https://tailscale.com/docs/features/access-control/grants\">grants</a>. While ACLs will continue to function indefinitely, the recommended best practice is to prefer grants. This guide walks you through the process of migrating from ACLs to grants. It also explains the benefits, provides conversion examples, and offers best practices to ensure a smooth transition.</p>\n<p>You can use the <a href=\"https://tailscale.com/docs/features/visual-editor\">visual policy editor</a> to manage your tailnet policy file. Refer to the <a href=\"https://tailscale.com/docs/reference/visual-editor\">visual editor reference</a> for guidance on using the visual editor.</p>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#understanding-the-differences\">Understanding the differences</a></h2>\n<p>ACLs and grants both use a <a href=\"https://tailscale.com/learn/principle-of-least-privilege\">least privilege</a> and <a href=\"https://en.wikipedia.org/wiki/Fail-safe\">deny-by-default</a> approach, but grants provide several advantages.</p>\n<p>ACLs focus primarily on network layer permissions, using a syntax that combines destinations and ports into a single field. Each ACL entry also requires an explicit <code>action</code> field, even though <code>accept</code> is the only possible value. This structure can become more complex and difficult to read as your tailnet grows.</p>\n<p>Grants, on the other hand, expand upon ACLs by unifying network and application layer capabilities under a shared syntax. They also separate destinations and ports/protocols into distinct fields for better readability and eliminate the redundant <code>action</code> field.</p>\n<p>For more information, refer to <a href=\"https://tailscale.com/docs/reference/grants-vs-acls\">grants vs. ACLs</a>.</p>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#benefits-of-migrating-to-grants\">Benefits of migrating to grants</a></h2>\n<p>Migrating to grants offers several benefits:</p>\n<ul>\n<li><strong>Network and application level permissions</strong>: The most significant advantage of grants is their ability to control both network and application layer access. While ACLs can only determine if a connection is allowed at the network level, grants can specify what actions are permitted in applications running at the destination once that connection is established.</li>\n<li><strong>More intuitive syntax</strong>: Grants provide a more intuitive and consistent syntax. By separating the destination and port specifications, they make your tailnet policy file easier to read, write, and maintain.</li>\n<li><strong>Routing awareness</strong>: The <a href=\"https://tailscale.com/docs/features/access-control/grants/grants-via\"><code>via</code></a> field in grants gives the ability to control how traffic flows through subnet routers, exit nodes, or app connectors on its way to the destination. This means you can control not only who can access what, but also how that access is routed.</li>\n</ul>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#structural-mapping-between-acls-and-grants\">Structural mapping between ACLs and grants</a></h2>\n<p>The following table outlines the mapping between the ACLs and grants structure to help visualize the conversion process.</p>\n<p>| ACLs element | Grants element | Notes |\r\n| --- | --- | --- |\r\n| <code>\"acls\": [...]</code> | <code>\"grants\": [...]</code> | Top-level array changes name |\r\n| <code>\"action\": \"accept\"</code> | <em>Removed</em> | Implied in grants format |\r\n| <code>\"src\": [...]</code> | <code>\"src\": [...]</code> | Source principals remain the same |\r\n| <code>\"dst\": [...]</code> | <code>\"dst\": [...]</code> and <code>\"ip\": [...]</code> | Destination principals and ports separated |\r\n| Port in destination: <code>\"tag:server:80\"</code> | Port in <code>\"ip\"</code> field: <code>\"80\"</code> | Port specification moves to IP field |\r\n| <code>\"proto\": \"tcp\"</code> | Part of <code>\"ip\"</code> field: <code>\"tcp:80\"</code> | Protocol embedded in IP field |\r\n| <code>\"srcPosture\": [...]</code> | <code>\"srcPosture\": [...]</code> | Same in both formats |\r\n| <em>Not available</em> | <code>\"app\": {...}</code> | New field for application capabilities |\r\n| <em>Not available</em> | <code>\"via\": [...]</code> | New field for routing control |</p>\n<p>This structural mapping provides a foundation for the systematic conversion process that follows.</p>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#basic-migration-patterns\">Basic migration patterns</a></h2>\n<p>The process of converting ACLs to grants follows some patterns that you can apply systematically to your tailnet policy file. This section outlines the basic conversion steps with practical examples.</p>\n<p>The fundamental pattern for converting a standard ACL entry to a grant is to reorganize the components while maintaining the same access permissions.</p>\n<ol>\n<li>Remove the <code>action</code> field.</li>\n<li>Combine port specifications from destination with protocols from the <code>proto</code> field into the <code>ip</code> field.</li>\n</ol>\n<p>In an ACL, you typically have a structure such as:</p>\n<pre><code class=\"language-json\">\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server:443\"],\\\r\n    \"proto\": \"tcp\",\\\r\n  }\\\r\n]\n</code></pre>\n<p>When converting to a grant, you'll reorganize this into:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server\"],\\\r\n    \"ip\": [\"tcp:443\"],\\\r\n  }\\\r\n]\n</code></pre>\n<p>Key differences: the grant removes the <code>action</code> field, as it's implied in grants, and moves the port specification from the <code>dst</code> field to a separate <code>ip</code> field. This separation makes the policy more readable and easier to maintain, especially when dealing with multiple ports or protocols.</p>\n<p>If the ACL doesn't specify the protocol, you don't need to specify it in the grant.</p>\n<p>For ACLs that specify multiple ports:</p>\n<pre><code class=\"language-json\">\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server:80\", \"tag:web-server:22\"],\\\r\n  }\\\r\n]\n</code></pre>\n<p>Becomes:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server\"],\\\r\n    \"ip\": [\"80\", \"22\"]\\\r\n  }\\\r\n]\n</code></pre>\n<p>However, if the original destination (<code>dst</code> field) targeted two different <a href=\"https://tailscale.com/docs/features/tags\">tags</a>, you must create two grants⎯one for each tag.</p>\n<pre><code class=\"language-json\">\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server:80\", \"tag:dev-server:22\"],\\\r\n  }\\\r\n]\n</code></pre>\n<p>Becomes:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:web-server\"],\\\r\n    \"ip\": [\"80\"]\\\r\n  },\\\r\n  {\\\r\n    \"src\": [\"group:eng\"],\\\r\n    \"dst\": [\"tag:dev-server\"],\\\r\n    \"ip\": [\"22\"]\\\r\n  }\\\r\n]\n</code></pre>\n<p>If you were to convert this ACL into a single grant, it would allow port <code>80</code> and port <code>22</code> for both the <code>dev-server</code> and <code>web-server</code> tags, which is a different permission outcome than the original ACL.</p>\n<h3><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#convert-acls-to-grants-using-the-admin-console\">Convert ACLs to grants using the admin console</a></h3>\n<p>The admin console includes a <strong>Convert to grants</strong> button next to the JSON editor that automatically converts your entire <code>acls</code> section to equivalent <code>grants</code> entries.</p>\n<ol>\n<li>Go to the <a href=\"https://login.tailscale.com/admin/acls\">Access controls</a> page of the admin console.</li>\n<li>Select <strong>JSON editor</strong>.</li>\n<li>Under <strong>Convert ACLs to grants</strong>, select <strong>Convert to grants</strong>.</li>\n<li>Review the converted grants in the editor. The conversion removes the <code>acls</code> section and replaces it with an equivalent <code>grants</code> section.</li>\n<li>Select <strong>Save</strong> to apply the changes.</li>\n</ol>\n<p>The <strong>Convert to grants</strong> button is always visible in the JSON editor but is disabled when your tailnet policy file does not contain an <code>acls</code> section.</p>\n<p>The conversion takes effect in the editor immediately with no confirmation dialog. Review the converted grants before saving. Tailscale logs all tailnet policy file changes in the <a href=\"https://tailscale.com/docs/features/logging/audit-logging\">configuration audit logs</a>, which lets you revert changes.</p>\n<h3><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#manually-convert-acls-to-grants\">Manually convert ACLs to grants</a></h3>\n<p>This section outlines a step-by-step process for manually converting your ACLs to grants, with considerations for different scenarios that might add complexity.</p>\n<p>Tailscale logs all tailnet policy file changes in the <a href=\"https://tailscale.com/docs/features/logging/audit-logging\">configuration audit logs</a>, which lets you revert changes.</p>\n<p>First, identify all ACL entries in your tailnet policy file and categorize them by their function or the resources they protect. This organization will help you convert related entries together and maintain a coherent structure in your grants.</p>\n<p>For each ACL entry, follow this conversion process:</p>\n<ol>\n<li>Create a new grant object with a <code>src</code> array containing the values from the ACLs <code>src</code> array.</li>\n<li>Create a <code>dst</code> array in the grant object.</li>\n<li>Create an <code>ip</code> array in the grant object.</li>\n<li>For each entry in the ACLs <code>dst</code> array:</li>\n<li>Split the entry on the colon (<code>:</code>) to separate the destination and ports.</li>\n<li>Add the destination (part before the colon) to the <code>dst</code> array.</li>\n<li>If the ACL rule has a <code>proto</code> field, add that protocol to the ports from the <code>dst</code> field (for example, <code>\"tcp:80\", \"tcp:443\"</code>).</li>\n<li>If no <code>proto</code> field is specified, don't use a protocol prefix.</li>\n<li>Add the resulting protocol string to the <code>ip</code> array.</li>\n<li>If the ACL rule has a <code>srcPosture</code> array, copy it directly to the grant object.</li>\n</ol>\n<p>If multiple destinations in a single ACL rule have different port requirements, create separate grant rules for each destination to maintain clear and specific permissions.</p>\n<p>For complex policies, consider a phased approach by converting one section or functional area at a time. This incremental strategy reduces risk and makes it easier to identify and resolve any issues that arise during the migration.</p>\n<p>After converting all ACLs to grants, review the entire tailnet policy file for consistency and completeness. Check that all necessary permissions have been preserved and that the resulting grants structure is logical and maintainable.</p>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#migration-scenarios\">Migration scenarios</a></h2>\n<p>Beyond the basic patterns, you might encounter more complex ACL configurations that require special attention during migration. This section explores these scenarios and provides guidance on converting them to grants.</p>\n<h3><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#wildcards\">Wildcards</a></h3>\n<p>When dealing with ACLs that use wildcard ports or protocol (<code>*</code>) specifications, the conversion to grants follows specific rules. For example, an ACL that permits access to all ports:</p>\n<pre><code class=\"language-json\">\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:prod\"],\\\r\n    \"dst\": [\"tag:database:*\"],\\\r\n  }\\\r\n]\n</code></pre>\n<p>Converts to:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:prod\"],\\\r\n    \"dst\": [\"tag:database\"],\\\r\n    \"ip\": [\"*\"],\\\r\n  }\\\r\n]\n</code></pre>\n<h3><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#ip-address-ranges-and-cidr-notations\">IP address ranges and CIDR notations</a></h3>\n<p>For ACLs that use IP address ranges or <a href=\"https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing\">CIDR notation</a>, the conversion is similar, but you must be careful about how you structure the <code>dst</code> and <code>ip</code> fields. In grants, the CIDR notation stays in the <code>dst</code> field, while protocol and port specifications move to the <code>ip</code> field:</p>\n<pre><code class=\"language-json\">\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"group:devops\"],\\\r\n    \"dst\": [\"192.0.2.0/24:22\"],\\\r\n    \"proto\": [\"tcp\"],\\\r\n  }\\\r\n]\n</code></pre>\n<p>Becomes:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"group:devops\"],\\\r\n    \"dst\": [\"192.0.2.0/24\"],\\\r\n    \"ip\": [\"tcp:22\"],\\\r\n  }\\\r\n]\n</code></pre>\n<h3><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#autogroups\">Autogroups</a></h3>\n<p>If you've been using <a href=\"https://tailscale.com/docs/reference/targets-and-selectors#autogroups\">autogroup selectors</a> such as <code>autogroup:member</code> or <code>autogroup:self</code> in your ACLs, these convert directly to grants without any special handling:</p>\n<pre><code class=\"language-json\">\"acls\": [\\\r\n  {\\\r\n    \"action\": \"accept\",\\\r\n    \"src\": [\"autogroup:member\"],\\\r\n    \"dst\": [\"autogroup:self:*\"],\\\r\n  }\\\r\n]\n</code></pre>\n<p>Becomes:</p>\n<pre><code class=\"language-json\">\"grants\": [\\\r\n  {\\\r\n    \"src\": [\"autogroup:member\"],\\\r\n    \"dst\": [\"autogroup:self\"],\\\r\n    \"ip\": [\"*\"],\\\r\n  }\\\r\n]\n</code></pre>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#ssh-rules\">SSH rules</a></h2>\n<p><a href=\"https://tailscale.com/docs/features/tailscale-ssh\">Tailscale SSH</a> uses a separate section in the policy file (<code>ssh</code>), separate from both ACLs and grants. During your migration, maintain these SSH rules in their original format.</p>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#testing-your-migration\">Testing your migration</a></h2>\n<p>You can write access control <a href=\"https://tailscale.com/docs/reference/syntax/policy-file#tests\">tests</a> in the tailnet policy file to ensure that your migrated policies grant the expected access.</p>\n<p>You can also use the <strong>Preview rules</strong> tab in the admin console editor. When you modify your tailnet policy file through the Tailscale admin console, you can preview the changes before applying them to check if the policies grant the expected access.</p>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#best-practices-for-grant-management\">Best practices for grant management</a></h2>\n<p>As you transition to grants and begin managing your policy in this new format, following these best practices will help ensure a smooth and maintainable access control system.</p>\n<ul>\n<li>Organize your grants logically, grouping related permissions together and using comments to explain the purpose of each section.</li>\n<li>Consider implementing a staged migration approach, particularly for larger organizations. Start by converting your most basic ACLs to grants, then gradually migrate more complex rules, adding application capabilities as the final step. This phased approach reduces risk and makes it easier to identify and resolve any issues that arise during the migration.</li>\n<li>Document your grant structure and the reasoning behind it, especially for application layer capabilities. This documentation will be valuable for onboarding new team members who need to understand or modify the policy.</li>\n</ul>\n<h2><a href=\"https://tailscale.com/docs/reference/migrate-acls-grants#troubleshooting-common-issues\">Troubleshooting common issues</a></h2>\n<p>Even with careful planning and testing, you might encounter some challenges during your migration.</p>\n<p>If you notice missing permissions after migration, double-check that you converted all ACL entries to grants correctly. A common mistake is forgetting to include all ports or protocols in the <code>ip</code> field or omitting certain destinations when consolidating multiple ACL entries.</p>\n<p>In complex setups, you might encounter overlapping grants that create unexpected access patterns. Review your grants to ensure that they don't inadvertently provide more access than intended, particularly when dealing with wildcards or ranges in the <code>ip</code> field.</p>\n<p>For more troubleshooting guidance, refer to <a href=\"https://tailscale.com/docs/reference/troubleshooting/grants\">Troubleshooting grants</a>.</p>\n<p><img src=\"https://cdn.brandfetch.io/tailscale.com/fallback/lettermark/theme/dark/h/256/w/256/icon?c=1bfwsmEH20zzEfSNTed\" alt=\"Project Logo\"></p>\n<p>Ask AI</p>\n<p>reCAPTCHA</p>\n<p>Recaptcha requires verification.</p>\n<p>protected by <strong>reCAPTCHA</strong></p>\n"}