Vicarian

A modern reverse proxy with built-in ACME support for automatic TLS certificate management

Secure, automated reverse proxy for self-hosted applications

Learn More View on GitHub Crates.io

About Vicarian

Vicarian is a TLS-first reverse proxy server with built-in ACME support. It is currently targeted at self-hosting and SOHO installations; in particular it supports provisioning TLS certificates behind-the-firewall via ACME DNS-01.

Reverse Proxy

Route traffic to multiple backend services based on URL contexts. Perfect for microservices and complex application setups.

Dynamic Certificates

Automatically provision and renew TLS certificates using ACME protocol with both HTTP-01 and DNS-01 challenge types.

DNS Integration

Support for multiple DNS providers via the zone-update library. Configure automatic certificate management behind firewalls.

Static Files

Native static-file serving with directory listing, automatic compression, and SPA fallback support. No external server needed.

Installation

Debian & Ubuntu packages

# Download the repository key:
curl -fsSL https://vicarian.org/debian/vicarian-repo.gpg \
    | sudo gpg --dearmor -o /etc/apt/keyrings/vicarian-repo-archive-keyring.gpg

# Add the APT source
echo "deb [signed-by=/etc/apt/keyrings/vicarian-repo-archive-keyring.gpg] https://vicarian.org/debian stable main" \
    | sudo tee /etc/apt/sources.list.d/vicarian.list

# Install Vicarian
sudo apt update && sudo apt install vicarian 

Binary releases

Binaries are available on the Github release page

Building from Source

git clone https://github.com/tarka/vicarian.git
cd vicarian
cargo build --release 

The binary will be available at target/release/vicarian

Systemd Service

An example systemd service is provided in systemd/vicarian.service. The systemd service sets the CAP_NET_BIND_SERVICE flag which allows binding to ports 80/443 without root.

Configuration Examples

Vicarian uses a configuration syntax based on HCL/Terraform:

ACME/Letsencrypt over DNS

This is full working example; other fragments are below:


// Unlike most other HTTP proxies we declare our TLS configuration
// up-front and then reference it in the vhost blocks. This allows
// Acme DNS-01 configuration to re-used across hosts (the expected
// use-case for Vicarian).

// An ACME DNS-01 provider using Porkbun. Secrets are read from the
// environment; see the README for using a systemd EnvironmentFile.
acme "le-porkbun" {
    acme_provider = "letsencrypt"    // Default
    contact = "admin@example.com"

    challenge {
        type = "dns-01"
        dns_provider {
            name = "porkbun"
            key = env("PORKBUN_KEY")
            secret = env("PORKBUN_SECRET")
        }
    }
}

listen {
    addrs = [
        "[::]"            // Default; this listens to IPv4 & IPv6 from everywhere
    ]
    insecure_port = 80    // Default
    tls_port = 443        // Default
}

vhost "files.example.com" {
    // Optional aliases for this host. These will be added to
    // the generated TLS certificate.
    aliases = [
        "docs.example.com",
        "pics.example.com",
    ]

    tls = "le-porkbun"

    // A service that does not allow a custom root/context,
    // so we must place at root.
    backend "/" {
        type = "proxy"
        url = "http://localhost:8443"
        // This service enforces TLS with a self-signed cert, so
        // we need to disable certificate verification.
        //
        // Looking at you Unifi Controller.
        trust = true
    }

    // A better behaved service that allows a custom root.
    backend "/copyparty" {
        type = "proxy"
        url = "http://localhost:9090"
    }
}
                        

Certificate Files

Using pre-generated certificate files:


cert "snakeoil" {
    keyfile = "/etc/ssl/certs/ssl-cert-snakeoil.pem"
    certfile = "/etc/ssl/private/ssl-cert-snakeoil.key"
    reload = true // Optional; defaults to true
}
                        

ACME over HTTP

Automatic certificate management via HTTP-01 challenge:


acme "le-http01" {
    contact = "admin@example.com"
    profile = "shortlived"
    challenge {
        type = "http-01"
    }
}
                        

Static Files

Serve files from a local directory natively:


vhost "example.com" {
    tls = "snakeoil"

    backend "/html" {
         type = "static"
         root = "/var/www"
    }
}
                        

Prometheus Metrics

Export built-in Prometheus metrics with optional auth:


vhost "example.com" {
    tls = "le-porkbun"

    backend "/metrics" {
        type = "metrics"
        // An authorisation key; if present
        // the `Authorization` header will be checked for this key.
        auth_key = env("my-secret-key")
    }
}