Vaultwarden Rate Limiting: How to Configure Login and Admin Protection

Vaultwarden protects authentication endpoints with two independent token-bucket rate limiters—one for user logins and one for admin actions—configurable via the LOGIN_RATELIMIT_* and ADMIN_RATELIMIT_* environment variables.

The dani-garcia/vaultwarden repository implements per-IP rate limiting using the governor crate to prevent brute-force attacks against both the login API and the admin panel. These mechanisms are instantiated at startup as static singletons and enforce strict request throttling before authentication handlers process credentials.

How Vaultwarden Rate Limiting Works

Vaultwarden employs token-bucket limiters built on the governor crate (v0.10.4), a thread-safe Rust implementation of the leaky-bucket algorithm. Each limiter tracks requests per IP address independently, allowing short bursts while enforcing average rate constraints over time.

The Two Built-in Limiters

The system maintains separate limiters for different attack surfaces:

Login Limiter (LIMITER_LOGIN in src/ratelimit.rs)

  • Protects: All password-login and two-factor authentication attempts
  • Default interval: 60 seconds per request (login_ratelimit_seconds)
  • Default burst: 10 requests (login_ratelimit_max_burst)
  • Config keys: LOGIN_RATELIMIT_SECONDS, LOGIN_RATELIMIT_MAX_BURST

Admin Limiter (LIMITER_ADMIN in src/ratelimit.rs)

  • Protects: Admin API calls, admin-token validation, and admin UI routes
  • Default interval: 300 seconds per request (admin_ratelimit_seconds)
  • Default burst: 3 requests (admin_ratelimit_max_burst)
  • Config keys: ADMIN_RATELIMIT_SECONDS, ADMIN_RATELIMIT_MAX_BURST

Both limiters initialize as static LazyLock instances at startup, reading configuration values from the global CONFIG object:

// src/ratelimit.rs – login limiter initialization
static LIMITER_LOGIN: LazyLock<Limiter> = LazyLock::new(|| {
    let seconds = Duration::from_secs(CONFIG.login_ratelimit_seconds());
    let burst = NonZeroU32::new(CONFIG.login_ratelimit_max_burst())
        .expect("Non-zero login ratelimit burst");
    RateLimiter::keyed(Quota::with_period(seconds)
        .expect("Non-zero login ratelimit seconds")
        .allow_burst(burst))
});

When a protected endpoint receives a request, Vaultwarden calls the appropriate check function before processing credentials:

pub fn check_limit_login(ip: &IpAddr) -> Result<(), Error> {
    LIMITER_LOGIN.check_key(ip).map_err(|_| err_code!("Too many login requests", 429))
}

pub fn check_limit_admin(ip: &IpAddr) -> Result<(), Error> {
    LIMITER_ADMIN.check_key(ip).map_err(|_| err_code!("Too many admin requests", 429))
}

These functions return HTTP 429 Too Many Requests immediately when the token bucket for that IP is exhausted.

Configuring Rate Limits

Vaultwarden reads rate-limiting configuration from environment variables or a .env file, with defaults defined in src/config.rs.

Default Configuration Values

According to the configuration structure in src/config.rs, the defaults are:

/// Seconds between login requests
login_ratelimit_seconds:       u64, false, def, 60;
/// Max burst size for login requests
login_ratelimit_max_burst:     u32, false, def, 10;

/// Seconds between admin login requests
admin_ratelimit_seconds:       u64, false, def, 300;
/// Max burst size for admin login requests
admin_ratelimit_max_burst:     u32, false, def, 3;

Environment Variable Configuration

Set the following variables in your environment or .env file (documented in .env.template lines 451-455):


# .env file

LOGIN_RATELIMIT_SECONDS=120
LOGIN_RATELIMIT_MAX_BURST=5
ADMIN_RATELIMIT_SECONDS=60
ADMIN_RATELIMIT_MAX_BURST=2

When running the Docker image, pass variables directly:

