tailscale-deploy
Deploy F# applications with Tailscale sidecar for private network access.Use when: "deploy", "production", "Tailscale", "docker-compose", "Docker","Portainer", "private network", "deployment", "ship it", "go live","home server", "self-host", "container", "release".Creates docker-compose.yml with app + Tailscale sidecar for secure access.No public ports, no authentication needed - Tailscale handles it.
When & Why to Use This Skill
This Claude skill automates the secure deployment of F# applications using a Tailscale sidecar pattern within Docker Compose. It enables developers to establish private network access for their containers without exposing public ports, leveraging WireGuard encryption to simplify remote access to home servers, Portainer stacks, and production environments.
Use Cases
- Deploying F# web applications to a home server or private cloud while maintaining secure remote access without the need for complex port forwarding or firewall rules.
- Setting up internal-only corporate tools or dashboards that require identity-based access control provided by Tailscale's networking layer.
- Generating production-ready docker-compose.yml configurations that include automated VPN sidecars, health checks, and persistent data volume management for F# projects.
- Streamlining the 'Ship It' process for developers who need to move from local F# development to a secured, networked environment with minimal manual configuration.
| name | tailscale-deploy |
|---|---|
| description | | |
| Use when | "deploy", "production", "Tailscale", "docker-compose", "Docker", |
| allowed-tools | Read, Edit, Write, Grep, Bash |
Tailscale Sidecar Deployment
When to Use This Skill
Activate when:
- User requests "deploy to production", "set up Tailscale"
- Need to create Docker deployment configuration
- Setting up private networking for F# application
- Deploying to Portainer or home server
- User mentions "docker-compose", "deployment", or "Tailscale"
Prerequisites
- Docker and Docker Compose installed
- Tailscale account (https://tailscale.com)
- F# application with Dockerfile (multi-stage build)
- Target deployment server (Linux recommended)
Architecture
Internet
↓ (blocked - no public ports)
Tailscale Network (WireGuard encrypted)
↓
Tailscale Sidecar Container
↓ (internal network)
F# Application Container
Key Benefits:
- No public ports exposed
- No authentication needed (Tailscale handles it)
- Access from any device on your Tailnet
- Encrypted WireGuard connections
- Device authorization at network level
docker-compose.yml
Location: Project root
version: '3.8'
services:
# F# Application
app:
build: .
container_name: my-fsharp-app
restart: unless-stopped
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:5000
volumes:
# Persist SQLite database and files
- ./data:/app/data
networks:
- app-network
depends_on:
- tailscale
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
# Tailscale Sidecar
tailscale:
image: tailscale/tailscale:latest
container_name: my-fsharp-app-tailscale
hostname: my-fsharp-app
restart: unless-stopped
environment:
- TS_AUTHKEY=${TS_AUTHKEY}
- TS_STATE_DIR=/var/lib/tailscale
- TS_HOSTNAME=my-fsharp-app
- TS_ACCEPT_DNS=true
- TS_USERSPACE=false
volumes:
- tailscale-data:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
networks:
- app-network
healthcheck:
test: ["CMD", "tailscale", "status"]
interval: 30s
timeout: 5s
retries: 3
networks:
app-network:
driver: bridge
volumes:
tailscale-data:
Configuration Notes:
hostname- Name that appears in Tailscale admin consoleTS_HOSTNAME- DNS name on Tailnet (access viahttp://my-fsharp-app)TS_AUTHKEY- One-time auth key from Tailscale admin/dev/net/tun- Required for Tailscale networkingcap_add- Required Linux capabilities for VPN
Environment Configuration
.env File
Location: Project root (same directory as docker-compose.yml)
# Tailscale authentication key
TS_AUTHKEY=tskey-auth-xxxxxxxxxxxxx
# Application settings (optional)
ASPNETCORE_ENVIRONMENT=Production
Important:
- Add
.envto.gitignore(never commit secrets) - Auth key is one-time use
- Generate new key for each deployment
Generate Tailscale Auth Key
- Go to https://login.tailscale.com/admin/settings/keys
- Click "Generate auth key"
- Settings:
- ✅ Reusable (if deploying multiple times)
- ✅ Ephemeral (device removed when container stops)
- Set expiration (recommend 90 days)
- Copy key to
.envfile
Deployment Steps
Local Development Testing
# 1. Build application
dotnet build
# 2. Build Docker image
docker build -t my-fsharp-app:latest .
# 3. Start stack
docker-compose up -d
# 4. Check logs
docker-compose logs -f
# 5. Verify Tailscale connection
docker exec my-fsharp-app-tailscale tailscale status
# 6. Access application
# Open browser: http://my-fsharp-app (from any device on Tailnet)
Production Deployment (Portainer)
Option 1: Using Portainer UI
- Login to Portainer (usually
http://server:9000) - Go to Stacks → Add stack
- Name:
my-fsharp-app - Build method: Web editor
- Paste
docker-compose.ymlcontent - Environment variables:
- Add:
TS_AUTHKEY=tskey-auth-xxxxx
- Add:
- Click Deploy the stack
Option 2: Using Git Repository
- Push docker-compose.yml to Git repository
- In Portainer: Stacks → Add stack
- Build method: Repository
- Enter Git URL and branch
- Set environment variables
- Enable Automatic updates (optional)
- Click Deploy the stack
Verify Deployment
# Check container status
docker ps
# Check app logs
docker logs my-fsharp-app
# Check Tailscale logs
docker logs my-fsharp-app-tailscale
# Verify Tailscale connection
docker exec my-fsharp-app-tailscale tailscale status
# Check health
docker inspect my-fsharp-app | grep -A 5 Health
# Test app endpoint
curl http://localhost:5000/health # From server
curl http://my-fsharp-app/health # From Tailnet device
Accessing the Application
From Tailnet Devices
Any device on your Tailnet can access:
http://my-fsharp-app
http://my-fsharp-app:5000
Install Tailscale on devices:
- Desktop: https://tailscale.com/download
- Mobile: App Store / Google Play
- Login with same Tailscale account
From Server (Local)
# Use localhost
curl http://localhost:5000
# Or use Tailscale hostname
curl http://my-fsharp-app:5000
Updating the Application
Update Code and Redeploy
# 1. Pull latest code
git pull origin main
# 2. Rebuild Docker image
docker-compose build
# 3. Restart containers
docker-compose up -d
# 4. Check logs
docker-compose logs -f app
In Portainer
- Go to Stacks → your stack
- Click Editor
- Click Update the stack
- Select Re-pull image and redeploy
- Click Update
Troubleshooting
Tailscale Connection Issues
# Check Tailscale status
docker exec my-fsharp-app-tailscale tailscale status
# Check Tailscale logs
docker logs my-fsharp-app-tailscale
# Verify /dev/net/tun exists
ls -la /dev/net/tun
# Restart Tailscale container
docker restart my-fsharp-app-tailscale
# Check network capabilities
docker exec my-fsharp-app-tailscale ip addr show
Common Issues:
| Issue | Solution |
|---|---|
/dev/net/tun not found |
Host needs TUN/TAP support: modprobe tun |
| Permission denied | Container needs NET_ADMIN capability |
| Auth key expired | Generate new key in Tailscale admin |
| Can't reach app | Check app is running: docker logs my-fsharp-app |
Application Issues
# Check app is running
docker ps | grep my-fsharp-app
# Check app logs
docker logs my-fsharp-app --tail 100
# Check app health
docker exec my-fsharp-app curl -f http://localhost:5000/health
# Check data volume
docker exec my-fsharp-app ls -la /app/data
# Restart app
docker restart my-fsharp-app
Network Issues
# Check network exists
docker network ls | grep app-network
# Inspect network
docker network inspect app-network
# Check containers can communicate
docker exec my-fsharp-app-tailscale ping -c 3 my-fsharp-app
Data Persistence Issues
# Check volume exists
docker volume ls | grep tailscale
# Check data directory
ls -la ./data
# Fix permissions (if needed)
sudo chown -R $(id -u):$(id -g) ./data
Multiple Applications
Deploy multiple F# apps on same server:
# app1/docker-compose.yml
services:
app:
container_name: todo-app
# ... config
tailscale:
container_name: todo-app-tailscale
hostname: todo-app
environment:
- TS_HOSTNAME=todo-app
# ... config
# app2/docker-compose.yml
services:
app:
container_name: notes-app
# ... config
tailscale:
container_name: notes-app-tailscale
hostname: notes-app
environment:
- TS_HOSTNAME=notes-app
# ... config
Access:
http://todo-apphttp://notes-app
Best Practices
✅ Do
- Use unique hostnames for each application
- Set auth key expiration (security)
- Use ephemeral keys for temporary deployments
- Monitor Tailscale connection health
- Keep Tailscale image updated
- Persist data in volumes (not containers)
- Use health checks
- Test locally before production deployment
❌ Don't
- Expose ports to public internet (defeats Tailscale purpose)
- Commit
.envfile to Git - Reuse auth keys across environments
- Run without
NET_ADMINcapability - Forget to mount
/dev/net/tun - Skip health checks
- Deploy without testing locally first
Security Considerations
Tailscale Provides:
- Network-level authentication (device authorization)
- WireGuard encryption
- Access control via Tailscale ACLs
- Automatic certificate management
Application Still Needs:
- Input validation (SQL injection, XSS prevention)
- Authorization (who can do what in the app)
- Secure data storage
- Audit logging
Tailscale Admin:
- Review connected devices regularly
- Disable/remove unused devices
- Use ACLs to restrict access by group
- Enable MFA for Tailscale account
Verification Checklist
-
docker-compose.ymlcreated -
.envfile created withTS_AUTHKEY -
.envadded to.gitignore - Tailscale auth key generated
- Application Dockerfile exists
- Data directory created
- Containers start successfully
- Tailscale shows connected
- Application accessible via Tailscale hostname
- Health checks passing
- Logs show no errors
Related Skills
- fsharp-feature - Complete application to deploy
- fsharp-backend - Backend that gets deployed
- fsharp-frontend - Frontend that gets deployed
Related Documentation
/docs/07-BUILD-DEPLOY.md- Docker build and deployment guide/docs/08-TAILSCALE-INTEGRATION.md- Detailed Tailscale integrationCLAUDE.md- Deployment model overview