Hey everyone! 👋 Just installed Tailscale on my laptop and my home server. The setup was super smooth, but now I'm staring at the admin console and the command line, feeling a bit lost. As someone who lives in Terraform and Ansible configs, I'm wondering about the "infrastructure as code" approach and the practical first steps.
I've got the nodes talking, which is awesome! But what are the first things I should actually *do* to make this useful and secure? My immediate goals are:
* Securely access my homelab services (like a NAS web UI) without opening firewall ports.
* Eventually connect a small AWS VPC.
* Set it up following best practices from the start.
Should my first move be setting up ACLs in the admin console? Or is it better to define things via Terraform? I saw the Tailscale provider and I'm tempted to jump right into code. Also, for a beginner, are "tags" or "user groups" more important to understand first?
Here's a snippet of what I'm thinking for Terraform, but I'm not sure if this is the right starting point:
```hcl
resource "tailscale_acl" "sample_acl" {
acl = <<-EOT
{
"tagOwners": {
"tag:homelab": ["autogroup:admin"],
},
"acls": [
{
"action": "accept",
"src": ["autogroup:admin"],
"dst": ["tag:homelab:*"],
},
],
}
EOT
}
```
Any advice on a good "day 1" checklist after installation would be hugely appreciated! I'm excited to build this out.
~CloudOps
Infrastructure as code is the only way
Your Terraform inclination is correct for reproducibility, but you're starting at the wrong abstraction layer. ACLs can come second.
Define your tags in the admin console first, assigning them to your homelab server. That immediately solves your NAS access goal without any firewall rules. Use a tag like `tag:server` and reference it in a quick test ACL like:
```
"acls": [{"action": "accept", "src": ["autogroup:members"], "dst": ["tag:server:*"]}],
```
This gets your services accessible now. Then you can codify this structure and the tag assignments via Terraform, which is crucial before you attach that AWS VPC. You don't want to discover your IaC gaps when you have cloud resources in the mix.
Less spend, more headroom.
Tags first, absolutely. They're the primitive you'll reference everywhere, both in the console and later in Terraform. Get them defined and assigned to your nodes manually, then you can start playing with ACLs to see how they work.
That immediate feedback is important before you codify it. Once you see how `tag:server` grants you access, you'll understand the structure better. Then shift to Terraform to lock it down, especially before that AWS VPC comes online. You don't want to be debugging a broken ACL import when you're trying to reach a cloud resource.
Your snippet is close, but you're missing the ACL policy itself. The `tagOwners` block just declares who can tag, it doesn't allow traffic. You need the `acls` array with "accept" rules using those tags. Start simple in the console, get a rule working, then translate that exact JSON into the Terraform resource.
Sleep is for the weak
Good point about the immediate feedback loop with manual tagging. That's the pragmatic way to learn the model.
Just remember the console's manual tag assignments are ephemeral. They won't survive a machine key expiration or a node deletion. The Terraform move isn't just for reproducibility, it's for state persistence. Your working console ACL becomes the spec for your `tailscale_tailnet_key` resource, which makes the tag assignments durable.
So agree with the sequence, but the shift to code isn't just "locking it down." It's what prevents your access from vanishing unexpectedly.
Show me the bill.
That's a crucial distinction about the state persistence mechanism. The `tailscale_tailnet_key` resource is indeed the correct tool for durable assignments, but its interaction with the node lifecycle can be subtle.
A related observation is that even with that key resource, a node's ephemerality depends on the *type* of authentication key used. A one-off key generated in the console for a single device is still ephemeral. The Terraform-managed tailnet key creates a reusable, persistent registration key. The durable tag assignments are a property of that key, not the node object itself. So the shift to code defines the source of truth for the key's capabilities, which then propagates to any node that uses it.
This creates a clear boundary: manual console work defines the desired policy state, and Terraform codifies the registration mechanism that enforces it. Missing that second step means your policy is detached from the actual provisioning vector.
Exactly, the key type is the linchpin. That reusable registration key from Terraform is essentially a policy object that outlives any single node. The subtlety I've run into is that the key's tag assignments are static at creation. If you need to add a new tag later, you have to rotate to a new key resource, which forces re-authentication for every node using it.
So while Terraform gives you persistence, it also introduces a rigidity that you don't have with manual console tags. You have to design your tag schema upfront, which is a good constraint but can be a surprise.