vllm-project/vllm · error

--ssl-certfile is required to enable TLS; --ssl-keyfile/--ss

Error message

--ssl-certfile is required to enable TLS; --ssl-keyfile/--ssl-ca-certs/--ssl-cert-reqs/--ssl-ciphers cannot be used without it

What it means

Thrown by TlsConfig::validate() when any TLS option (--ssl-keyfile, --ssl-ca-certs, --ssl-cert-reqs, --ssl-ciphers) is set but --ssl-certfile is not. The Rust frontend mirrors Python uvicorn's ssl_* arguments and treats the server certificate as the switch that enables TLS; structural validation runs at startup, before the OpenSSL context is built.

Source

Thrown at rust/src/server/src/config.rs:135

    /// (combined PEM).
    pub key_file: Option<String>,
    /// PEM CA bundle used to verify client certificates (mTLS). Required when
    /// `cert_reqs` is non-zero.
    pub ca_certs: Option<String>,
    /// Client-certificate requirement, mirroring Python's `ssl.CERT_*`:
    /// 0 = none, 1 = optional, 2 = required.
    pub cert_reqs: i32,
    /// OpenSSL cipher string for TLS 1.2 and below, mirroring Python's
    /// `ssl.set_ciphers`. `None` keeps the forward-secret AEAD default.
    pub ciphers: Option<String>,
}

impl TlsConfig {
    /// Structurally validate the TLS arguments; the cert/key material is parsed
    /// later, when the OpenSSL context is built.
    pub fn validate(&self) -> Result<()> {
        if self.cert_file.is_none() {
            bail!(
                "--ssl-certfile is required to enable TLS; \
                 --ssl-keyfile/--ssl-ca-certs/--ssl-cert-reqs/--ssl-ciphers \
                 cannot be used without it"
            );
        }
        if !matches!(self.cert_reqs, 0..=2) {
            bail!(
                "--ssl-cert-reqs must be 0 (none), 1 (optional), or 2 (required), got {}",
                self.cert_reqs
            );
        }
        if self.cert_reqs != 0 && self.ca_certs.is_none() {
            bail!(
                "--ssl-ca-certs is required when --ssl-cert-reqs is {} \
                 (client certificate verification)",
                self.cert_reqs
            );
        }

View on GitHub (pinned to c794754062)

Solutions

  1. Add --ssl-certfile pointing to your PEM certificate chain: --ssl-certfile server.pem --ssl-keyfile server.key.
  2. If the PEM contains both cert and key, --ssl-certfile alone is sufficient per the TlsConfig docs.
  3. If you did not intend to enable TLS, remove all --ssl-* flags (including --ssl-ciphers and --ssl-cert-reqs defaults set by your launcher).
  4. Verify flag ordering in your unit/systemd file — a missing value can silently consume the next flag.

Example fix

# before
vllm serve model --ssl-keyfile server.key --ssl-ca-certs ca.pem

# after
vllm serve model --ssl-certfile server.pem --ssl-keyfile server.key --ssl-ca-certs ca.pem
Defensive patterns

Strategy: validation

Validate before calling

fn tls_config_complete(cert: &Option<PathBuf>, others_set: bool) -> bool {
    !others_set || cert.is_some()
}

Type guard

fn tls_is_structurally_valid(tls: &Option<TlsConfig>) -> bool {
    match tls {
        None => true,
        Some(t) => t.cert_file.is_some(),
    }
}

Try / catch

match config.tls.as_ref().map(|t| t.validate()) {
    Some(Err(e)) => eprintln!("TLS config rejected: {e}"),
    _ => {}
}

Prevention

When it happens

Trigger: Configuring e.g. --ssl-keyfile server.key --ssl-cert-reqs 2 without also passing --ssl-certfile server.pem. Also triggered by setting only --ssl-ciphers or only --ssl-ca-certs. Any TlsConfig with cert_file == None fails validate() unconditionally, even if all other fields are defaults.

Common situations: Migrating a deployment where the cert/key live in a combined PEM and only the key flag was carried over; CI pipelines that template TLS flags conditionally; combining-peer-cert (mTLS) setup where --ssl-ca-certs was added first.

Understand the failure class

Related errors


AI-assisted analysis of vllm-project/vllm@c794754062 (2026-08-14). Data as JSON: /api/errors/4a605383c6e9e721. Report an issue: GitHub.