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:
Cloud Equivalent:
Break-even: Less than 2 months. After that, pure savings.
Complete Control
Learning Opportunity
Self-hosting forces you to understand:
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
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:
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:
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.4Use 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-contribConfigure 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/photomanagerCreate 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
EOFchmod +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 -fDocker logs
docker logs -f apiNginx access logs
tail -f /var/log/nginx/access.log
Cost Comparison
My Self-Hosted Setup:
Equivalent Cloud (AWS):
Savings: $5,441/year after first year
Real-World Use Cases
I currently self-host:
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:
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:
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.
