A modern reverse proxy with built-in ACME support for automatic TLS certificate management
Secure, automated reverse proxy for self-hosted applications
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.
Route traffic to multiple backend services based on URL contexts. Perfect for microservices and complex application setups.
Automatically provision and renew TLS certificates using ACME protocol with both HTTP-01 and DNS-01 challenge types.
Support for multiple DNS providers via the zone-update library. Configure automatic certificate management behind firewalls.
Native static-file serving with directory listing, automatic compression, and SPA fallback support. No external server needed.
# 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
Binaries are available on the Github release page
git clone https://github.com/tarka/vicarian.git
cd vicarian
cargo build --release
The binary will be available at target/release/vicarian
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.
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"
}
}
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
}
Automatic certificate management via HTTP-01 challenge:
acme "le-http01" {
contact = "admin@example.com"
profile = "shortlived"
challenge {
type = "http-01"
}
}
Serve files from a local directory natively:
vhost "example.com" {
tls = "snakeoil"
backend "/html" {
type = "static"
root = "/var/www"
}
}
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")
}
}