Configuration
Busker is configured with an ordinary Clojure map.
The same config shape is used when starting a server with
ol.busker/start! and when changing a
running server with ol.busker/reload!.
You can write that map directly or generate it from environment variables,
files, a database, or other application state. It’s just data.
When Busker starts or reloads, it turns your config map into a normalized config snapshot. A snapshot is the effective config after defaults are applied.
A running server is backed by a runtime generation. A generation is the set of runtime resources built from one snapshot. For example, it includes open sockets, worker threads, native protocol state, handlers, and TLS automation state.
On reload, Busker builds a new generation while the current one keeps serving traffic. If the new generation starts successfully, new connections and requests move to it. The old generation stops accepting new work and drains the connections and requests it is already handling. If the new generation fails to start, the old generation keeps serving traffic.
A minimal HTTP config with HTTP/1.1, HTTP/2, and automatic gzip/zstd/brotli compression is:
{:entrypoints {:http {:bind "127.0.0.1:8080"
:tls false }}
:dispatch [{:handler (fn [_req]
{:status 200
:headers {"content-type" "text/plain" }
:body "ok" })}]}
That’s how you would use Busker when coming from traditional Clojure ring-adapters like Jetty or http-kit.
A minimal HTTP+HTTPS config with HTTP/1.1, HTTP/2, HTTP/3, automatic gzip/zstd/brotli compression, and certificates obtained and renewed automatically is:
{:tls {:certificates {:manage ["example.com" ]}}
:entrypoints {:http {:bind ":80"
:tls false }
:https {:bind ":443" }}
:dispatch [{:handler (fn [_req]
{:status 200
:headers {"content-type" "text/plain" }
:body "ok" })}]}
Note the addition of the example.com domain, that is required so Busker can
manage the certificate lifecyle for you.
An entrypoint names one or more network listeners and the protocols they accept.
A dispatcher is one ordered step in the request pipeline that can match, transform, or handle requests for one or more entrypoints. In conventional Clojure programs there is usually only a single dispatcher: the Ring handler.
Busker accepts the following top-level config keys.
:entrypoints and :dispatch are required. Other keys are optional and
either use their documented default or remain unset.
| key | value |
|---|---|
|
Map of entrypoint ids to entrypoint maps (see below). |
|
Ordered vector of dispatcher maps (see below). |
|
Global TLS, certificate, ACME, and session ticket config (see below). |
|
Positive integer event loop worker count, default |
|
Response buffering pool implementing |
|
Positive integer connection cap, default |
|
Response aggregation bytes, default |
|
Server header value, default |
|
Optional Proxy-Status identity. |
|
Optional positive integer request body byte limit. |
|
Optional positive integer internal delegation limit. |
|
Optional positive integer internal reprocess limit. |
|
Optional TLS handshake timeout in milliseconds. |
|
Optional non-negative idle pipe cache limit. |
|
Optional HTTP/1.1 request timeout in milliseconds. |
|
Optional HTTP/1.1 request I/O timeout in milliseconds. |
|
Allow h2c upgrade, default |
|
Optional HTTP/2 idle timeout in milliseconds. |
|
Optional HTTP/2 graceful shutdown timeout in milliseconds. |
|
Optional positive integer concurrent HTTP/2 stream limit. |
|
Optional positive integer concurrent HTTP/2 request limit. |
|
Optional positive integer streaming request limit. |
|
Optional positive integer priority stream tracking limit. |
|
Optional positive integer per-stream window bytes. |
|
Optional suspicious behavior delay in milliseconds. |
|
Optional HTTP/3 idle timeout in milliseconds. |
|
Optional HTTP/3 graceful shutdown timeout in milliseconds. |
|
Optional positive integer per-stream window bytes. |
|
Optional non-negative ACK frequency; |
|
Enable response compression, default |
|
Minimum response bytes for compression, default |
|
Gzip level |
|
Brotli level |
|
Zstandard compression level, default |
Entrypoints (:entrypoints)
Entrypoints describe how traffic enters Busker.
Each entrypoint gives a stable id to one or more bind addresses and the protocol and TLS settings used by those listeners. The id exists so other config sections can refer to the entrypoint without repeating host and port details.
:entrypoints in the top-level config is a map from keyword entrypoint ids to
maps:
{:entrypoints {:plain {:bind ["127.0.0.1:8080" "[::1]:8080" ]
:tls false }
:secure {:bind ":8443" }}}
The ids (:plain and :secure above) are arbitrary user-defined keywords.
-
:bindis a string or non-empty vector of strings. TCP and UDP bind forms are":8080","host:8080", and"[::1]:8080". Unix domain sockets use pathname form"unix:/path.sock"or abstract namespace form"unix:@abstract-name". -
:tlsisfalsefor cleartext or a map to enable TLS. A TLS entrypoint requires top-level:tls :certificates :loador:tls :certificates :manage. Entrypoint-local TLS settings use the same keys as top-level:tls, but certificate material remains top-level. -
:http1?and:http2?default totrue. -
:http3?defaults totruewhen TLS is enabled andfalseotherwise. HTTP/3 requires TLS and is not supported on Unix domain sockets.
Dispatchers (:dispatch)
Dispatchers describe what Busker does with each request after it arrives.
Busker evaluates them in order, optionally restricting them by entrypoint, match predicate, group, middleware, and terminal behavior.
:dispatch in the top-level config is an ordered vector of dispatcher maps.
For conventional Clojure programs, the most common shape is a single dispatcher that points at one Ring handler. Start with this unless one server needs multiple entrypoint-specific or staged request pipelines.
{:dispatch [{:handler your-ring-handler}]}
Advanced use-cases can add entrypoint filters, match predicates, mutually exclusive groups, middleware, and terminal behavior. This is useful when using Busker as an application router or reverse-proxy-style edge, where one server routes different traffic classes before handing each request to the appropriate Ring handler or upstream:
{:dispatch [{:entrypoints #{:secure }
:group :site
:match my.app/api-request?
:middleware [[my.app/wrap-audit {:mode :strict }]
my.app/wrap-auth]
:handler my.app/handler
:terminal? true }]}
-
:entrypoints- Optional set of entrypoint ids where the dispatcher may run (default: all entrypoints). -
:group- Optional key that allows only the first matching dispatcher in the group (default: none). -
:match- Optional predicate of the current request (default: match every request). -
:middleware- Vector of middleware functions, symbols, or[middleware opts]entries (default:[]). -
:handler- Handler function for the dispatcher (default:{:status 200}). -
:terminal?- Return the handler result immediately when true (default:false).
Function references in :match, :handler, and :middleware may be direct
functions or qualified symbols.
Middleware entries are Ring-style wrapper functions. [wrap opts] calls
(wrap handler opts), and a bare wrap calls (wrap handler).
Non-terminal dispatcher results become the current value for later dispatchers, so dispatchers can act as request transforms before a terminal handler returns a Ring response.
TLS (:tls, top-level)
The top-level TLS config describes how Busker terminates encrypted traffic, finds certificate material, and runs certificate automation for TLS entrypoints.
:tls in the top-level config is a map of options:
-
:certificates- Map of certificate sources and managed names; see below (default: none). -
:storage-Storageinstance or storage factory map; see Storage factory map below (default: Clave file storage for managed certificates). -
:issuers- Vector of issuer maps; see Issuer maps below (default: Let’s Encrypt production). -
:issuer-selection- ACME issuer choice strategy,:in-orderor:shuffle(default::in-order). -
:key-type- Certificate key algorithm (default::p256). -
:key-reuse- Reuse private keys when renewing certificates (default:false). -
:cache-capacity- Positive integer certificate cache capacity (default:1000). -
:solvers- ACME challenge solver map; see Solvers below (default: Busker-managed:http-01for managed certificates). -
:ocsp- OCSP option map; see OCSP map below (default: none). -
:config-fn- Function or qualified symbol returning per-subject/per-domain overrides (default: none). -
:http-client- HTTP client option map for ACME requests (default: none). -
:tls-compatibility-mode- TLS cipher/protocol preset,:modernor:intermediate(default::modern). -
:session-tickets- Session ticket map; see Session ticket map below (default: enabled in memory).
Certificates map (:tls :certificates)
The :certificates map contains:
-
:load- Vector of static certificate source maps (default:[]).-
:type- Source type, either:pemor:folder(required). -
:cert-file- PEM certificate chain path for:pemsources. -
:key-file- PEM private key path for:pemsources. -
:path- Directory path for:foldersources.
-
-
:manage- Vector of subject names (domain names) Busker should obtain and renew automatically (default:[]).-
Entries are strings such as
"example.com"or"*.example.com".
-
Static sources in :load are read during startup and reload. Busker extracts
subject names from each certificate and uses SNI to choose a matching static
certificate during the TLS handshake. If there is no SNI match, Busker falls
back to the first loaded static certificate.
Subject names in :manage are handed to Clave certificate automation. Busker
starts the automation machinery during startup and reload, stores issued
certificates in TLS storage, and uses SNI to look them up during TLS
handshakes. Certificate lifecycle work such as obtain, renewal, and OCSP
maintenance operates in the background.
{:tls {:certificates {:manage ["example.com" "www.example.com" ]}}}
{:tls {:certificates {:load [{:type :pem
:cert-file "example.com/fullchain.pem"
:key-file "example.com/privkey.pem" }]}}}
Storage factory map (:tls :storage)
The :storage key may be a
ol.clave.storage/Storage
instance or a factory map.
When omitted, managed certificate automation uses
ol.clave.storage.file/file-storage
with its no-arg default root.
The Clave default is to use <dir>/ol.clave, where <dir> is
$STATE_DIRECTORY (for systemd usage) or $XDG_DATA_HOME. While often not
necessary, you can configure this explicitly.
When used as a map :storage contains:
-
:factory- Function or qualified symbol returning anol.clave.storage/Storageimplementation (required). -
factory-specific keys - Options passed to the factory (default: none). For the built-in filesystem storage factory, see
ol.clave.storage.file/file-storage. Its options include:root, a root directory string orjava.nio.file.Path.
Issuer maps (:tls :issuers)
The :issuers vector contains maps:
-
:directory-url- ACME directory URL (required). -
:email- Contact email for certificate notifications (default: none). -
:eab- External account binding credentials for restricted CAs (default: none).
OCSP map (:tls :ocsp)
The :ocsp map contains:
-
:enabled?- Enable OCSP stapling (default:true). -
:must-staple?- Require OCSP must-staple in certificates (default:false).
Session ticket map (:tls :session-tickets)
The :session-tickets map contains:
-
:disabled?- Disable session ticket resumption when true (default:false). -
:persistence- Session ticket persistence mode,:memoryor:storage(default::memory).-
:storagepersistence requires top-level:tls :storage.
-
-
:max-keys- Maximum number of ticket keys retained in rotation (default:4). -
:lifetime-seconds- TLS session ticket lifetime in seconds (default:86400).
Solvers (:tls :solvers)
Solvers describe how ACME challenge records are presented and cleaned up while Busker obtains managed certificates.
Busker provides an integrated :http-01 solver by default. It serves
/.well-known/acme-challenge/… through the Ring handler pipeline. Busker
does not provide :dns-01 or :tls-alpn-01 by default; add those to
:solvers when needed.
The :solvers map contains additional Clave solver entries keyed by challenge
type:
-
:http-01- HTTP challenge solver for public HTTP validation. Busker always replaces the value of this key with its integrated solver. -
:dns-01- DNS challenge solver for DNS-provider validation. This is required for wildcard names such as"*.example.com". -
:tls-alpn-01- TLS ALPN challenge solver for port 443 validation.
To write a custom solver, see
ol.clave.certificate and
ol.clave.certificate/validate-solvers.
This example adds :dns-01, while Busker still adds its integrated :http-01
solver:
{:tls {:certificates {:manage ["example.com" "*.example.com" ]}
:solvers {:dns-01 my-dns-solver}}}