featured image

Taking Back the Keys: Escaping Subscription Fatigue with a Zero-Trust Password Vault

This tutorial guides you through setting up a self-hosted Vaultwarden instance on TrueNAS Scale, emphasizing data sovereignty and zero-trust security principles.

Published

Fri Nov 14 2025

Technologies Used

Vaultwarden TrueNAS Scale ZFS Tailscale Docker
Beginner 11 minutes

The digital landscape has forced us into a corner of subscription fatigue and surrendered data sovereignty. Relying on SaaS platforms for password management means placing the literal keys to your digital life into the hands of third-party conglomerates. As highlighted in our overarching architectural review, repurposing legacy hardware into a personal cloud allows us to reclaim this independence.

However, hosting a critical security application on a home network bridges a terrifying gap between convenience and vulnerability. In this deep dive, we will engineer a self-hosted instance of Vaultwarden (a lightweight, community-driven implementation of the Bitwarden API) on TrueNAS Scale. We will build a highly performant deployment that binds containerized applications to self-healing ZFS datasets, completely hardens the admin perimeter, and routes all traffic through a zero-trust Tailscale mesh network.


The Self-Host Stack: Arming Your Hardware for Data Sovereignty

Before we begin provisioning storage and orchestrating containers, you must understand the environment we are building within. We are bypassing traditional reverse-proxy setups (like Nginx or Traefik) and public port-forwarding to adopt an “invisible” zero-trust posture.

Knowledge Base:

  • Container Volumes vs. Bind Mounts: You should understand how Docker maps internal container paths to persistent host-level directories.
  • ZFS Dataset Hierarchies: Familiarity with creating and managing datasets on TrueNAS Scale, specifically understanding POSIX ACLs (Access Control Lists).
  • Mesh VPN Routing: An understanding of how Tailscale creates a peer-to-peer Tailnet using WireGuard under the hood.

Environment Prerequisites:

  • Host OS: TrueNAS Scale (ElectricEel or newer, supporting native Docker/compose via tools like Dockge).
  • Storage: A configured ZFS Pool (e.g., tank).
  • Networking: Tailscale installed on the TrueNAS host, exposing the local subnet or running as an exit node.
  • Client Tools: The official Bitwarden Browser Extension, and a secure terminal emulator to access the TrueNAS shell.

Architectural Anatomy: Weaving Storage, Containers, and Mesh Networks

To successfully deploy Vaultwarden, we must decouple the application logic from the persistent state. TrueNAS Scale provides the ZFS storage backbone, while Docker isolates the application runtime.

Think of this architecture like a high-security digital bank vault: ZFS is the self-healing, titanium floor ensuring the data never degrades; the Vaultwarden container is the robotic teller handling requests; and Tailscale is a private, invisible, encrypted tunnel leading directly from your pocket to the teller, entirely bypassing public roads.

graph TD
    subgraph "External World (Zero-Trust)"
        Client[Client Device\nBitwarden Extension]
    end

    subgraph "Tailscale Mesh VPN (Tailnet)"
        Client -- "WireGuard Encrypted Tunnel" --> TS_Node[TrueNAS Tailscale Interface\n100.x.x.x]
    end

    subgraph "TrueNAS Scale Host Environment"
        TS_Node -- "Port 80/443" --> VW_Container((Vaultwarden\nDocker Container))
        
        subgraph "ZFS Pool (tank)"
            VW_Container -- "Read/Write Config" --> DS_Config[Dataset: /config\nApps Permissions]
            VW_Container -- "Read/Write Database" --> DS_DB[Dataset: /db\nGeneric Permissions]
        end
    end
    
    style TS_Node fill:#4d4d4d,stroke:#fff,stroke-width:2px,color:#fff
    style VW_Container fill:#2a75d3,stroke:#fff,stroke-width:2px,color:#fff
    style Client fill:#2a75d3,stroke:#fff,stroke-width:2px,color:#fff
    style DS_Config fill:#0e5a35,stroke:#fff,color:#fff
    style DS_DB fill:#0e5a35,stroke:#fff,color:#fff

Forging the Vault: Dataset Provisioning and Container Orchestration

Our implementation requires precise mapping between our TrueNAS file system and the Docker container. Rather than relying entirely on TrueNAS’s GUI abstraction, we will explore the underlying Docker Compose logic—which is what tools like Dockge execute under the hood.

1. Defining the Core Service and Storage

First, we define our service. Vaultwarden requires persistent storage so that container restarts don’t result in catastrophic data loss. In TrueNAS, you create a parent dataset (e.g., vaultwarden) and two child datasets: config (with Apps permissions) and db (Generic permissions).

# docker-compose.yml
version: '3.8'

services:
  vaultwarden:
    # Use the latest Alpine Linux build for the smallest attack surface
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    ports:
      # Map host port 8080 to container port 80
      - "8080:80"
    volumes:
      # Map our isolated TrueNAS ZFS datasets into the container
      - /mnt/tank/apps/vaultwarden/config:/data
      - /mnt/tank/apps/vaultwarden/db:/data/db

2. Injecting Environment Variables (The Naive Approach)