docker run -d \
  -e LOGIN_RATELIMIT_SECONDS=120 \
  -e LOGIN_RATELIMIT_MAX_BURST=5 \
  -e ADMIN_RATELIMIT_SECONDS=60 \
  -e ADMIN_RATELIMIT_MAX_BURST=2 \
  -v /path/to/data:/data \
  vaultwarden/server:latest

Admin UI Configuration

You can modify rate limits through the Vaultwarden admin UI, which persists values to DATA_FOLDER/config.json. However, environment variables take precedence over UI-configured values. For permanent changes that survive container restarts or UI resets, use the .env file or Docker environment variables.

Practical Implementation Examples

Tightening Security for High-Risk Deployments

To reduce brute-force vulnerability for internet-facing instances:


# Require 5 minutes between login attempts, allow burst of 3

LOGIN_RATELIMIT_SECONDS=300
LOGIN_RATELIMIT_MAX_BURST=3

# Restrict admin access to once per 10 minutes, burst of 1

ADMIN_RATELIMIT_SECONDS=600
ADMIN_RATELIMIT_MAX_BURST=1

Disabling Rate Limits for Internal Networks

While not recommended for production, you can effectively disable limiting by setting high burst values (though Vaultwarden requires non-zero values):


# Effectively unlimited for trusted networks

LOGIN_RATELIMIT_SECONDS=1
LOGIN_RATELIMIT_MAX_BURST=1000
ADMIN_RATELIMIT_SECONDS=1
ADMIN_RATELIMIT_MAX_BURST=1000

Key Source Files

  • src/ratelimit.rs: Contains the LIMITER_LOGIN and LIMITER_ADMIN static instances, plus check_limit_login() and check_limit_admin() functions.
  • src/config.rs: Defines the configuration structure with default values for the four ratelimit parameters.
  • src/api/identity.rs: Invokes check_limit_login() before processing identity authentication requests.
  • .env.template: Documents the environment variables for operator configuration.

Summary

  • Vaultwarden uses two independent token-bucket limiters (governor crate) to protect login and admin endpoints separately.
  • Login rate limiting defaults to 60-second intervals with a burst capacity of 10 requests per IP.
  • Admin rate limiting defaults to 300-second intervals with a burst capacity of 3 requests per IP.
  • Configure limits via the LOGIN_RATELIMIT_SECONDS, LOGIN_RATELIMIT_MAX_BURST, ADMIN_RATELIMIT_SECONDS, and ADMIN_RATELIMIT_MAX_BURST environment variables.
  • Exceeded limits return HTTP 429 responses with descriptive error messages.
  • Environment variables override settings configured through the admin UI.

Frequently Asked Questions

How do I know if Vaultwarden is rate limiting my requests?

When a client exceeds the configured burst size or average rate, Vaultwarden returns HTTP 429 Too Many Requests with the message "Too many login requests" or "Too many admin requests" before processing authentication logic. Check your reverse proxy or application logs for these status codes if legitimate users experience lockouts.

What's the difference between the login and admin rate limiters?

The login limiter protects password-based authentication and two-factor endpoints in src/api/identity.rs, while the admin limiter restricts access to the admin API, admin-token validation, and management UI routes. They operate independently, meaning hitting the login limit does not affect admin access and vice versa.

Can I configure rate limits through the admin UI instead of environment variables?

Yes, the admin UI persists rate limit changes to config.json in the data folder, but these values are overridden by environment variables if both are present. For Docker deployments or infrastructure-as-code setups, environment variables provide more reliable, reproducible configuration.

Do rate limits apply per user account or per IP address?

Vaultwarden applies rate limits per IP address, not per user account. The check_limit_login() and check_limit_admin() functions extract the client IP from the request and use it as the key for the token-bucket tracker. This prevents attackers from cycling through different usernames to bypass limits, but means shared NAT gateways (corporate networks, VPNs) share the same rate limit budget.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →