Post

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

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:

ComponentBitwarden OfficialVaultwarden
LanguageC#/.NETRust
Memory Usage2GB+<100MB
DatabaseMSSQLSQLite/PostgreSQL
Admin PortalRequiredOptional

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?

  1. Zero Public IP Requirements: Ideal for residential ISPs
  2. DDoS Protection: Cloudflare’s network absorbs attacks
  3. Access Policies: Geo-blocking, IP restrictions
  4. 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

  1. Outbound Access: TCP/443 to Cloudflare IPs
  2. No Inbound Ports: Tunnels use outbound-only connectivity
  3. 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 interface
  • SIGNUPS_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:

  1. Confirm tunnel status:
    1
    
    cloudflared tunnel info $CONTAINER_NAMES
    
  2. 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:

SymptomDebug CommandSolution
502 Bad Gatewaycloudflared tunnel info $CONTAINER_NAMESRestart cloudflared service
“Invalid token” errorsdocker compose logs vaultwardenRotate ADMIN_TOKEN
Database lockedfuser /opt/vaultwarden/data/db.sqlite3Rebuild 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:

  1. Documentation Debt: Poorly maintained projects become community liabilities
  2. 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_ALLOWED post-initial setup
  • Implement IP-based access controls for admin interfaces
  • Monitor Cloudflare Tunnel logs for credential stuffing attempts

For those continuing this journey:

  1. Explore Cloudflare Zero Trust for device attestation
  2. Implement Vaultwarden’s HAProxy integration for load balancing
  3. 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.

This post is licensed under CC BY 4.0 by the author.