Back to articles
Technology Insight

Building a Self-Hosted Passwordless Authentication Server with Zitadel on a VPS: A Comprehensive Guide

May 28, 2026

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 ~/zitadel

Zitadel 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 -d

This 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 zitadel

Look 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:

  1. Navigate to Settings: Go to your Organization settings or global Instance settings.
  2. Modify Login Policy: Under the 'Login Policy' section, locate the 'Factors' or 'Authentication Methods' settings.
  3. Enable Passkeys: Toggle the option for Passkeys / WebAuthn to allowed or mandatory.
  4. 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 ufw to close all inbound ports on the VPS except for 80, 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.

Building a Self-Hosted Passwordless Authentication Server with Zitadel on a VPS: A Comprehensive Guide | DPTCloud