To configure the application initially, we must set up the ADMIN_TOKEN—a cryptographic string that grants access to the /admin panel.

    environment:
      # 🔴 NAIVE APPROACH: Hardcoding the token in plaintext Compose.
      # While necessary for initial setup, leaving this here is dangerous.
      - ADMIN_TOKEN=$$argon2id$$v=19$$m=65536...[REDACTED]...
      - WEBSOCKET_ENABLED=true
      # Force the server to use Tailscale or a specific domain
      - DOMAIN=http://10.99.0.191:8080 

3. Securing the perimeter (The Refined Solution)

Once you log into the admin panel (e.g., http://10.99.0.191:8080/admin), you must immediately disable “Allow new signups”. However, simply removing the ADMIN_TOKEN from the Docker Compose file or TrueNAS UI does not actually disable the admin panel if Vaultwarden has already cached it.

We must access the TrueNAS shell to surgically remove the cached token from the application’s brain.

# Navigate to the ZFS dataset bound to the container's /data directory
cd /mnt/tank/apps/vaultwarden/config

# Open the Vaultwarden-generated configuration state file
nano config.json

# --- INSIDE config.json ---
# Scroll down and locate the "admin_token" key-value pair.
# "admin_token": "$argon2id$v=19$m=65...",  <-- DELETE THIS ENTIRE LINE
# --------------------------

# Restart the container to flush the memory and apply the stripped config
docker restart vaultwarden

💡 Pro-Tip: For maximum security, do not use an admin token at all after initial setup. If you ever need to access the admin panel again, inject a fresh hash into config.json, restart the container, make your changes, and immediately delete the token again.


Rust Foundations and ZFS Resilience: Why Vaultwarden Outperforms

Moving from how to construct this server to why it works so well reveals the brilliance of upcycling older hardware with modern software paradigms.

🔵 Deep Dive: The Memory Footprint of Rust vs. C# The official Bitwarden server infrastructure is written in C# and relies on heavily orchestrated MS SQL databases. It expects enterprise-grade memory availability (often requiring 4GB+ of RAM just to idle). Vaultwarden is written in Rust, a systems programming language with no garbage collector. By managing memory safely at compile-time and utilizing the lightweight Rocket web framework, Vaultwarden operates with an incredibly minimal heap size. It can serve hundreds of concurrent websocket connections on a 9-year-old CPU using less than 150MB of RAM.

ZFS Copy-on-Write (CoW) and Database Integrity: Vaultwarden natively utilizes SQLite (or PostgreSQL, if explicitly configured). On standard filesystems like EXT4, database writes modify blocks in place. If the system loses power during a write, the database can corrupt. TrueNAS utilizes ZFS, which operates on a Copy-on-Write transactional model. When the database updates a record, ZFS writes the new data to a new block on the disk, and only updates the filesystem pointer once the write is 100% verified. This means your password vault database mathematically cannot be caught in an incomplete write state, effectively eliminating bit-rot and silent corruption.


Battle-Testing the Perimeter: Threat Mitigation and Data Integrity

Self-hosting a security product is not a “set-and-forget” endeavor. You must aggressively anticipate failure states and malicious actors.

The False Sense of Configuration Security

🔴 Danger: The most critical edge case covered in our implementation is the config.json override behavior. Vaultwarden uses a hierarchy for its settings. If you apply a setting via the Web Admin UI, it writes that setting to config.json, which overrides any environment variables set in your TrueNAS UI or Docker Compose file. If an attacker discovers your open port, and you falsely believe the admin panel is disabled because you removed the environment variable, they can still access it using cached cookies or brute force if the config.json wasn’t manually purged.

Network Concurrency and Brute Force Mitigation

Because we rely strictly on Tailscale, we bypass traditional DDoS and brute-force vectors. If Vaultwarden were exposed via port-forwarding (e.g., port 443 open on your router), bots would bombard the login endpoint endlessly. By placing the container entirely behind a Tailnet, the server simply drops any packet that doesn’t carry a valid WireGuard cryptographic signature. The application code never even has to process the malicious traffic, saving CPU cycles and entirely eliminating the risk of zero-day exploits in Vaultwarden’s public-facing login portal.

Failsafes and Disasters

If the TrueNAS server experiences catastrophic hardware failure, standard RAID only provides disk-level redundancy, not disaster recovery. Because our database (db) and configurations (config) are segmented into explicit ZFS datasets, we can automate localized snapshotting. You can schedule TrueNAS to take a recursive, read-only ZFS snapshot of the vaultwarden dataset every 15 minutes. If a bad update breaks the container, or you accidentally delete your master vault, you can roll back the entire directory state in milliseconds.


Master of the Keep: Your Sovereign Password Infrastructure

You have successfully traversed the gap from a high-level conceptual overview to a production-ready, highly secure deployment. You now know how to provision persistent ZFS storage, orchestrate a Rust-based Docker container, inject and safely revoke cryptographic admin tokens, and isolate the entire architecture behind a zero-trust mesh network.

By binding Vaultwarden to TrueNAS and Tailscale, you have done more than just save a monthly subscription fee. You have fundamentally transformed legacy silicon into an enterprise-grade, cryptographically secure data vault, successfully executing on the promise of true digital sovereignty.

We respect your privacy.

← View All Tutorials

Related Projects

    Ask me anything!