Introduction
Running applications on cloud providers is convenient—until you see the monthly bill. Between compute costs, bandwidth charges, and database fees, a simple side project can easily cost $50-100/month. But what if you already have powerful hardware sitting at home?
Self-hosting gives you complete control, zero recurring costs (except electricity), and the flexibility to experiment without worrying about quotas. The challenge has always been secure remote access. That's where Tailscale changes everything.
In this guide, I'll show you how I self-host my applications using Tailscale for zero-trust networking, making my home lab as accessible as any cloud service—but without the monthly bills.
Why Self-Host?
Before diving into the technical details, let's talk about why self-hosting makes sense:
Cost Savings
My Setup:
- Beelink GTi13 Ultra mini PC: $799 one-time
- Intel i9-13900HK processor (24 cores)
- 64GB DDR5 RAM
- 1TB NVMe SSD
- Power consumption: ~65W under load
Cloud Equivalent:
- AWS EC2 r6i.4xlarge: ~$700/month
- 16 vCPUs, 128GB RAM
- Plus: EBS storage, bandwidth, data transfer
Break-even: Less than 2 months. After that, pure savings.
Complete Control
- No arbitrary limits on CPU, RAM, or bandwidth
- Install anything without vendor restrictions
- Access raw logs and system metrics
- Customize networking however you need
- Keep your data physically in your possession
Learning Opportunity
Self-hosting forces you to understand:
- Linux system administration
- Networking fundamentals
- Security best practices
- Database management
- Backup strategies
These skills make you a better engineer, period.
The Traditional Problem: Port Forwarding Hell
Before Tailscale, self-hosting meant dealing with:
Security Nightmares
# The old way: exposing ports to the internet
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # SSH exposed!
Every open port is an attack surface. SSH brute force attempts, automated scanners, and bot traffic constantly hammering your server.
Network Complexity
- Router port forwarding configuration
- Dynamic DNS for changing home IP
- Certificate management for HTTPS
- Firewall rules on router AND server
- VPN setup for secure access
It works, but it's fragile and time-consuming to maintain.
Enter Tailscale: Zero-Trust Networking
Tailscale creates a secure overlay network using WireGuard. Each device gets a private IP address (100.x.y.z range), and all traffic is encrypted point-to-point.
How It Works
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Laptop │ │ Tailscale │ │ Home Lab │
│ 100.1.2.3 │────────▶│ Coordinator │◀────────│ 100.1.2.4 │
└─────────────┘ └──────────────┘ └─────────────┘
│ │
└────────────────────────────────────────────────┘
Encrypted WireGuard Tunnel
Key points:
- No port forwarding required
- No public IP exposure
- Zero-trust by default
- Automatic NAT traversal
- Encrypted everything
Setting Up Tailscale
Step 1: Install Tailscale
On your home server (Ubuntu/Debian):
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
On your laptop:
- Download from https://tailscale.com/download
- Install and sign in
Both devices now appear in your Tailscale network!
Step 2: Enable IP Forwarding (Optional)
If you want your server to act as an exit node:
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Then advertise as exit node:
sudo tailscale up --advertise-exit-node
Step 3: Set Up Magic DNS
Tailscale provides automatic DNS for your devices:
# Instead of remembering IPs:
ssh 100.1.2.4
# Use friendly names:
ssh homelab.tail-scale.ts.net
Enable in the Tailscale admin console under DNS settings.
Deploying Applications
Now the fun part—actually running your applications!
Example 1: PostgreSQL Database
# Install PostgreSQL
sudo apt update
sudo apt install postgresql postgresql-contrib
# Configure to listen on Tailscale IP only
sudo nano /etc/postgresql/16/main/postgresql.conf
Change:
listen_addresses = '100.1.2.4' # Your Tailscale IP
Update authentication:
sudo nano /etc/postgresql/16/main/pg_hba.conf
Add:
host all all 100.0.0.0/8 scram-sha-256
Restart:
sudo systemctl restart postgresql
Now your database is accessible from any Tailscale device but invisible to the internet!
Example 2: .NET Core Web API
Here's how I deployed my PhotoManagerAPI:
# Publish the application
dotnet publish -c Release -o /var/www/photomanager
# Create systemd service
sudo nano /etc/systemd/system/photomanager.service
Service file:
[Unit]
Description=Photo Manager API
After=network.target
[Service]
Type=notify
User=www-data
WorkingDirectory=/var/www/photomanager
ExecStart=/usr/bin/dotnet /var/www/photomanager/PhotoManagerAPI.dll
Restart=always
RestartSec=10
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://100.1.2.4:5000
[Install]
WantedBy=multi-user.target
Start the service:
sudo systemctl enable photomanager
sudo systemctl start photomanager
Access from any device: http://homelab.tail-scale.ts.net:5000
Example 3: Docker Containers
Run multiple services with Docker:
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
ports:
- "100.1.2.4:5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
api:
build: .
environment:
- ConnectionStrings__DefaultConnection=${CONN_STRING}
- ASPNETCORE_URLS=http://100.1.2.4:5000
depends_on:
- postgres
nginx:
image: nginx:alpine
ports:
- "100.1.2.4:80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
volumes:
postgres_data:
Deploy:
docker-compose up -d
Advanced Features
ACL Rules
Control who can access what:
{
"acls": [
{
"action": "accept",
"src": ["tag:dev"],
"dst": ["tag:servers:*"]
},
{
"action": "accept",
"src": ["user@example.com"],
"dst": ["homelab:5000,5432"]
}
],
"tagOwners": {
"tag:dev": ["user@example.com"],
"tag:servers": ["user@example.com"]
}
}
Subnet Routes
Share your entire home network:
sudo tailscale up --advertise-routes=192.168.1.0/24
Now access all home devices through Tailscale!
MagicDNS + HTTPS
Use Tailscale's built-in HTTPS certificates:
tailscale cert homelab.tail-scale.ts.net
Configure your web server:
server {
listen 443 ssl;
server_name homelab.tail-scale.ts.net;
ssl_certificate /root/.tailscale-certs/homelab.tail-scale.ts.net.crt;
ssl_certificate_key /root/.tailscale-certs/homelab.tail-scale.ts.net.key;
location / {
proxy_pass http://localhost:5000;
}
}
Security Best Practices
Even with Tailscale, follow these guidelines:
1. Principle of Least Privilege
# Don't run everything as root
sudo useradd -m -s /bin/bash appuser
sudo chown -R appuser:appuser /var/www/myapp
2. Firewall Configuration
# Only allow Tailscale interface
sudo ufw default deny incoming
sudo ufw allow in on tailscale0
sudo ufw enable
3. Regular Updates
# Auto-update script
cat << 'EOF' > /usr/local/bin/update-system.sh
#!/bin/bash
apt update
apt upgrade -y
apt autoremove -y
systemctl restart tailscaled
EOF
chmod +x /usr/local/bin/update-system.sh
# Add to cron
echo "0 3 * * 0 /usr/local/bin/update-system.sh" | sudo crontab -
4. Backup Everything
# Automated PostgreSQL backup
cat << 'EOF' > /usr/local/bin/backup-db.sh
#!/bin/bash
BACKUP_DIR="/backups/postgres"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
pg_dump -U postgres photomanager > $BACKUP_DIR/backup_$DATE.sql
# Keep only last 7 days
find $BACKUP_DIR -name "backup_*.sql" -mtime +7 -delete
EOF
chmod +x /usr/local/bin/backup-db.sh
echo "0 2 * * * /usr/local/bin/backup-db.sh" | sudo crontab -
Monitoring and Maintenance
System Monitoring
I use a simple bash script for monitoring:
#!/bin/bash
# /usr/local/bin/system-health.sh
CPU=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
MEM=$(free | grep Mem | awk '{print ($3/$2) * 100.0}')
DISK=$(df -h / | awk 'NR==2 {print $5}' | cut -d'%' -f1)
echo "CPU: ${CPU}%"
echo "Memory: ${MEM}%"
echo "Disk: ${DISK}%"
# Alert if any threshold exceeded
if (( $(echo "$CPU > 80" | bc -l) )); then
echo "WARNING: High CPU usage!"
fi
Application Logs
# View application logs
sudo journalctl -u photomanager -f
# Docker logs
docker logs -f api
# Nginx access logs
tail -f /var/log/nginx/access.log
Cost Comparison
My Self-Hosted Setup:
- Hardware: $799 (one-time)
- Electricity: ~$10/month (65W * 24h * $0.15/kWh)
- Internet: Already paying for it
- Total Year 1: $919
- Total Year 2+: $120/year
Equivalent Cloud (AWS):
- EC2 r6i.2xlarge: $350/month
- RDS PostgreSQL: $150/month
- EBS Storage: $30/month
- Total: ~$530/month = $6,360/year
Savings: $5,441/year after first year
Real-World Use Cases
I currently self-host:
- PhotoManagerAPI: Photography business management
- Personal blog: Angular + .NET Core
- PostgreSQL databases: Multiple projects
- Development environments: Testing new features
- Game servers: Occasional Minecraft server for friends
Total monthly cloud cost if hosted elsewhere: ~$800 Actual cost: $10 electricity
Conclusion
Self-hosting with Tailscale gives you cloud-like convenience with none of the recurring costs. The initial setup takes a weekend, but then it just works—securely and reliably.
Is it for everyone? No. If you value your time at $200/hour and hate sysadmin work, cloud hosting makes sense. But if you enjoy learning, want complete control, and have capable hardware, self-hosting is incredibly rewarding.
Key Takeaways:
- Tailscale eliminates traditional self-hosting complexity
- Security is actually better than basic cloud setups
- Cost savings are massive for long-term projects
- You learn invaluable systems administration skills
Ready to start self-hosting? Install Tailscale, spin up a VM or use old hardware, and deploy your first app. You might never go back to paying monthly cloud bills.
My Setup:
- Beelink GTi13 Ultra (i9-13900HK, 64GB RAM)
- Ubuntu 24.04 LTS
- Docker + Docker Compose
- PostgreSQL 16
- Tailscale for networking
- Automated backups to external drive
Questions about self-hosting? Reach out—I love helping people break free from cloud costs!
Related Posts
Building with Angular + Firebase? Check out my Photography E-Commerce Platform that combines Firebase auth with Stripe payments and AWS S3 storage.
Self-hosting your projects? Learn how I host production apps on a Raspberry Pi with PM2, Nginx, and Tailscale.
About the Author: Ricardo Gil is a full-stack software engineer specializing in .NET/C#, Angular, and cloud platforms. Read more or subscribe for updates.
Want this kind of setup for your business?
I deploy private, self-hosted infrastructure on Proxmox: AI, automation and internal tools on hardware you own, documented and handed off. See the Private AI Stack →
Not sure what you need? Book a free discovery call.
