·
One small EC2 instance replaces all your SSH tunnels, bastion hosts, and VPN clients.
You have an RDS database, an Elasticache cluster, and a DynamoDB table sitting in a private VPC. No public endpoints. To access them from your laptop, the traditional options are: SSH tunnels through a bastion host, AWS Client VPN ($0.10/hour per connection), or SSM port forwarding with arcane aws ssm start-session commands that nobody remembers.
There's a simpler approach. A Tailscale subnet router running on a small EC2 instance advertises your VPC's private CIDR range to your Tailscale network. Once it's set up, you connect to RDS, Elasticache, and DynamoDB endpoints from your laptop as if you were inside the VPC. No tunnels, no port forwarding, no VPN clients.
Launch a t4g.nano (or t3.micro) in a private subnet of your VPC. Amazon Linux 2023 or Ubuntu work fine. The instance needs outbound internet access (through a NAT gateway) so Tailscale can establish its connection.
SSH into the instance and install Tailscale:
curl -fsSL https://tailscale.com/install.sh | sh
Start Tailscale with subnet routing enabled. Replace the CIDR with your VPC's range:
sudo tailscale up --advertise-routes=10.0.0.0/16 --hostname=aws-subnet-router
This tells Tailscale to advertise the entire 10.0.0.0/16 range through this machine. Any device on your Tailscale network can now route traffic to private IPs in that range through this instance.
The instance needs IP forwarding enabled so it can route packets between Tailscale and the VPC:
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
Go to the Tailscale admin console, find your subnet router machine, and approve the advertised routes. Until you approve them, no traffic flows.
If you're using Tailscale ACLs, you may need to add an autoApprovers section to automatically approve subnet routes for specific users or groups - unapproved routes are one of the subnet routing and split DNS gotchas that come with running Tailscale on AWS.
The subnet router needs security group rules that allow it to reach your private resources. Create a security group for the subnet router (or use an existing one) and add it as an inbound source on each resource's security group.
On the RDS instance's security group, add an inbound rule:
| Type | Port | Source |
|---|---|---|
| PostgreSQL | 5432 | sg-subnet-router |
| MySQL/Aurora | 3306 | sg-subnet-router |
On the Elasticache cluster's security group:
| Type | Port | Source |
|---|---|---|
| Redis | 6379 | sg-subnet-router |
| Memcached | 11211 | sg-subnet-router |
DynamoDB is a managed service accessed via the AWS API, not a direct TCP connection - and this is the step most subnet-router guides get wrong. A VPC gateway endpoint only rewrites route tables inside the VPC; from your laptop, the standard DynamoDB hostname still resolves to public AWS IPs that sit outside the CIDR your subnet router advertises, so requests never enter the tunnel. What works from a laptop is an interface endpoint (AWS PrivateLink for DynamoDB): it creates elastic network interfaces with private IPs from your VPC subnets - addresses inside the advertised range, reachable through the subnet router like any other private resource.
Create the interface endpoint (note the explicit endpoint type):
aws ec2 create-vpc-endpoint \
--vpc-id vpc-abc123 \
--service-name com.amazonaws.us-east-1.dynamodb \
--vpc-endpoint-type Interface \
--subnet-ids subnet-abc123 \
--security-group-ids sg-endpoint
On the endpoint's security group (sg-endpoint above), allow inbound HTTPS (443) from the subnet router's security group. Unlike the free gateway endpoint, an interface endpoint is billed at standard PrivateLink rates - an hourly per-AZ charge plus per-GB data processing.
With the subnet router running and routes approved, you can now connect directly to private endpoints from your local machine. Tailscale routes the traffic transparently.
psql -h mydb.abc123.us-east-1.rds.amazonaws.com -U postgres -d myapp
That's it. No tunnel. No port forwarding. The RDS DNS name resolves to a private IP, Tailscale recognizes it's in the advertised range, and routes it through the subnet router.
redis-cli -h my-cluster.abc123.0001.use1.cache.amazonaws.com -p 6379
Same principle. The Elasticache endpoint resolves to a private IP in the VPC, and Tailscale handles the routing.
Point the AWS CLI at the interface endpoint's DNS name with --endpoint-url. When AWS creates the endpoint it generates endpoint-specific DNS names (shown in the endpoint's details); they resolve publicly to the endpoint's private VPC IPs, so no DNS changes are needed on your laptop - Tailscale sees a destination inside the advertised range and routes it through the subnet router:
aws dynamodb list-tables \
--endpoint-url https://vpce-1a2b3c4d-5e6f.dynamodb.us-east-1.vpce.amazonaws.com \
--region us-east-1
SDKs take the same value - for example endpoint_url on a boto3 client. One caution straight from AWS's documentation: do not create DNS overrides (a private hosted zone, or a Tailscale split-DNS entry) that point the default dynamodb.us-east-1.amazonaws.com hostname at the endpoint's IPs. DynamoDB's DNS configuration can change over time, and AWS warns that overrides like that can silently route requests back over public IPs. The supported path is the endpoint-specific DNS name via --endpoint-url.
If you use yaw as your terminal, you can save these database connections in the connection manager. Your RDS PostgreSQL endpoint, Elasticache Redis cluster, and any SSH hosts on the Tailscale network are all available as saved connections with one-click access.
Yaw automatically discovers Tailscale machines on your network and lets you save them as SSH connections. For database connections, add the RDS or Elasticache endpoint as a PostgreSQL, MySQL, or Redis connection in the connection manager. Next time you need to check something in production, it's one click instead of remembering an endpoint URL and typing credentials.
The subnet router is a t4g.nano: about $3/month. Tailscale is free for personal use (up to 100 devices) and starts at $5/user/month for teams. The DynamoDB interface endpoint adds a standard PrivateLink charge - an hourly per-AZ fee plus per-GB data processing, typically a few dollars a month at dev-workload volumes. That's it.
Compare that to AWS Client VPN at $0.10/hour per active connection ($73/month if you're connected 8 hours a day) or the operational overhead of maintaining bastion hosts, SSH keys, and tunnel scripts.
Enable the Tailscale service so it starts automatically:
sudo systemctl enable tailscaled
For infrastructure-as-code deployments, generate a reusable auth key in the Tailscale admin console and pass it to tailscale up --authkey=tskey-... in your user data script. No interactive login needed.
Tailscale ACLs let you control which users or groups can access the subnet routes. You might allow the engineering team to access the staging VPC but restrict production access to the SRE team. This is more granular than security groups alone.
If you have multiple VPCs (staging, production, different regions), run a subnet router in each one. Each advertises its own CIDR range. From your laptop, you can reach any of them simultaneously - something that's painful with traditional VPNs that conflict with each other's routes.
One t4g.nano replaces your bastion host, your SSH tunnel scripts, your VPN client, and the 15 minutes you spend every morning re-establishing connections. You open your terminal, type psql -h mydb.abc123.us-east-1.rds.amazonaws.com, and you're in. Private resources, no ceremony.
Published by Yaw Labs.