I Built A Janky Cloudflare Bitwarden Server For Myself Forgot About It And Woke Up To 400 Forks
I Built A Janky Cloudflare Bitwarden Server For Myself Forgot About It And Woke Up To 400 Forks
1. Introduction
The self-hosted infrastructure landscape is filled with stories of “scratch-your-own-itch” solutions that unexpectedly go viral. This is one of those tales – a cautionary yet enlightening account of how a makeshift Cloudflare + Vaultwarden (formerly Bitwarden_RS) deployment I created to bypass commercial password manager limitations accidentally became a widely-forked GitHub repository.
Modern password managers increasingly lock essential security features like TOTP 2FA and passkey management behind paywalls. When Bitwarden began enforcing email-based 2FA without adequate fallback mechanisms (creating a dangerous chicken-and-egg scenario where your email credentials depend on the very vault you can’t access), many technical users started exploring self-hosted alternatives.
This comprehensive guide examines:
- The architectural decisions behind a zero-cost Cloudflare Tunnel-based Vaultwarden deployment
- Critical security tradeoffs when self-hosting sensitive infrastructure
- How abandoned projects can inadvertently become internet infrastructure
- Best practices for maintaining secure self-hosted services
Targeting experienced DevOps engineers and sysadmins, we’ll dissect real-world infrastructure decisions through the lens of automation, security, and operational negligence. You’ll gain actionable insights for designing resilient homelab environments while avoiding the pitfalls of “set-and-forget” deployments.
Key technical focus areas include: Cloudflare Tunnels, Vaultwarden containerization, secrets management, and infrastructure-as-code practices for solo deployments.
2. Understanding the Technology Stack
What is Vaultwarden?
Vaultwarden is an unofficial Rust implementation of the Bitwarden server API, designed for resource-constrained environments. Unlike the official server requiring MSSQL and 2GB+ RAM, Vaultwarden runs efficiently in Docker containers with SQLite or PostgreSQL backends.
Key Features:
- Full Bitwarden client compatibility
- TOTP 2FA generation/validation
- Emergency access and vault export
- SMTP integration for alerts
Architecture Comparison:
| Component | Bitwarden Official | Vaultwarden |
|---|---|---|
| Language | C#/.NET | Rust |
| Memory Usage | 2GB+ | <100MB |
| Database | MSSQL | SQLite/PostgreSQL |
| Admin Portal | Required | Optional |
Cloudflare Tunnels Explained
Cloudflare Tunnels create secure outbound connections from your origin server to Cloudflare’s edge network using cloudflared. This eliminates public IP exposure and bypasses NAT/firewall configurations.
Why Tunnels for Self-Hosting?
- Zero Public IP Requirements: Ideal for residential ISPs
- DDoS Protection: Cloudflare’s network absorbs attacks
- Access Policies: Geo-blocking, IP restrictions
- Cost: Free tier suffices for personal use
Security Tradeoffs:
- TLS termination occurs at Cloudflare edge
- Cloudflare acts as MITM for HTTPS traffic
- API keys grant broad account access
3. Prerequisites
System Requirements
- CPU: x86_64 or ARMv7+ (Raspberry Pi 4 works)
- RAM: 512MB minimum (1GB recommended)
- Storage: 10GB+ (for database and backups)
- OS: Linux kernel 5.4+ (Ubuntu 22.04 LTS tested)
Software Dependencies
1
2
3
4
5
# Verified versions as of 2024-03-15
docker-ce >= 24.0.6
docker-compose-plugin >= 2.23.0
cloudflared >= 2024.2.1
sqlite3 >= 3.37.2
Network Considerations
- Outbound Access: TCP/443 to Cloudflare IPs
- No Inbound Ports: Tunnels use outbound-only connectivity
- DNS: Registered domain with Cloudflare Nameservers
Security Pre-Checks
- Disable root SSH access
- Configure UFW firewall (allow SSH only)
- Enable unattended-upgrades
- Generate SSH key pair for repository access
4. Installation & Setup
Step 1: Docker Deployment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Create persistent volume
mkdir -p /opt/vaultwarden/{data,config}
# docker-compose.yml
cat <<EOF > /opt/vaultwarden/docker-compose.yml
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:2024.2.1
container_name: vaultwarden
restart: unless-stopped
volumes:
- /opt/vaultwarden/data:/data
environment:
- ADMIN_TOKEN=$(openssl rand -base64 48)
- WEBSOCKET_ENABLED=true
- DOMAIN=https://vault.example.com
EOF
# Start container
docker compose -f /opt/vaultwarden/docker-compose.yml up -d
Critical Environment Variables:
ADMIN_TOKEN: Secures the /admin interfaceSIGNUPS_ALLOWED=false(Post-setup recommendation)INVITATIONS_ALLOWED=false
Step 2: Cloudflare Tunnel Setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Install cloudflared
wget https://github.com/cloudflare/cloudflared/releases/download/2024.2.1/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
# Authenticate
cloudflared tunnel login
# Create tunnel
cloudflared tunnel create vaultwarden
# Configure ingress
cat <<EOF > ~/.cloudflared/config.yml
tunnel: TUNNEL_UUID
credentials-file: /home/user/.cloudflared/TUNNEL_UUID.json
ingress:
- hostname: vault.example.com
service: http://localhost:80
originRequest:
noTLSVerify: true
- service: http_status:404
EOF
# Start tunnel service
sudo cloudflared service install
sudo systemctl start cloudflared
Verification Steps:
- Confirm tunnel status:
1
cloudflared tunnel info $CONTAINER_NAMES - Test WebSocket connectivity:
1
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" https://vault.example.com
5. Configuration & Optimization
Security Hardening
a) Rate Limiting in cloudflared:
1
2
3
4
5
6
7
8
# cloudflared config.yml additions
originRequest:
noTLSVerify: true
proxyType: socks
keepAliveTimeout: 30
connectTimeout: 30
tcpKeepAlive: 10
httpHostHeader: vault.example.com
b) Vaultwarden Admin Panel Restrictions:
1
2
3
# Restrict admin panel to Tailscale IP
docker compose -f /opt/vaultwarden/docker-compose.yml exec vaultwarden \
sh -c 'echo "DENY FROM ALL\nALLOW FROM 100.72.0.0/16" > /opt/vaultwarden/config/admin_ips.txt'
Performance Tuning
SQLite Optimization:
1
2
3
4
docker compose exec vaultwarden sqlite3 /data/db.sqlite3
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -10000;
Memory Limits:
1
2
3
4
5
6
7
# docker-compose.yml additions
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
6. Usage & Operations
Daily Maintenance
Backup Strategy:
1
2
3
4
5
6
# Daily encrypted backup script
docker compose -f /opt/vaultwarden/docker-compose.yml exec -T vaultwarden \
sqlite3 /data/db.sqlite3 ".backup '/data/backup.sqlite3'"
gpg --encrypt --recipient backup@example.com /opt/vaultwarden/data/backup.sqlite3
rclone copy /opt/vaultwarden/data/backup.sqlite3.gpg s3:my-bucket/vaultwarden/
Monitoring Command:
1
2
watch -n 30 'docker compose -f /opt/vaultwarden/docker-compose.yml \
logs --tail=50 | grep -E "ERROR|WARN"'
Access Management
1
2
3
4
5
6
7
# Emergency export (JSON format)
docker compose -f /opt/vaultwarden/docker-compose.yml exec vaultwarden \
./vaultwarden export --format json > /opt/vaultwarden/export.json
# 2FA reset procedure
sqlite3 /opt/vaultwarden/data/db.sqlite3 \
"UPDATE twofactor SET verified = 0 WHERE user_uuid = 'USER_UUID';"
7. Troubleshooting
Common Issues & Solutions:
| Symptom | Debug Command | Solution |
|---|---|---|
| 502 Bad Gateway | cloudflared tunnel info $CONTAINER_NAMES | Restart cloudflared service |
| “Invalid token” errors | docker compose logs vaultwarden | Rotate ADMIN_TOKEN |
| Database locked | fuser /opt/vaultwarden/data/db.sqlite3 | Rebuild SQLite indices |
Log Analysis Patterns:
1
2
3
4
5
6
# Find failed logins
docker compose -f /opt/vaultwarden/docker-compose.yml logs vaultwarden | \
grep -E 'Email:\w+@\w+\.\w+'
# Monitor brute-force attempts
journalctl -u cloudflared | grep -E 'cfd_tunnel.*src_ip=.*status=403'
8. Conclusion
The viral nature of my “janky” Vaultwarden deployment highlights two critical DevOps truths:
- Documentation Debt: Poorly maintained projects become community liabilities
- Security Through Obscurity ≠ Security: 400 forks means 400 attack surfaces
This guide demonstrates how to implement a Cloudflare-backed password manager with enterprise-grade security on a shoestring budget. Key takeaways:
- Always disable
SIGNUPS_ALLOWEDpost-initial setup - Implement IP-based access controls for admin interfaces
- Monitor Cloudflare Tunnel logs for credential stuffing attempts
For those continuing this journey:
- Explore Cloudflare Zero Trust for device attestation
- Implement Vaultwarden’s HAProxy integration for load balancing
- Study NIST SP 800-63B for auth best practices
Self-hosting critical infrastructure demands vigilance far beyond initial deployment. Set calendar reminders to audit configurations quarterly – your future self (and accidental fork maintainers) will thank you.