Building a Self-Hosted Passwordless Authentication Server with Zitadel on a VPS: A Comprehensive Guide
Introduction to Modern Identity Management
In the contemporary digital landscape, traditional password-based authentication has become one of the most significant security vulnerabilities for enterprises and scaling platforms alike. From credential stuffing attacks to sophisticated phishing campaigns, relying on users to create and memorize complex strings of text poses an ongoing risk. This vulnerability has driven the industry toward Passwordless Authentication, specifically leveraging modern standards like WebAuthn and Passkeys.
While cloud-based Identity as a Service (IDaaS) providers offer quick solutions, they frequently introduce unpredictable usage-based pricing, data residency challenges, and limited control over core infrastructure. For engineering teams seeking a balance between absolute data sovereignty, cost efficiency, and advanced security, a self-hosted solution is the ideal approach. This guide will walk you through building your own Self-Hosted Passwordless Authentication Server using Zitadel deployed on a Virtual Private Server (VPS).
Why Zitadel for Passwordless Auth?
Zitadel has emerged as a premier open-source Identity and Access Management (IAM) solution. Unlike older legacy systems, Zitadel was built from the ground up with a cloud-native mindset, offering features that make it uniquely suited for passwordless architectures:
- First-Class Passkey Support: Built-in out-of-the-box support for WebAuthn, allowing users to authenticate via biometrics (Touch ID, Face ID) or hardware security keys (YubiKeys).
- Multi-Tenancy Architecture: Natively supports isolated organizations within a single instance, making it perfect for B2B applications or multi-department ecosystems.
- Audit Trail & Event Sourcing: Every state change is recorded, providing an immutable history for compliance and security auditing.
- Standard-Compliant: Fully supports OpenID Connect (OIDC), OAuth 2.0, and SAML 2.0, ensuring seamless integration with your existing software stack.
Prerequisites and Environment Setup
Before initiating the deployment process, ensure your infrastructure meets the following minimum technical requirements:
- VPS Hardware: Minimum 2 vCPUs, 4GB RAM, and 20GB of SSD storage. Zitadel utilizes an underlying database (CockroachDB or PostgreSQL) which requires stable I/O performance.
- Operating System: Ubuntu 22.04 LTS or newer recommended.
- Domain Name: A fully qualified domain name (FQDN), e.g.,
auth.yourcompany.com, with A/AAAA records pointed to your VPS IP address. - Software Dependencies: Docker Engine (v20.10+) and Docker Compose (v2.0+) installed on the host system.
Step 1: Setting Up the Infrastructure File Structure
To ensure a clean configuration, log into your VPS via SSH and establish a dedicated directory structure for the Zitadel deployment:
mkdir -p ~/zitadel/config
cd ~/zitadelZitadel can operate with various storage backends. For a self-hosted environment with high scalability potential, we will deploy it alongside CockroachDB. Create a configuration file named config.yaml inside the config directory to define Zitadel's operational parameters, security keys, and database connection details.
Step 2: Crafting the Docker Compose Configuration
Next, you need to write the orchestration manifest. Create a file named docker-compose.yml in the root of your ~/zitadel directory. This file manages the lifecycle of the Zitadel application server, the database, and a reverse proxy for handling TLS/SSL termination.
Note: For production deployments, it is highly recommended to run Zitadel behind a secure reverse proxy such as Nginx, Traefik, or Caddy to seamlessly handle Let's Encrypt SSL certificates.
Below is an optimized configuration blueprint utilizing Caddy for automatic SSL certificate management:
version: '3.8'
services:
cockroachdb:
image: cockroachdb/cockroach:v23.1.0
command: start-single-node --insecure
volumes:
- crdb-data:/cockroach/cockroach-data
ports:
- "26257:26257"
zitadel:
image: ghcr.io/zitadel/zitadel:latest
command: start-from-init --config /config/config.yaml
environment:
- ZITADEL_DATABASE_COCKROACH_HOST=cockroachdb
- ZITADEL_DATABASE_COCKROACH_PORT=26257
- ZITADEL_EXTERNALSECURE=true
- ZITADEL_EXTERNALDOMAIN=auth.yourcompany.com
- ZITADEL_EXTERNALPORT=443
volumes:
- ./config:/config
depends_on:
- cockroachdb
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
command: caddy reverse-proxy --from auth.yourcompany.com --to zitadel:8080
volumes:
- caddy-data:/data
- caddy-config:/config
depends_on:
- zitadel
volumes:
crdb-data:
caddy-data:
caddy-config:Step 3: Initializing and Starting the Server
With the orchestration definitions in place, you can initialize the database schema and start the Zitadel engine. Execute the following command in your terminal:
docker-compose up -dThis command downloads the necessary container images, configures the single-node CockroachDB cluster, runs Zitadel's internal initialization scripts, and instructs Caddy to provision a free TLS certificate via Let's Encrypt. You can monitor the initialization process and retrieve your temporary administrative credentials by viewing the container logs:
docker-compose logs --follow zitadelLook for a log entry displaying the automatically generated Default Username (typically [email protected] or based on your domain) and the temporary password string.
Step 4: Configuring the Passwordless Experience
Once the server is running, navigate to [https://auth.yourcompany.com](https://auth.yourcompany.com) via a web browser. Log in using the administrative credentials extracted from the logs. You will be prompted immediately to change the default password.
To configure true passwordless authentication across your organization, follow these architectural steps within the Zitadel Console:
- Navigate to Settings: Go to your Organization settings or global Instance settings.
- Modify Login Policy: Under the 'Login Policy' section, locate the 'Factors' or 'Authentication Methods' settings.
- Enable Passkeys: Toggle the option for Passkeys / WebAuthn to allowed or mandatory.
- Disable External Passwords (Optional): For strict passwordless environments, configure the policy to allow users to register and authenticate exclusively via their hardware or biometric keys, bypassing traditional input fields entirely.
Step 5: Enrolling a Passkey (The User Experience)
When a new user is provisioned or signs up on your new IAM platform, their onboarding journey shifts from creating a complex string of characters to registering a cryptographic keypair:
During their initial login, Zitadel prompts them to "Set up a Passkey". The browser invokes the operating system's native biometric prompt (such as Windows Hello, Apple Touch ID, or Android Biometrics). Once authenticated locally on their device, a unique public key is sent and securely stored on your self-hosted Zitadel server, while the private key never leaves the user's hardware. Subsequent logins take less than two seconds, requiring only a biometric scan.
Security Hardening and Best Practices
Running a self-hosted IAM infrastructure demands rigorous adherence to infrastructure security best practices. Consider implementing the following steps prior to exposing the system to production traffic:
- Database Backups: Implement cron jobs to execute routine, encrypted snapshot backups of the CockroachDB volumes to an offsite S3-compatible object storage tier.
- Firewall Restriction: Use tools like
ufwto close all inbound ports on the VPS except for80,443, and your designated custom secure SSH port. - SMTP Configuration: Configure a trusted transactional email provider (such as SendGrid, Postmark, or Mailgun) within Zitadel's SMTP settings to ensure secure, reliable delivery of onboarding and identity verification emails.
Conclusion
Deploying a self-hosted Zitadel server on a VPS effectively eliminates reliance on costly third-party identity vendors while radically upgrading your application's security posture. By shifting to a passwordless model powered by WebAuthn and Passkeys, you protect your infrastructure from credential theft while offering frictionless onboarding for your users. With full control over your data, compliance requirements, and configuration options, your self-hosted auth server forms a highly secure foundation for future application development.
