Deploying Vaultwarden with Docker Compose and Caddy for Automatic HTTPS
Learn how to deploy Vaultwarden with Docker Compose and Caddy to enable automatic HTTPS, ensuring a secure and efficient self-hosted password manager.
Disclosure: this article contains an affiliate link. DotExe may earn a commission if you purchase through it, at no extra cost to you. Any discount is subject to Hostinger's current offers (20% OFF) and eligibility terms.
Self-hosting a password manager is one of the most impactful projects for personal data security. Vaultwarden, a lightweight implementation of the Bitwarden API written in Rust, provides a robust, resource-efficient alternative to the official Bitwarden server. Because Vaultwarden handles sensitive credentials, a secure deployment is not optional—it is a requirement. This guide walks you through how to deploy Vaultwarden with Docker Compose and Caddy, a modern web server that automates SSL/TLS certificate management, ensuring your instance is encrypted and accessible via HTTPS.
Understanding the Architecture
To deploy Vaultwarden securely, we separate the application logic from the web-facing traffic. Vaultwarden itself does not natively handle SSL/TLS termination effectively for public-facing production environments. Instead, we place a reverse proxy in front of it. Caddy is an excellent choice here because it automatically requests, renews, and manages Let's Encrypt certificates, removing the manual overhead of managing SSL configurations.
In this setup, the user connects to your server over HTTPS. Caddy terminates the connection, verifies the domain, and forwards the request to the internal Vaultwarden container. This architecture ensures that your password data is encrypted in transit while keeping the Vaultwarden container isolated from direct public exposure. By abstracting the TLS layer, you ensure that your application logic remains focused on managing credentials while the proxy handles the complexities of web traffic, rate limiting, and encryption.
Prerequisites and Assumptions
Before you begin, ensure you have the following:
- A Linux server with Docker and Docker Compose installed.
- A domain name (e.g.,
vault.example.com) pointed to your server's public IP address via an A record. - Port 80 and 443 open on your firewall to allow Caddy to perform the ACME challenge for SSL certificates.
- A basic understanding of Linux filesystem permissions, as you will need to manage directories for persistent data storage.
Configuring the Docker Compose Environment
We will use a single docker-compose.yml file to manage both the Vaultwarden application and the Caddy proxy. This ensures that both services share the same network, allowing them to communicate securely without exposing ports to the host's public interface unnecessarily. Using a single file also simplifies lifecycle management, allowing you to bring the entire stack up or down with a single command.
Create a directory for your project and add the following docker-compose.yml file:
version: '3.8'
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: always
environment:
- SIGNUPS_ALLOWED=false
- DOMAIN=https://vault.example.com
- DATABASE_URL=sqlite:///data/db.sqlite3
volumes:
- ./vw-data:/data
caddy:
image: caddy:latest
container_name: caddy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_data:/data
- ./caddy_config:/config
depends_on:
- vaultwarden
Key Configuration Details
- SIGNUPS_ALLOWED: Set this to
falseonce you have created your initial account to prevent unauthorized users from registering on your instance. This is a critical security step for any private password manager. - Volumes: We map the local
./vw-datadirectory to the container. This is critical for data persistence; without it, your vault would be wiped every time the container restarts. See Implementing Docker Compose Persistent Storage for Spring Boot for more on why persistent volumes are the foundation of reliable containerized services. - Caddy Networking: By placing both services in the same
docker-composefile, they automatically join a default network, allowing Caddy to reachvaultwardenby its service name. This avoids the need to expose the Vaultwarden container to the host's network interfaces directly.
Setting Up the Caddyfile
Caddy uses a configuration file called the Caddyfile to define how it should handle incoming requests. Create a file named Caddyfile in the same directory as your docker-compose.yml:
vault.example.com {
reverse_proxy vaultwarden:80
}
Replace vault.example.com with your actual domain. When Caddy starts, it will detect this domain, contact Let's Encrypt, and automatically provision an SSL certificate. This is the core of the "automatic HTTPS" feature that makes Caddy so powerful for self-hosting. If you need to add custom headers or security policies, you can easily extend this file with directives like header or request_header to further tighten your security posture.
Launching and Verifying the Deployment
With the files in place, deploy the stack using the following command:
docker compose up -d
To verify that the containers are running correctly, use docker compose ps. If a container exits immediately, you can troubleshoot the issue by following the steps in Debugging a Docker Container That Exits Immediately After It Starts. Check the logs using docker compose logs -f to see the initialization process. You should see Caddy outputting messages related to ACME certificate issuance. Once the containers are up, navigate to your domain in a web browser. You should see the Vaultwarden login screen, and your browser should indicate a secure connection (the padlock icon). If you encounter issues, check the Caddy logs with docker compose logs caddy to see if there were any errors during the certificate issuance process.
Operational Considerations and Maintenance
Data Backups
Since Vaultwarden stores your passwords in a SQLite database by default, backing up the /data directory is straightforward but requires care. Ensure you have a strategy for Designing Self-Hosted Backups You Can Actually Restore. For Vaultwarden specifically, you should periodically copy the db.sqlite3 file to a secure, off-site location. Because SQLite is a file-based database, a simple file copy while the container is running might lead to a corrupted backup. It is recommended to use the sqlite3 command-line tool to perform a hot backup or pause the container briefly during the backup process to ensure data consistency.
Security Hardening
While HTTPS provides encryption in transit, you should also consider Hardening Your Linux Server: A Practical Checklist for Production Security to protect the host machine itself. Disable root SSH login, use a firewall like UFW to restrict access, and keep your Docker images updated to patch potential vulnerabilities. Consider implementing fail2ban to mitigate brute-force attempts against your login page. Furthermore, ensure that your Docker host is running a supported kernel and that you are monitoring system resources to detect anomalous behavior that might indicate a compromise.
Scaling and Performance
For most home users, the default configuration is sufficient. However, if you plan to share your instance or expect high traffic, you might need to look into Vaultwarden High Availability: Scaling for Production Reliability. Note that high availability adds significant complexity, including the need for a PostgreSQL database instead of the default SQLite. For standard setups, monitoring memory usage is important; if you notice high memory consumption, investigate the logs for potential memory leaks or excessive background tasks. You can use tools like Netdata to monitor the health of your Docker containers in real-time, as detailed in Monitor Docker Containers with Netdata.
Managing Resource Limits
To ensure your host system remains stable, it is best practice to define resource constraints for your containers. By default, Docker containers can consume as much memory and CPU as the host kernel allows. You can add deploy constraints to your docker-compose.yml to prevent a single container from starving the host of resources. For example, limiting the memory of the Vaultwarden container can prevent OOM (Out of Memory) kills from crashing your entire server. Always test your application under load to determine the appropriate limits for your environment.
Troubleshooting Common Issues
If you find that your domain is not resolving or Caddy is failing to issue certificates, verify your DNS settings. Ensure that the A record is pointing to the correct public IP and that no other service is occupying ports 80 or 443. If you are behind a NAT or router, ensure that port forwarding is correctly configured. Another common issue involves firewall rules; verify that your cloud provider's firewall (like AWS Security Groups or GCP Firewall Rules) allows inbound traffic on these ports. If you are using a local firewall like UFW, ensure you have explicitly allowed traffic to these ports.
When troubleshooting networking, the docker network commands can be invaluable. You can inspect the network created by Docker Compose to ensure that the vaultwarden and caddy containers are on the same subnet and can communicate. If they cannot resolve each other by service name, check the logs for any errors related to DNS resolution within the container. Remember that Caddy needs to be able to reach the internet to communicate with Let's Encrypt's servers; if your server has restricted outbound access, this will cause certificate issuance to fail.
Affiliate Resource
If you are looking for a reliable foundation for your self-hosted projects, consider exploring Hostinger Web Hosting. They offer various plans that can serve as a starting point for your homelab journey.
Disclosure: this article contains an affiliate link. DotExe may earn a commission if you purchase through it, at no extra cost to you. Any discount is subject to Hostinger's current offers (20% OFF) and eligibility terms.
Conclusion
Deploying Vaultwarden with Docker Compose and Caddy provides a secure, automated, and professional-grade password management solution. By leveraging Caddy's automatic SSL capabilities, you eliminate the manual burden of certificate management, allowing you to focus on maintaining your data. Remember to keep your software updated, perform regular backups, and secure your host machine to ensure your vault remains a safe repository for your credentials. The combination of Docker's isolation and Caddy's simplicity makes this stack highly maintainable for both beginners and experienced sysadmins.
Frequently Asked Questions
Why do I need a reverse proxy like Caddy for Vaultwarden?
Vaultwarden does not natively handle SSL/TLS certificate management, which is required for secure HTTPS communication. A reverse proxy like Caddy sits in front of the application to terminate SSL connections, handle certificate renewals automatically, and forward traffic to the internal container.
Is the data in my Vaultwarden instance encrypted?
Yes, Vaultwarden encrypts all data at rest using AES-256 bit encryption, and the connection between your browser and the server is encrypted in transit via HTTPS. This dual-layer approach ensures that your passwords remain private even if the network traffic is intercepted or the database file is accessed.
Can I use a domain name I already own for this setup?
Yes, you can use any domain you own by creating an A record in your DNS provider's dashboard that points to your server's public IP address. Caddy will use this domain to verify your identity with Let's Encrypt and generate the necessary SSL certificates automatically.
What happens if the Caddy container restarts?
Caddy is designed to persist its configuration and certificates in the volumes defined in your docker-compose.yml file. When the container restarts, it will load the existing certificates from the caddy_data volume, ensuring that your service remains accessible without needing to re-verify your domain.